Canvas 基本动画
终于来到了动画这个章节,终于把一些基础的东西都学完了,我觉得应该撒花一下....
Web 天生为交互而生,而 <canvas>
又是 HTML 元素之一,还有强大的 JavaScript
作为操作语言。所以,在 <canvas>
中实现动画是相当的容易的
唯一的小遗憾就是
图像一旦绘制出来,它就是一直保持那样了
如果需要移动它,我们不得不对所有东西(包括之前的)进行重绘。重绘是相当费时的,而且性能很依赖于电脑的速度
当然了,这点遗憾算得了什么呢? 刀山火海我们也要闯闯啊...
Canvas 实现动画的基本步骤
我们先来看看实现动画的基本步骤,哦,不,是如何画出动画中的一帧
-
清空 canvas
只要接下来的内容没有完全充满 canvas (例如背景图),就要需要清空所有
ctx.clear(0,0,canvas.width,canvas.height)
-
保存 canvas 状态
如果接下来要改变 canvas 状态的设置(样式,变形之类的), 又要在每画一帧之时都是原始状态的话,需要先保存一下
ctx.save()
-
绘制动画图形 ( animated shapes )
这才是动画帧的重绘
-
恢复 canvas 状态
如果已经保存了 canvas 的状态,可以先恢复它,然后重绘下一帧
这个是基本步骤,不一定都要遵守的,不过,遵守没什么坏处,不遵守就坏处多多了
定时重绘
在 canvas 上绘制动画的每一帧调用的都是 canvas 提供的或者自定义的方法
但是它只提供了绘制动画的每一帧,要让动画动起来,那么就要定时的重绘了
定时能力就不是 <canvas>
的能力范围了,好在 windows
对象够兄弟,提供了好些方法
比如说 setInterval
、 setTimeout
和 requestAnimationFrame()
这三个方法: window.setInterval()
、window.setTimeout()
和 window.requestAnimationFrame()
都可以定时的回调第一个方法
但它们之间是有差别的
-
window.setInterval()
一定设定,
window.setInterval()
就会一直执行下去,除非程序销毁或者主动调用clearInterval()
取消定时执行 -
window.setTimeout()
这个应该叫做延时执行更好理解些,当然如果延时执行的回调方法又调用它设置下一个延时
那么效果就和
window.setInterval()
差不多了同样的,如果不想执行了,可以调用
clearTimeout()
取消执行 -
requestAnimationFrame(callback)
window.requestAnimationFrame()
方法告诉浏览器希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用
window.setInterval() 动画范例
接下来我们使用 window.setInterval()
实现一个模拟太阳系的动画吧,先看看代码和效果
<!DOCTYPE html> <meta charset="utf-8"> <canvas id="canvas-1" width="300" height="300"> </canvas> <script> var sun = new Image(); var moon = new Image(); var earth = new Image(); var ctx = document.getElementById('canvas-1').getContext('2d'); function init(){ sun.src = 'https://www.twle.cn/static/i/canvas/canvas_animation_sun.png'; moon.src = 'https://www.twle.cn/static/i/canvas/canvas_animation_moon.png'; earth.src = 'https://www.twle.cn/static/i/canvas/canvas_animation_earth.png'; setInterval(function(){draw();},1000/60); } function draw() { ctx.globalCompositeOperation = 'destination-over'; ctx.clearRect(0,0,300,300); // clear canvas ctx.fillStyle = 'rgba(0,0,0,0.4)'; ctx.strokeStyle = 'rgba(0,153,255,0.4)'; ctx.save(); ctx.translate(150,150); // Earth var time = new Date(); ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() ); ctx.translate(105,0); ctx.fillRect(0,-12,50,24); // Shadow ctx.drawImage(earth,-12,-12); // Moon ctx.save(); ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() ); ctx.translate(0,28.5); ctx.drawImage(moon,-3.5,-3.5); ctx.restore(); ctx.restore(); ctx.beginPath(); ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit ctx.stroke(); ctx.drawImage(sun,0,0,300,300); window.requestAnimationFrame(draw); } init(); </script>
代码很简单,我们挑几个终点的地方讲解下
-
setInterval(function(){draw();},1000/60);
设置动画为 60 帧想必你听说过 xx 帧动画,xx 帧动画就是 1 秒绘制多少帧,因为 1 秒 = 1000 毫秒
而
setInterval()
和setTimeout()
方法的时间单位又是毫秒 -
((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds()
这段代码使用当前时间戳(秒+毫秒) 用来绘制当前应该旋转的角度
(2*Math.PI)/6)
其实就是360/6=60°
(2*Math.PI)/6000)
其实就是360/6000=0.06°
-
动画的每一帧都是通过图形变换来实现的
-
ctx.globalCompositeOperation = 'destination-over'
设置图形混合模式这是一个什么模式呢? 就是先画的图片在上,后画的图片在下