一、问题出现
测试同学发现页面打开多个标签页后,有一些页面的pag动效会渲染错误,变成一个白色正方形的哭脸表情
二、问题排查
1. pag文件是否请求正常
在环境打开控制台,刷新页面,发现pag文件都是正常请求到了,动效也正常显示。那看来并不是文件的问题
2. 多个页面的问题吗?
在环境中,尝试连续打开
2个标签页,页面正常显示。尝试连续打开
3个标签页,页面正常显示。尝试连续打开
4个标签页,第一个打开的页面中的4个动效出现了哭脸表情
!cool,问题复现了,不错不错,就怕复现不了的BUG。打开控制台,发现Console有提示,看来是什么东西被删掉了
复制这个错误 Too many active WebGL contexts. Oldest context will be lost.,去libpag的社区找找有没有相关的错误,果然找到相关的答案 1484,官方的回答如下:
问题定位到了,就是一个页面里面创建了太多PAGPlayer了(一个页面用了5个PAGPlayer,相当于用了5个active WebGL contexts),4个标签页需要用20个,超出了chrome的16个限制,那么之前的就会被丢掉,渲染错误,就会出现哭脸。
三、问题解决
1. 官方提议
在上面官方的回答中,提议使用组合 PAGComposition 的方式渲染,就是把多个pag文件放到一个canvas里面去渲染,那就只用了1个active WebGL contexts,问题基本就解决了。
但是我这个页面比较特殊,动效的位置很分散,且嵌套在不同的组件里面,用一个canvas去渲染很明显是不现实的,所以官方的解决方法不太适合
2. 尝试解决
有没有一种可能,在标签不显示的时候,这个动效其实并没有起作用,那我是不是可以直接销毁掉,等标签显示的时候再渲染就好了。好像有搞头,试一下
加上一个函数 watchVisibility 用于监听标签显隐,标签隐藏的时候,执行destroy方法
const watchVisibility = () => {
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 销毁 PAGPlayer
if (PAGPlayer) {
PAGPlayer.destroy();
PAGPlayer = null;
}
} else {
// 创建 PAGPlayer
if (!PAGPlayer) {
nextTick(() => {
initPag();
});
}
}
});
};
试验一下,连续打开4个页面,结果第1个打开的页面还是会出现错误的提示。
3. 原来要这样?
看来destroy并没有能清除掉WebGL的上下文,只能继续在网上找找解决方法。这个问题如果是canvas的限制,那是不是其他的框架也有这个问题?
这次我直接搜 Too many active WebGL contexts. Oldest context will be lost.,找到了 这个
原来可以手动销毁上下文,好像有搞头,试一下
<canvas v-if="!docuemntHidden"></canvas>
const watchVisibility = () => {
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 销毁 PAGPlayer
if (PAGPlayer) {
// 销毁canvas引用的GL上下文
PAGPlayer.canvasElement.getContext('webgl')
.getExtension('WEBGL_lose_context')
.loseContext();
PAGPlayer.destroy();
PAGPlayer = null;
}
docuemntHidden.value = true;
} else {
// 创建 PAGPlayer
docuemntHidden.value = false;
if (!PAGPlayer) {
nextTick(() => {
initPag();
});
}
}
});
};
找到PAGPlayer引用的canvas,手动销毁WebGL的上下文,再给渲染的canvas标签加上渲染条件
试验一下,连续打开4个页面,再打开第一个标签,正常显示动效,似乎是解决了,尝试多几个标签切换,确实没问题。
FF和Edge也不放过,也没问题,看来这个解决方式可行,COOL!