这年头,HTML5火遍大江南北呀,神马?火遍全球?对!(在国内跟着火的,还有一个叫“H5”的名儿,具体你懂的)
跟着它顺便火的,还有个HTML5的新标签,叫Canvas,专门用于Web界面画画图,搞点交互,模拟,游戏啥的。虽然大家都称Canvas为HTML5的新标签,看起来好像跟HTML语言有什么关联似的,但其实Canvas画图是通过JavaScript来实现的。所以,如果你想学习Canvas画图,你最好要有一定的JavaScript基础。
前言
提到画图,现实下对于没啥美术天赋及基础滴人来说,那简直不易,就是再怎么画,明明你心目中想画的是白龙马,结果画完画风一变成了草泥马-_-| 而程序里头,这画图涉及到图形学,也不易啊,若还想在静态图形的基础上配个动画,啧啧,必须用上各种小学中学乃至大学各种数学物理知识,那个时候,瞬间发现,原来学了介么多年的数理化,总算有用武之地了,那个内牛满面。
“说了这么多不相干的啰嗦,你说,那今天我们到底要整啥?!” ——路人甲
“来,别急嘛,我们先上一张可爱的效果图”
一张粉嫩的欢乐脸
我嘞个去,有点意思嘛!
How to do it 肿么实现?
想看整个实现的所有源文件?请访问全球最大“同性”交友平台GitHub FunFace了解,想切身体验一下?请不必客气地点击此Demo
光这样就行?那我一些关键点还是看不懂啊啊啊~好吧,俺把源码重点部分该注释的都重新注释助于理解之外,并抽取其中的一些核心代码来谈谈HTML5 Canvas的一些东东。
核心一
先定义一个init函数进行一个初始化的工作,将画布的宽高、鼠标的位置坐标、瞳孔的归位控制、眼睛的位置及半径等等变量做初始化。这里会涉及到一个重要的核心点——requestAnimationFrame
起初,我们要完成一些动画效果的话,会经常使用window.setTimout()或者window.setInterval()来定时不断更新元素的DOM状态位置等来实现动画(画面的更新频率必须要达到每秒60次才能让肉眼看到流畅的动画效果)。不过,这样实现动画的方式极为耗费资源,经常出现这样的情况,刚开始比较流畅,几分钟之后动画可能就不行了。
如今,HTML5/CSS3时代,我们要想在Web里实现一些动画,可选择性已经丰富了很多。借助requestAnimationFrame,CSS3的transition,CSS3的animattion+keyframes等等都能实现想要的一些动画。但是,CSS3动画还是有不少局限性,比如不是所有属性都能参与动画、动画缓动效果太少、无法完全控制动画过程等等,所以有的时候我们还是不得不使用setTimeout或setInterval的方式来实现动画,可setTimeout和setInterval有着严重的性能问题,虽然某些现代浏览器对这两函数进行了一些优化,但还是无法跟CSS3的动画性能相提并论。这个时候,就该requestAnimationFrame上场了,这次这个案例主要就是通过requestAnimationFrame来实现。
关于requestAnimationFrame
来看下Mozilla MDN给出的诠释:
The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint. The method takes as an argument a callback to be invoked before the repaint.
也就说,window.requestAnimationFrame()这个方法是用来在页面重绘之前,通知浏览器调用一个指定的函数,以满足开发者操作动画的需求。这个方法接受一个函数为参,该函数会在重绘前进行调用。
这个方法的原理,其实大致跟setTimeout/setInterval差不多,通过递归调用同一方法来不断更新画面以达到动起来的效果,但比setTimeout/setInterval更具优势:
- 浏览器自动专门优化,且重绘的时间间隔紧紧跟随浏览器的刷新频率,动画更流畅;
- 若页面不是激活状态(隐藏或不可见)下,动画会自动暂停,有效节省CPU、GPU及内存开销;
使用语法:
|
|
浏览器最新支持情况
就目前来说,主流现代浏览器都对它提供了较好的支持,包括IE10+,Firefox,Chrome,Safari,Opera等,在移动设备上,除了Opera Mini之外也都支持requestAnimationFrame,如下图所示:
浏览器Polyfill兼容延伸
支持requestAnimationFrame的浏览器有些还是自己的私有实现,所以有些还得加前缀,对于不支持requestAnimationFrame的浏览器,我们只能使用setTimeout,因为两者的使用方式几近相同,所以这两者的兼容并不难。对于支持requestAnimationFrame的浏览器,我们使用requestAnimationFrame,而不支持的我们优雅降级使用传统的setTimeout。以下为封装后统一能兼容各大浏览器的API。
|
|
核心二
我们画画以及实现动画的基础,是确定好每一个画中的元素的坐标,半径,移动方式,移动速度等等,只有确定好了这些数据,才能进行下一步的绘画过程,这就好比炒一个菜,你得先准备好食材,才能进行下一步的加工烹饪。
我们这个案例最重要的就是确定跟随鼠标位置移动的瞳孔如何绘制,这就涉及到瞳孔的中心点(pupilX, pupilY)的实时位置,涉及到如何计算两点之间距离,为了有动画效果还要计算出加速度及偏移量的弹动等等。
比如这次案例里当鼠标移动时所要计算的“鼠标所在点位置”与“眼睛中心点”两点的距离。一般来说,如果是计算任意两点的距离,有两种方法:一种利用勾股定理计算,适用于两点距离很近的情况;一种按标准的球面大圆劣弧长度计算,适用于距离较远的情况。我们这次主要采用前者进行计算。
|
|
通过以上计算,就可以算出两点的距离。除了距离,我们还得计算出偏移的角度来,可通过下面计算得出:
|
|
偏移角度拿来做什么呢?主要是用来进行下一步计算得到加速度。
|
|
最终我们计算出加速度下的偏移量弹动值 + 瞳孔原坐标值 = 最新瞳孔的中心坐标
|
|
结尾
待所有的数据都确定准备好了,就可以完成最终的简单绘画过程了。
|
|
|
|
此刻,一个粉嫩的欢乐小脸,出现在了你的面前 :)