【Fanvas技术解密】HTML5 canvas实现脏区重绘
先说明一下,fanvas是笔者在企鹅公司开发的,即将开源的flash转canvas工具。
脏区重绘(dirty rectangle)并不是一门新鲜的技术了,这在最早2D游戏诞生的时候就已经存在。
复杂的术语或概念就不多说,简单说,脏区重绘就是每一帧绘制图形界面的时候,只重新绘制有变化的区域,而不是全屏刷新。很明显,这肯定能带来性能的提升。
举个例子,看下边两个图:
假设这里是动画的连续2帧,那么从第一帧到第二帧,其实变化的只有蝴蝶的区域。那么所谓的脏区就是两个图片的红色框之和,要把上一帧的蝴蝶擦掉,然后把新区域的蝴蝶位置也擦掉,接着才能绘制新的背景和蝴蝶。这相比整屏重绘,重绘的面积小了几十倍,由于canvas 2d使用的是CPU处理,那么相应地,CPU处理的像素个数就少了很多倍,顺理成章,动画的效率就会提高。
看起来非常简单,大概来说,只需要2步:
1、找出这一帧变化的矩形区域;
2、利用canvas的api实现脏区重绘。
但是,问题来了,怎么计算变化区域呢?canvas又是否提供了现成的接口呢?我们拿上述蝴蝶的例子,逐步来看看。
首先,我们看关于脏区的计算。
如果动画非常简单,没有使用“显示列表”,所有图案都是一层绘制的,那么“也许”绘制者,也就是开发者了,可能会知道蝴蝶的位置,然后手工指定重绘的区域。呃。。。等等,好像有点什么问题,不可能每次都手工指定重绘的区域!!!
再看看Fanvas里的情况,Fanvas采用了显示列表,把图案拆分为多个元件,元件和元件之间以“显示列表”的形式组织起来,这参考了Flash的技术。这里,蝴蝶被封装为一个Shape,蝴蝶在画面飞舞,抽象为Shape在父元件中移动、旋转。最初,在Shape中绘制蝴蝶的时候,可能占据的矩形区域是(x:0,y:0,width:100,height:50),这里参考的是Shape内部的坐标系(还没放到舞台上)。然后,蝴蝶被添加到舞台上时,需要位移和旋转,例如做了(x:400,y:100)的位移,和旋转了60度。这时候如何计算新的矩形呢?
这个过程其实就是局部坐标系映射到全局坐标系的问题,涉及到矩阵计算,可以参考我之前写的文章,这里就不多说了。http://km.oa.com/articles/show/238103。另外,提一下,这里其实还有一个难点,初始绘制时(x:0,y:0,width:100,height:50),这个矩形是如何计算得到的呢?如果绘制的是一个图片,那当然好计算;如果是一系列的矢量线条,这个就略麻烦了,不过这个不在这里讨论了,因为Fanvas是Flash导出Canvas动画,在导出的时候Flash自带了这个矩阵信息。
上述的计算都在一个前提情况下:我们已知蝴蝶是唯一一个变化的元件,但在实际动画过程中,如何自动识别变化的内容呢?
要从动画的原理说起,动画过程无非分为4种操作:
1. 新建一个元件(例如蝴蝶),添加到舞台上;
2. 移动、旋转、放缩原有的元件;
3. 删除已有的元件;
4. 修改元件的遮罩关系,这点有点特殊,如果对flash动画不熟悉的同学可能不大理解,不过不重要,我们知道有这回事就可以了,不影响文章的继续阅读。
那么,在Fanvas中,我们就需要对上述4种情况分别处理。
1. 新建:只有1个脏矩形,就是这个元件本身;
2. 移动/旋转/放缩:元件上一帧的矩形区域是脏区,新一帧的矩形区域也是脏区;
3. 删除:跟新建情况一样;
4. 遮罩变化:跟2一样。
理清楚这些细节之后,如何实现就比较好办了,无非就是每一帧绘制前把脏区列表情况,然后计算出所有脏区矩形,再开始绘制。
接着,我们再来看第二步,canvas如何具体操作,是否有脏区重绘接口?
其实,canvas并没有真正的脏区重绘接口,不过有一个clip,这个一般用于实现遮罩,不过也可以取巧的用来实现脏区重绘。经笔者测试,简单使用clip虽然性能优化不是太明显,但还是有20%的提升的。再复杂一些,当然大家可以自行根据脏区列表,重写每个元件的绘制方法,自行实现脏区重绘,不过笔者估计啊,js写这么多逻辑,最终还是吃力不讨好。
我们来看看代码:
for (var i = ; i < dirtyRectList.length; i++) {
var rect = dirtyRectList[i];
ctx.clearRect(rect.x, rect.y, rect.width, rect.height);
}
ctx.beginPath();
for (var i = ; i < dirtyRectList.length; i++) {
var rect = dirtyRectList[i];
ctx.rect(rect.x, rect.y, rect.width, rect.height);
}
ctx.clip();
相信变量名已经很明显的暴露了自己的用途,大家应该明白,实现脏区重绘非常简单,只需要在全部绘制前加那么一段clip,搞掂。
不过啊,这里可有一个很大的坑,估计有同学也知道。
正如上图所示,会出现一些1px白线或者没清干净的bug,尤其是舞台本身有拉伸的情况下,这种bug更明显。经过笔者多次摸索,大概搞清楚了,主要就是脏区要算仔细(如果舞台有拉伸,很容易算出来有1、2px差别),画面要等比例拉伸,另外就是清除和重绘时,大方点,给1px的放宽。
最后变成:
for (var i = 0; i < dirtyRectList.length; i++) {
var rect = dirtyRectList[i];
ctx.clearRect(rect.x-1, rect.y-1, rect.width+2, rect.height+2);
}
ctx.beginPath();
for (var i = 0; i < dirtyRectList.length; i++) {
var rect = dirtyRectList[i];
ctx.rect(rect.x-1, rect.y-1, rect.width+2, rect.height+2); }
ctx.clip();
至此,Fanvas脏区重绘的秘密就彻底曝光了。。。
最后来看看实际的效果(第一张是没有使用脏区重绘,第二张使用脏区重绘):
当然,这并不是每个动画都有效果,因为一些动画本来就大范围变化,所以Fanvas针对这些情况也做了兼容处理,如果发现脏区太多或者面积太大,就继续使用原来的全屏重绘。
【Fanvas技术解密】HTML5 canvas实现脏区重绘的更多相关文章
- 【HTML5 Canvas】计算元件/显示对象经过Matrix变换后在上级/舞台上的bounds(边界矩形rect)
如上图所示,这样的一个简单矩形,边界矩形是(x:-28, y:-35, width:152, height:128),这是在这个元件/显示对象自己的坐标空间的范围. 那么把这个放到父元件(舞台)中,再 ...
- 通过分析 WPF 的渲染脏区优化渲染性能
原文:通过分析 WPF 的渲染脏区优化渲染性能 本文介绍通过发现渲染脏区来提高渲染性能. 本文内容 脏区 Dirty Region WPF 性能套件 脏区监视 优化脏区重绘 脏区 Dirty Regi ...
- html5 Canvas和SVG的区别是什么(总结)
html5 Canvas和SVG的区别是什么(总结) 一.总结 一句话总结:都是2D做图,svg是矢量图,canvas是位图.Canvas 是逐像素进行渲染的,适合游戏. 1.svg的全称是什么? S ...
- html5 canvas的教程
原文地址:http://www.cnblogs.com/tim-li/archive/2012/08/06/2580252.html 原作很强悍 导航 前言 基本知识 绘制矩形 清除矩形区域 圆弧 路 ...
- HTML5 canvas画图
HTML5 canvas画图 HTML5 <canvas> 标签用于绘制图像(通过脚本,通常是 JavaScript).不过,<canvas> 元素本身并没有绘制能力(它仅仅是 ...
- html5 canvas 详细使用教程 转
分类: html5(9) 原文地址:http://www.cnblogs.com/tim-li/archive/2012/08/06/2580252.html 原作很强悍 导航 前言 基本知识 绘 ...
- html5和html的区别是什么(精问)
html5和html的区别是什么(精问) 一.总结 一句话总结:html5:简洁(文档生命,链接引入) 语义化(语义化标签) API(canvas,地理位置等) 一些标签(input新类型) 二. ...
- HTML5 程序设计 - 使用HTML5 Canvas API
请你跟着本篇示例代码实现每个示例,30分钟后,你会高喊:“HTML5 Canvas?!在哥面前,那都不是事儿!” 呵呵.不要被滚动条吓到,很多都是代码和图片.我没有分开写,不过上面给大家提供了目录,方 ...
- 赠书:HTML5 Canvas 2d 编程必读的两本经典
赠书:HTML5 Canvas 2d 编程必读的两本经典 这两年多一直在和HTML5 Canvas 打交道,也带领团队开发了世界首款基于HTML5 Canvas 的演示文档工具---AxeSlide( ...
随机推荐
- Material Designer的低版本兼容实现(六)—— Ripple Layout
新版的Android5.0添加了涟漪效果,虽然开源的库提供了各种控件便于大家使用涟漪效果.但是仍旧不可能满足所有需求,因此我今天改出来一个类叫做,LayoutRipple,其实感觉跟应该叫Ripple ...
- 几种常见的JavaScript混淆和反混淆工具分析实战
几种常见的JavaScript混淆和反混淆工具分析实战 xiaix2016-03-05+8共1195751人围观 ,发现 5 个不明物体WEB安全 信息安全常被描述成一场军备竞赛,白帽与黑帽,渗透测试 ...
- JUnit4单元测试入门教程
本文按以下顺序讲解JUnit4的使用 下载jar包 单元测试初体验 自动生成测试类 执行顺序 @Test的属性 下载jar包## 下载地址 在github上,把以下两个jar包都下载下来. 下 ...
- Ubuntu 虚拟机安装几点细节整理
虚拟机或者Wubi安装其实都挺简单的,这里还是再次总结下,给遇到麻烦的同学一点参考. 虚拟机安装 虚拟机直接通过新建-标准-选择镜像,Vmware能够自动识别镜像并进行Easy Install安装,E ...
- 再看copy_on_write缩小临界区的例子
本例子是模拟的读者写者问题,采用shared_ptr+写时拷贝实现,其中我觉得一个比较值得注意的地方是考虑到对象可能在临界区析构而将析构移除临界区,这对于多线程来说要多看多思. #include< ...
- CSS-下拉导航条
Web网站中很多时候都会出现下拉导航条,有的是通过CSS实现,有的通过JavaScript插件实现,其实CSS实现起来比较简单,先来看一个简版的下拉菜单: Html代码通过ul列表实现: <ul ...
- Construct Binary Tree from Preorder and Inorder Traversal leetcode java
题目: Given preorder and inorder traversal of a tree, construct the binary tree. Note: You may assume ...
- 解剖android中的闹钟app 一
首先,看一看android市场上有哪些主流的闹钟app了,我们来进行一个简单的评测: 一.正点闹钟 这是一款源自金山技术的闹钟app,其主力创始团队都是来自于金山,其装机量,下载量都是排名第一.老样子 ...
- python3 验证码图片切割
切割前图片 切割后四个图片 代码 #coding:utf8 import os from PIL import Image,ImageDraw,ImageFile import numpy impor ...
- Java对象的序列化和反序列化源码阅读
前言 序列化和反序列化看起来用的不多,但用起来就很关键,因为稍一不注意就会出现问题.序列化的应用场景在哪里?当然是数据存储和传输.比如缓存,需要将对象复刻到硬盘存储,即使断电也可以重新反序列化恢复.下 ...