您的位置  > 互联网

重温FPS和浏览器重绘的知识,你知道吗?

最近,在手Q魔法表情的项目中,遇到了一个奇怪的闪烁问题。

这里所说的闪烁是指动画开始播放时,突然出现片刻的空白(大约1到2帧)。

闪烁分析

这个神奇的表情其实是一个HTML5版本的动画,利用腾讯即将推出的开源版本将SWF转换为2D动画。

iOS系统下,无论哪个型号、哪个系统版本,都没有问题。

但部分机器出现奇怪的闪烁现象,包括小米note、小米4、三星、魅族等。 奇怪的是,同系统的小米红米Note却完全正常。

通过查看H5 api信息,我们知道e是4.4才支持的,动画机制是如果接口不可用则进行替换。

所以看起来有点混乱。 红米Note也是4.4系统,所有iOS系统都可以。 也许这就是问题所在。

温习有关 FPS 和浏览器重绘的知识。 浏览器保持一定的帧速率(通常为 60fps)来刷新屏幕(包括页面)。 动画绘制过程包括几个步骤:

1. 删除整个内容;

2.计算所有组件/元素的位置颜色;

3. 将所有部件一一画出。

这个过程是由不准确驱动的,时钟无法与浏览器的重绘频率同步。

那么,可能会出现如下时序情况:

1. 删除整个内容;

2、当浏览器到达重绘时间点时,此时为空白,浏览器绘制空白;

3. 50ms后,这一帧动画的所有元素都绘制完毕(由于动画的复杂性,可能需要很长时间,超过16ms)

关键点就在这里。

好招数不怕旧

双缓冲,只要你对图形图像处理编程有一点了解,你就应该听说过这个术语,即使你不知道它是什么。 这个技术非常非常古老,非常非常简单,但是非常非常有效。

我们看一下百度百科上的解释。 可能不专业,但我想已经足以说明问题了。

闪烁是图形编程中的常见问题。 需要多个复杂绘制操作的图形操作可能会导致渲染图像闪烁或具有其他不可接受的外观。 双缓冲的使用解决了这些问题。 双缓冲使用内存缓冲区来解决多次绘制操作引起的闪烁问题。 启用双缓冲后,所有绘图操作都会首先渲染到内存缓冲区,而不是屏幕上的绘图表面。 所有绘图操作完成后,内存缓冲区将直接复制到其关联的绘图表面。 由于屏幕上只进行一次图形操作,因此消除了复杂绘图操作引起的图像闪烁。

回到我们的动画,我们发现出现闪烁、掉帧的问题是因为有些模型没有自动双缓冲(一般这些都是在底层实现的)导致的,而每一帧的动画过程相对来说是比较慢的。长,而摩擦力在排除上一帧动画后,需要几十毫秒的时间才能绘制下一帧,在这段时间里会出现明显的间隙。

解决办法是:

创建一个临时对象并首先将下一帧动画绘制到该临时对象上。 每次实际绘制时,擦除后都会立即复制临时内容。 这个复制过程非常非常高效,所以闪烁基本上可以消除。

具体代码

    p.update = function() {
        if (!this.cacheCanvas) {
            this.cacheCanvas = document.createElement("canvas");
            this.cacheCanvas.width = this.canvas.width;
            this.cacheCanvas.height = this.canvas.height;
        }
        updateMovieClip();    //图形变换
        var ctx = this.cacheCanvas.getContext("2d");
        ctx.setTransform(1, 0, 0, 1, 0, 0);
        ctx.save();
        ctx.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1);     //部分Android机器很奇葩,如果局部刷新会出现空白的情况
        drawMovieclip(ctx);    //绘制
        ctx.restore();
        //双缓冲,先画到临时canvas,再转到正式canvas
        ctx = this.canvas.getContext("2d");
        ctx.clearRect(0, 0, this.canvas.width + 1, this.canvas.height + 1);
        ctx.drawImage(this.cacheCanvas, 0, 0, this.canvas.width, this.canvas.height);
    };