之前用Canvas做过几个动画效果,效果如下图所示(点击图片可跳转到demo页面查看源码):
这几个动画做下来,发现和canvas本身的关系不大,还是“无css3时代”的那一套js动画逻辑;
页面出现了动画,肯定是有元素的属性在改变,动画过程其实就是时间和属性值的函数,比如最简单的匀速运动公式:属性值 = 常量 * 时间;
思路
js 做动画主要逻辑(思路)算是有两类:
1、固定间隔方式:
动画的每一帧(定时器)里边,当前值 = 初始值 + 当前次数 * 固定间隔
通过控制固定间隔的大小来控制总的动画时间,适合匀速运动;
2、固定时间方式(以匀速运动为例):
- 动画的总时间固定,帧率固定,相除得出总的运动次数;
- 动画的结束位置 减去 动画的开始位置,得到动画值的差;
- 动画的每一帧里边,当前值 = 初始值 + 动画差值 * (当前次数 / 总次数)
下面是公式推导,在各种缓冲、加速运动时会用到:
当前次数 / 总次数 =>
小于等于1,等于1的时候,就是动画结束的时候,当前值 = 初始值 + 动画差值 * 1 = 结束值 =>
设 当前次数 / 总次数 为a,那么当前属性值 y 与次数 x 的关系就是 y = ax,如下图坐标系中的 y=ax 线,a 在x1的时候为1 =>
整个运动过程就是从(0,0) 到 p(x1, y1);那么在坐标系里边画一条 y=a^3*x,也过两个点,这样加速运动就出来了(斜率变大);
最终得出:
- 加速运动:当前值 = 初始值 + 动画差值 * (当前次数 / 总次数)^3 *(不一定非得三次方,平方、四次方都可以,就是效果不太好)
- 缓冲运动:当前值 = 初始值 + 动画差值 * (1 – (当前次数 / 总次数) ^ 3)
- 自定义运动:当前值 = 初始值 + 动画差值 * 缓动函数
Canvas动画原理
canvas做动画的原理,就是在canvas这个画板上画出内容,几毫秒后擦除再画下一帧,几毫秒后擦除再画下一帧… 直到最后停止;
canvas比上面的 js 动画多了一步,因为它是一个画板,擦除的时候是全部擦除(如果某个矩形区域内不涉及动画变动,也可以不用清除),所以之前的运动状态还有不做动画的图形每次都要画出来,
编写动画的步骤
1、初始化 canvas 和准备工作:
取兼容的 requestAnimationFrame 和 cancelAnimationFrame;
canvas 的 style 里设置为容器宽高,本身 width / height 属性设置为容器的两倍,这个主要是解决2倍屏模糊的问题;
var requestAni = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
return setTimeout(callback, 1000 / 60);
};
var cancelAni = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame ||
window.msCancelAnimationFrame || function (ani) {
clearTimeout(ani);
};
var ctx = null, canvas = null;
function init() {
var $canvas = $chart.find("canvas");
canvas = $canvas.get(0);
$canvas.css({width: w, height: he});
canvas.width = w2;
canvas.height = he2;
ctx = canvas.getContext("2d");
}
2、找到动画的起始位置和结束位置,也就是动画图形的初始和结束状态;
上面三个例子基本都是圆环或圆,圆的角度如下:
比如上面第二个demo的起始位置:
3、找出次数与动画的关系,这个就和上边的 js 做动画一样了;
匀速运动下,基本逻辑就是:当前值 = 开始值 + 当前次数 * 每次的增量(定值);
demo中的三个例子,都是匀速运动,用的也都是这种方式,之后改一版加速或者缓冲运动的效果对比一下;
当当前值大于等于结束值时,停止动画,画到结束值;
function draw() {
var index = 0;
var myReq = requestAni(step);
function step() {
index++;
var cur = startArc + index * interval; // index:次数;interval:间隔,每次的增量; endArcFront : 结束角度
if (cur >= endArcFront) { // 判断是否要结束动画
cancelAni(myReq);
drawSingle(endArcFront);
return;
}
drawSingle(cur);
myReq = requestAni(step);
}
}
多个元素运动的,需要定义好开始条件;比如第三个demo,画对号里面,定义的是:外层圆走到一半时,内部对号开始动;
4、画出静态图形,找到结束状态,关闭动画;
*本文是很早之前整理的,有点儿粗糙,但思路还算有借鉴意义,所以搬了过来;