上一篇介绍了canvas的基本用法和canvas动画的原理,最后提到为了编程方便,我们会封装一些类。这篇文章对封装的这些类进行简单的介绍。
Stage
舞台。一个项目中只有一个舞台对象,所有可视对象都被添加进舞台的children数组中。舞台对象拥有一个render属性,是Renderer类的一个实例,用于对可视对象进行渲染。Stage提供两个方法,其实现如下:
addChild: function (child) {
this.children.push(child);
},
render: function () {
if (this.update) {
this.update();
}
this.renderer.render(this.children);
requestNextAnimationFrame(this.render.bind(this));
},
复制代码
addChild用于将可视对象添加进children中,render方法相当于上一节中提到的animate方法,用于实现动画循环。其中this.update方法为动画的更新逻辑,每一帧都会执行一次。执行完更新逻辑后重新渲染可视对象,就实现了动画的效果。
Renderer
用于实现渲染,提供一个render方法:
render: function (displayObjectArr) {
if (!this.ctx || typeof this.ctx.drawImage !== 'function') {
return;
}
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
for (var i = 0, n = displayObjectArr.length; i < n; i++) {
displayObjectArr[i].draw(this.ctx);
}
}
复制代码
该方法在stage中调用,先清除上一帧的内容,然后分别调用每一个可视对象的draw方法进行绘制。可视对象的draw方法由可视对象自己实现,这里只负责调用即可。
Loader
用于加载图片资源。提供三个方法:
add: function(src){
this._srcArr.push(src);
},
load: function(callback){
var _this = this;
var count = 0;
for (var i = 0; i < this._srcArr.length; i++) {
var imgObj = this._srcArr[i];
var src = imgObj.src;
var img = new Image();
img.onload = function(){
count++;
_this._imgs[imgObj.name] = img;
if (count === _this._srcArr.length) {
if (callback) {
callback();
}
}
}
img.onerror = function(){
count++;
console.log(src + ' 下载失败');
}
img.src = src;
}
},
get: function(key){
return this._imgs[key];
}
复制代码
add用于添加图片对象,该对象包括name和src两个属性。load用于实现加载。get方法用于通过图片的name获取图片对象。
Sprite
精灵,是一种可视对象,可以理解为一个集成了动画的图形对象,它包含了绘制出这个图形所需要的所有信息,包括一个图片对象,以及它的位置、宽高、角度以及锚点位置等信息。通过这些信息,我们就能将精灵对象绘制在canvas画布上,具体绘制方法如下:
draw: function (ctx) {
ctx.save();
ctx.translate(this.x, this.y);
if (this.rotation) {
ctx.rotate(this.rotation);
}
ctx.translate(-this.pivotX, -this.pivotY);
ctx.drawImage(this.image, 0, 0, this.image.width, this.image.height, 0, 0,
this.width || this.image.width, this.height || this.image.height);
ctx.restore();
}
复制代码
通过改变该对象的x、y、width、height、rotation、pivotX和pivotY属性,就可以改变图片在当前帧中出现的位置、大小、角度等。
Graphics
是另一种可视对象,用于绘制多边形。该对象同样也保存了绘制一个多边形所需要的全部信息,可以用它来绘制线条、矩形、圆等。这个类的实现比较复杂,方法也比较多,不一一介绍,有兴趣的可以去看看源码。这里只说明一下它绘制的思想:多边形对象拥有一个私有数组属性_actions,每一个跟绘制逻辑有关的方法其实是往_actions数组里添加了一个“动作”,这些动作其实就是绘制的具体步骤。在调用draw方法时,会依次执行这些绘制的动作,这样,多边形就被绘制在了canvas画布上。
Polyfill
实现了两个方法,主要用于兼容各个浏览器。一个是requestNextAnimationFrame,是requestAnimationFrame的一个polyfill实现,用法与requestAnimationFrame相同,在浏览器支持requestAnimationFrame时使用requestAnimationFrame,不支持时则使用setTimeout代替;另一个是函数的bind方法,在浏览器支持bind方法时使用原生的bind,不支持时则使用自定义的bind。
例子
OK,写了这么多,究竟好不好用?让我们来试一下:
//定义一个舞台
var stage = new Stage(document.getElementById('canvas'));
//加载资源
var loader = new Loader();
loader.add({name: 'ball', src: './images/ball.png'});
loader.load(createScene);
//创建场景
function createScene() {
//创建Sprite的一个实例对象ball
var ball = new Sprite({
x: 20,
y: 20,
width: 16,
height: 16,
pivotX: 32,
pivotY: 32,
image: loader.get('ball')
});
//将ball加入舞台
stage.addChild(ball);
//定义更新逻辑
stage.update = function () {
ball.x += 2;
ball.y += 1;
ball.rotation += 0.1;
if (ball.width < 64) {
ball.width += 1;
ball.height += 1;
}
}
//开始绘制
stage.render();
}
}
复制代码
不出意外的话,可以看到如下动画效果:
小结
本文对canvas动画中用到的一些类及其实现进行了介绍,并通过一个例子演示了其用法。由于这个系列文章的主要目的是介绍一些基础canvas动画效果的实现原理及方法,而不是实现一个canvas动画引擎,所以只对这些类进行了简单的实现,能够满足后续演示的需求即可。要深入了解比较完备的实现,可以研究一下当前流行的一些框架的源码,如阿里的Hilo,以及国外的phaser等。
对于canvas的基础介绍到这里就结束了,下一篇文章开始,我们来正式介绍如何实现一些特定的动画效果。
奉上本系列文章所涉及的源码的git地址,喜欢的话麻烦给个star哦!