通常来讲,在H5开发中,使用canvas每每只是为了展现一些简单的图表或者简单短小的动画,不多考虑到有闪烁的问题。 最近,在手机QQ魔法表情的项目中,就遇到了奇葩的闪烁问题。 这里说的闪烁,是指动画刚开始播放,忽然出现瞬间空白(大概1帧到2帧的时间)。html
这个魔法表情,实际是html5版本的动画,使用Fanvas(即将腾讯开源),从swf转化为canvas 2d动画。 在iOS体系下,不管哪一个机型仍是哪一个系统版本,都没有出现问题。 可是,在部分Android机器上则出现了很奇葩的闪烁,包括小米note,小米4,三星,魅族。奇怪的是,小米同体系的红米note则彻底正常。html5
翻阅H5 api的资料,咱们知道requestAnimationFrame在Android 4.4后才支持,而动画的机制是,若是该接口不可用,则采用setInterval取代。编程
那么貌似有点眉目了,红米note也是4.4系统,而iOS全系都ok,也许问题就在这。 重温一下FPS和浏览器重绘的知识。浏览器保持一个帧频(通常60fps)刷新画面,这就包括页面中的canvas。而动画的绘制过程,包括几个步骤: 一、擦除整个canvas; 二、计算全部元件/图元的位置颜色; 三、逐个逐个,绘制全部元件到canvas上。 这个过程,由不精准的setInterval驱动,这个时钟没法跟浏览器重绘的频率同步。canvas
那么,就可能出现这样的时序状况: 一、擦除整个canvas; 二、浏览器到达重绘时间点,此时canvas为空白,浏览器绘制空白的canvas; 三、50ms后,这一帧动画全部元件绘制完成(可能会由于动画复杂, 而消耗长时间,超过16ms) 关键点就在这里了。api
双缓冲,只要对图形图象处理编程有稍稍一些了解,都应该听过这个术语,即便不知道这玩意是什么。这个技术很是很是古老,也很是很是简单,但效果却很是很是好。 来看看百度百科的说明,可能没有wikipedia专业,但我以为足够解释问题了。浏览器
闪烁是图形编程的一个常见问题。须要多重复杂绘制操做的图形操做会致使呈现的图像闪烁或具备其余不可接受的外观。双缓冲的使用解决这些问题。双缓冲使用内存缓冲区来解决由多重绘制操做形成的闪烁问题。当启用双缓冲时,全部绘制操做首先呈现到内存缓冲区,而不是屏幕上的绘图图面。全部绘制操做完成后,内存缓冲区直接复制到与其关联的绘图图面。由于在屏幕上只执行一个图形操做,因此消除了由复杂绘制操做形成的图像闪烁。动画
回到咱们的动画中,发现殊途同归,闪烁、掉帧的问题根源就是由于部分机型下没有自动实现cnavas的双缓冲(通常这些都是底层实现的),而canvas每一帧动画过程又比较漫长,擦除上一帧动画后,要过几十毫秒才能绘制完成下一帧,而这段时间内就会出现明显的空白。 解决办法就是: 建立一个临时canvas,先把下一帧动画绘制到临时canvas上。在每次真正绘制的时候,擦除正式canvas后,立刻drawImage把临时canvas的内容copy过去,而这个copy过程是很是很是高效的,因此基本能够杜绝闪烁。this
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); };