HTML5中提供了transform transition等动画方式,已经能够满足绝大部分动画需求。
但在移动端,使用Transform等还是会出现不流畅的情况,比如背景上一个无限循环的动画,不管是使用setInterval 还是捕捉每次的AnimationEnd来实现,都会有一定的问题,因为我们设定的延时还是动画时间都不能得到保证,并且会影响页面性能。
优化是无尽的,所以学习使用Canvas
使用Canvas能做什么?
1.知道/控制每帧的绘制
2.预加载img来绘制
3.canvas保证了性能
如何使用Canvas?
http://www.w3school.com.cn/html5/html_5_canvas.asp
W3C等有canvas的简单介绍,使用Canvas的基本步骤就是
1 2 |
var c=document.getElementById("myCanvas"); var cxt=c.getContext("2d"); |
拿到canvas标签的dom,调用dom的getContext接口拿到Canvas类,就可以使用canvas的各种接口了
Canvas接口手册:http://www.w3school.com.cn/tags/html_ref_canvas.asp
canvas简单使用
1.绘制矩形
1 2 |
cxt.fillStyle="#FF0000"; cxt.fillRect(0,0,150,75); |
2.绘制线
1 2 3 4 |
cxt.moveTo(10,10); cxt.lineTo(150,50); cxt.lineTo(10,50); cxt.stroke(); |
3.绘制圆形
1 2 3 4 5 |
cxt.fillStyle="#FF0000"; cxt.beginPath(); cxt.arc(70,18,15,0,Math.PI*2,true); cxt.closePath(); cxt.fill(); |
4.绘制渐变
1 2 3 4 5 |
var grd=cxt.createLinearGradient(0,0,175,50); grd.addColorStop(0,"#FF0000"); grd.addColorStop(1,"#00FF00"); cxt.fillStyle=grd; cxt.fillRect(0,0,175,50); |
5.绘制图
1 2 3 |
var img=new Image() img.src="flower.png" cxt.drawImage(img,0,0); |
这些绘制组合使用基本上可以满足我们的一般绘制需求。
绘制动画基本流程
But,如果要绘制动画,还需要配合另一个神接口,
1 |
requestAnimationFrame |
requestAnimationFrame的用法请自行google。大概原理就是,requestAnimationFrame需要传入一个函数,浏览器每绘制一帧都会通过requestAnimationFrame来调用这个函数。通常,我们把这个函数命名为step,在step中,放入我们的draw函数(这里与Android的draw流程很类似,不过Android更方便些)。
draw与requestAnimationFrame绑定
1 2 3 4 5 6 7 8 |
this.show = function() { var step = function() { this.draw(); this.animationId = requestAnimationFrame(step.bind(this)); }; this.animationId = requestAnimationFrame(step.bind(this)); this.element.style.visibility = "visible"; }; |
解绑(这里把绑定与show/hide放到了一起)
1 2 3 4 |
this.hide = function() { cancelAnimationFrame(this.animationId); this.element && (this.element.style.visibility = "hidden"); }; |
draw函数
1 2 3 4 5 6 |
this.draw = function() { this.flash++; this.context.clearRect(0, 0, this.element.width, this.element.height); this.rect0.update(); this.rect0.draw(this.context); }; |
draw函数中我们把绘制分为两步,一步是update(和Android类比就是在这里做mesure,确定View中各个元素的位置),第二步是绘制。
子View中实现各自的update和draw
需要注意的是,Canvas的使用宽高默认为300 150,我们设定的宽高只会对Canvas进行缩放。
update-动画曲线
使用css动画时,可以方便的给一个动画曲线,比如easeinout等,使用canvas绘制就需要我们不断的update绘制范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
this.update = function() { if (!this.isAnimating) { return; } this.t += 0.05; switch (this.direction) { case "right": this.x = easeInOutQuint(this.t) * 70; break; case "down": this.y = easeInOutQuint(this.t) * 70; break; case "left": this.x = (1 - easeInOutQuint(this.t)) * 70; break; case "up": this.y = (1 - easeInOutQuint(this.t)) * 70; break; } if (this.t >= 1) { this.nextDirection(); this.t = 0; } }; |
在update里面,我们完成让一个矩形以指定动画曲线在四个方向转圈的位移。
缓动函数就负责返回位移的值
下面的easeInOutQuint按照步进返回值
1 2 3 |
var easeInOutQuint = function (t) { return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t; }; |
更高明的做法,比如Jquery中,以时间为参数
1 2 3 4 5 6 7 8 9 10 11 |
function easeOutBounce(progress, currentTime, begin, change, duration) { if ((currentTime/=duration) < (1/2.75)) { return change*(7.5625*currentTime*currentTime) + begin; } else if (currentTime < (2/2.75)) { return change*(7.5625*(currentTime-=(1.5/2.75))*currentTime + .75) + begin; } else if (currentTime < (2.5/2.75)) { return change*(7.5625*(currentTime-=(2.25/2.75))*currentTime + .9375) + begin; } else { return change*(7.5625*(currentTime-=(2.625/2.75))*currentTime + .984375) + begin; } } |
draw就比较简单了
1 2 3 4 |
this.draw = function(ctx) { ctx.fillStyle = this.color; ctx.fillRect(this.x, this.y, 70, 70); }; |