【Fanvas技术解密】HTML5 canvas实现脏区重绘

先说明一下,fanvas是笔者在企鹅公司开发的,即将开源的flash转canvas工具。

 

脏区重绘(dirty rectangle)并不是一门新鲜的技术了,这在最早2D游戏诞生的时候就已经存在。

复杂的术语或概念就不多说,简单说,脏区重绘就是每一帧绘制图形界面的时候,只重新绘制有变化的区域,而不是全屏刷新。很明显,这肯定能带来性能的提升。

举个例子,看下边两个图:

wps771D.tmp[8]  wps771E.tmp[4]

假设这里是动画的连续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度。这时候如何计算新的矩形呢?

wps772F.tmp[4]

 

这个过程其实就是局部坐标系映射到全局坐标系的问题,涉及到矩阵计算,可以参考我之前写的文章,这里就不多说了。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 = 0; i < dirtyRectList.length; i++) { var rect = dirtyRectList[i]; ctx.clearRect(rect.x, rect.y, rect.width, rect.height); 
} 
ctx.beginPath();
for (var i = 0; i < dirtyRectList.length; i++) {var rect = dirtyRectList[i];ctx.rect(rect.x, rect.y, rect.width, rect.height);
}
ctx.clip();

 

相信变量名已经很明显的暴露了自己的用途,大家应该明白,实现脏区重绘非常简单,只需要在全部绘制前加那么一段clip,搞掂。

不过啊,这里可有一个很大的坑,估计有同学也知道。

wps7730.tmp[4]

正如上图所示,会出现一些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脏区重绘的秘密就彻底曝光了。。。

最后来看看实际的效果(第一张是没有使用脏区重绘,第二张使用脏区重绘):

wps7740.tmp[4]   wps7751.tmp[4]

wps7752.tmp[4] 

wps7762.tmp[4]

当然,这并不是每个动画都有效果,因为一些动画本来就大范围变化,所以Fanvas针对这些情况也做了兼容处理,如果发现脏区太多或者面积太大,就继续使用原来的全屏重绘。

转载于:https://www.cnblogs.com/kenkofox/p/4506592.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javascript 学习教程

1&#xff0c;JavaScript 是一种轻量级的编程语言&#xff0c;是可插入 HTML 页面的编程代码。 2,以<script>标签开始&#xff0c;以</script>标签结束。 3,引用放在外部文件的扩展名为.js的脚本文件 <script src"myScript.js"></script> 4&…

我国非按劳分配收入

我国现阶段非按劳分配收入探讨 我国社会主义初级阶段的个人消费品分配&#xff0c;是通过多种分配方式实现的。除了占主体地位的按劳分配方式之外&#xff0c;还存在许多种类的非按劳分配方式。因此&#xff0c;在我国城乡居民个人收入总额中&#xff0c;除了一部分按劳分配收入…

html调用js里面的函数,html如何调用js函数

html调用js函数的方法&#xff1a;1、用控件本身进行调用&#xff1b;2、通过javascript中的时间控件定时执行&#xff1b;3、通过getElementById调用&#xff1b;4、通过document.getElementsByName调用。本文操作环境&#xff1a;Windows7系统、html5&&javascript1.8…

大部分人都会做错的经典JS闭包面试题

2019独角兽企业重金招聘Python工程师标准>>> 由工作中演变而来的面试题 JS中有几种函数 创建函数的几种方式 三个fun函数的关系是什么&#xff1f; 函数作用域链的问题 到底在调用哪个函数&#xff1f; 后话 转载于:https://my.oschina.net/u/3687565/blog/1549046

STM32片上Flash内存映射、页面大小、寄存器映射

转自&#xff1a;http://blog.chinaunix.net/uid-20617446-id-3847242.html 一、怎么看Flash大小 1.1 通过型号 型号会印在MCU表面&#xff0c;可以通过观察获得&#xff0c;我的是STM32F103RBT6(以下分析基于这个型号)&#xff0c;对照下图的STM32产品命名&#xff0c;可知STM…

如何设计实现一个地址反解析服务?

http://www.cnblogs.com/LBSer/p/4507829.html 一、什么是地址反解析 我们都知道手机定位服务&#xff0c;其本质是汇总各种信号得出一个经纬度坐标&#xff08;x,y&#xff09;&#xff08;具体定位原理可以参考&#xff1a;LBS定位技术、基于朴素贝叶斯的定位算法&#xff09…

html5 hr代码缩减比例,HTML HR size用法及代码示例

DOM HR size属性用于设置或返回元素的size属性的vlue。用法:它返回HR大小属性。hrobject.size用于设置HR大小属性。hrobject.size"value"属性值&#xff1a;value:它包含指定HR元素高度的像素值。返回值&#xff1a;它返回一个字符串值&#xff0c;该值代表HR元素的高…

【转】C++标准转换运算符static_cast

static_cast<new_type> (expression) 虽然const_cast是用来去除变量的const限定&#xff0c;但是static_cast却不是用来去除变量的static引用。其实这是很容易理解的&#xff0c;static决定的是一个变量的作用域和生命周期&#xff0c;比如&#xff1a;在一个文件中将变量…

MySQL Binlog Mixed模式记录成Row格式

背景&#xff1a; 一个简单的主从结构&#xff0c;主的binlog format是Mixed模式&#xff0c;在执行一条简单的导入语句时&#xff0c;通过mysqlbinlog导出发现记录的Binlog全部变成了Row的格式&#xff08;明明设置的是Mixed&#xff09;&#xff0c;现在就说说在什么情况下Bi…

update语句中使用子查询

55. 更改 108 员工的信息: 使其工资变为所在部门中的最高工资, job 变为公司中平均工资最低的 job 1). 搭建骨架 update employees set salary (), job_id () where employee_id 108; 2). 所在部门中的最高工资 select max(salary) from employees where department_id ( s…

html后台数据分类管理,细分数据.html

&#xfeff;细分数据$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.html; };…

SpringBoot的配置文件加载顺序和使用方式

1、bootstrap.properties bootstrap.properties 配置文件是由“根”上下文优先加载&#xff0c;程序启动之初就感知 如&#xff1a;Spring Cloud Config指定远程配置中心地址&#xff0c;就要在这个文件中指定。这样才能在启动之初发现远程配置中心&#xff0c;并从远程获取配置…

Get请求

写在前面的话 XMLHttpRequest对象的open方法的第一个参数为request-type,取值可以为get或post.本篇介绍get请求. get请求的目的,主要是为了获取数据.虽然get请求可以传递数据,但传递数据的目的是为了告诉服务器,给我们什么内容. 使用get请求时,参数都是随url进行传递的. 使用ge…

css3中的BFC,IFC,GFC和FFC(转载)

作者原文网址&#xff1a;http://www.cnblogs.com/dingyufenglian/p/4845477.html What‘s FC&#xff1f; 一定不是KFC&#xff0c;FC的全称是&#xff1a;Formatting Contexts&#xff0c;是W3C CSS2.1规范中的一个概念。它是页面中的一块渲染区域&#xff0c;并且有一套渲染…

javaweb学习总结——Filter高级开发

在filter中可以得到代表用户请求和响应的request、response对象&#xff0c;因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装&#xff0c;再把包装对象传给目标资源&#xff0c;从而实现一些特殊需求。 一、Decorator设计模式 1.1、Decorator设计模…

html期末网页设计,求网页设计的期末作业一份 HTML的

1&#xff0e; 课程设计建议主题方向&#xff1a;电子商务类网站、门户类网站、专题类网站。整体要求&#xff1a;主题鲜明、健康&#xff1b;风格自然、内容充实、完整&#xff1b;布局合理&#xff0c;配色和谐。(5分)2&#xff0e; 网站至少包括15张页面(包括首页)&#x…

Android(java)学习笔记10:同步中的死锁问题以及线程通信问题

1. 同步弊端&#xff1a; &#xff08;1&#xff09;效率低 &#xff08;2&#xff09;如果出现了同步嵌套&#xff0c;就容易产生死锁问题 死锁问题及其代码 &#xff1a; &#xff08;1&#xff09;是指两个或者两个以上的线程在执行的过程中&#xff0c;因争夺资源产生的一种…

4源代码的下载和编译

1、Android移植主要就是Linux内核移植&#xff0c;而Linux内核移植主要是Linux驱动移植&#xff0c;为了开发和测试Linux驱动&#xff0c;要在Ubuntu下搭建两套开发环境&#xff1a;Android应用程序开发环境和Linux内核开发环境&#xff1b; 2、Android源代码包括&#xff1a;内…

在html中三个图片切换,轻松搞定网页中的图片切换

生活中经常看到&#xff0c;像新浪等很多门户网站的首页都有滚动图片的展示&#xff0c;如下图所示&#xff1a;某网站首页滚动切换图片这样不但可以减少文字的单一、乏味&#xff0c;而且可以直观内容&#xff0c;更好的吸引用户。那在我们做软件系统时&#xff0c;是否也可以…

python 进程编程速成

python具有thread多线程库&#xff0c;但多线程并不是真正的多线程&#xff0c;不能充分利用多核CPU资源。 在大多数情况下&#xff0c;python可以使用multiprocessing多进程库&#xff0c;可以轻松完成从单进程到并发执行的转换。 multiprocessing库支持子进程、通信和共享数据…