JS-使用canvas绘制动画

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的基本步骤就是

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.绘制矩形

cxt.fillStyle="#FF0000";
cxt.fillRect(0,0,150,75);

2.绘制线

cxt.moveTo(10,10);
cxt.lineTo(150,50);
cxt.lineTo(10,50);
cxt.stroke();

3.绘制圆形

cxt.fillStyle="#FF0000";
cxt.beginPath();
cxt.arc(70,18,15,0,Math.PI*2,true);
cxt.closePath();
cxt.fill();

4.绘制渐变

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.绘制图

var img=new Image()
img.src="flower.png"
cxt.drawImage(img,0,0);

这些绘制组合使用基本上可以满足我们的一般绘制需求。

绘制动画基本流程

But,如果要绘制动画,还需要配合另一个神接口,

requestAnimationFrame

requestAnimationFrame的用法请自行google。大概原理就是,requestAnimationFrame需要传入一个函数,浏览器每绘制一帧都会通过requestAnimationFrame来调用这个函数。通常,我们把这个函数命名为step,在step中,放入我们的draw函数(这里与Android的draw流程很类似,不过Android更方便些)。

draw与requestAnimationFrame绑定

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放到了一起)

this.hide = function() {
    cancelAnimationFrame(this.animationId);
    this.element && (this.element.style.visibility = "hidden");
};

draw函数

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绘制范围。

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按照步进返回值

var easeInOutQuint = function (t) {
    return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
};

更高明的做法,比如Jquery中,以时间为参数

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就比较简单了

this.draw = function(ctx) {
    ctx.fillStyle = this.color;
    ctx.fillRect(this.x, this.y, 70, 70);
};

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注