Canvas 动画分析

之前用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,也过两个点,这样加速运动就出来了(斜率变大);

最终得出:

  1. 加速运动:当前值 = 初始值 + 动画差值 * (当前次数 / 总次数)^3        *(不一定非得三次方,平方、四次方都可以,就是效果不太好)
  2. 缓冲运动:当前值 = 初始值 + 动画差值 * (1 – (当前次数 / 总次数) ^ 3) 
  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、画出静态图形,找到结束状态,关闭动画;


*本文是很早之前整理的,有点儿粗糙,但思路还算有借鉴意义,所以搬了过来;

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复