【H5动画】谈谈canvas动画的闪烁问题

一般来说,在H5开发中,使用canvas往往只是为了展示一些简单的图表或者简单短小的动画,很少考虑到有闪烁的问题。 最近,在手机QQ魔法表情的项目中,就遇到了奇葩的闪烁问题。 这里说的闪烁,是指动画刚开始播放,突然出现瞬间空白(大概1帧到2帧的时间)。

闪烁分析

这个魔法表情,实际是html5版本的动画,使用Fanvas(即将腾讯开源),从swf转化为canvas 2d动画。 在iOS体系下,无论哪个机型还是哪个系统版本,都没有出现问题。 但是,在部分Android机器上则出现了很奇葩的闪烁,包括小米note,小米4,三星,魅族。奇怪的是,小米同体系的红米note则完全正常。

翻阅H5 api的资料,我们知道requestAnimationFrame在Android 4.4后才支持,而动画的机制是,如果该接口不可用,则采用setInterval取代。

那么貌似有点眉目了,红米note也是4.4系统,而iOS全系都ok,也许问题就在这。 重温一下FPS和浏览器重绘的知识。浏览器保持一个帧频(一般60fps)刷新画面,这就包括页面中的canvas。而动画的绘制过程,包括几个步骤: 1、擦除整个canvas; 2、计算所有元件/图元的位置颜色; 3、逐个逐个,绘制所有元件到canvas上。 这个过程,由不精准的setInterval驱动,这个时钟无法跟浏览器重绘的频率同步。

那么,就可能出现这样的时序情况: 1、擦除整个canvas; 2、浏览器到达重绘时间点,此时canvas为空白,浏览器绘制空白的canvas; 3、50ms后,这一帧动画所有元件绘制完成(可能会因为动画复杂, 而消耗长时间,超过16ms) 关键点就在这里了。

好招不怕旧

双缓冲,只要对图形图象处理编程有稍稍一些了解,都应该听过这个术语,即使不知道这玩意是什么。这个技术非常非常古老,也非常非常简单,但效果却非常非常好。 来看看百度百科的说明,可能没有wikipedia专业,但我觉得足够解释问题了。

闪烁是图形编程的一个常见问题。需要多重复杂绘制操作的图形操作会导致呈现的图像闪烁或具有其他不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操作造成的闪烁问题。当启用双缓冲时,所有绘制操作首先呈现到内存缓冲区,而不是屏幕上的绘图图面。所有绘制操作完成后,内存缓冲区直接复制到与其关联的绘图图面。因为在屏幕上只执行一个图形操作,所以消除了由复杂绘制操作造成的图像闪烁。

回到我们的动画中,发现异曲同工,闪烁、掉帧的问题根源就是因为部分机型下没有自动实现cnavas的双缓冲(一般这些都是底层实现的),而canvas每一帧动画过程又比较漫长,擦除上一帧动画后,要过几十毫秒才能绘制完成下一帧,而这段时间内就会出现明显的空白。 解决办法就是: 创建一个临时canvas,先把下一帧动画绘制到临时canvas上。在每次真正绘制的时候,擦除正式canvas后,马上drawImage把临时canvas的内容copy过去,而这个copy过程是非常非常高效的,所以基本可以杜绝闪烁。

具体代码
    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);

    };

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程直播室

源码发布:一个Angular写得Markdown编辑器 参考资料

18020
来自专栏IMWeb前端团队

移动端重构实战系列3——各种等分

本文作者:IMWeb 结一 原文出处:IMWeb社区 未经同意,禁止转载 ”本系列教程为实战教程,是本人移动端重构经验及思想的一次总结,也是对sand...

1.2K70
来自专栏九彩拼盘的叨叨叨

我完成的百度前端技术学院任务列表

21020
来自专栏腾讯社交用户体验设计

H5动画开发快车道 - AnimateCC与createjs开发实践

95530
来自专栏大前端开发

微信小程序之生成图片分享

通过社交软件分享的方式来进行营销小程序,是一个常用的运营途径。小程序本身支持直接将一个小程序的链接卡片分享至微信好友或微信群,然后别人就可以通过点击该卡片进入该...

87730
来自专栏Youngxj

给网页添加一个QQ客服悬浮框

51370
来自专栏更流畅、简洁的软件开发方式

学习HTML5之表单

HTML5 的标准已经定了,应该火了,或者已经火了。那么是不是可以学习一下呢? 目前h5的主场还是在手机端,pc还是受困于浏览器的兼容,主要是IE在拖后腿。所以...

52850
来自专栏测试2017

test

Markdown 是用来生成 HTML 结构的。样式和结构分离,算是其设计思想之一。而这也是现在 HTML 的发展方向。作为印证,<center> 这样的纯为样...

22900
来自专栏腾讯Bugly的专栏

通往全栈工程师的捷径 —— React

首先,我们来看看 React 在世界范围的热度趋势,下图是关键词“房价”和 “React” 在 Google Trends 上的搜索量对比,蓝色的是 React...

376100
来自专栏非著名程序员

推荐几个比较炫酷效果的开源项目和开源库

上次推送了一篇关于推荐开源项目和开源库的文章,引起的反响不错,那我就持续搞下去,继续分享我收藏和看到的不错的开源库。相信这些推送应该对大家的帮助很大。 Shin...

36980

扫码关注云+社区

领取腾讯云代金券