前面我写过一个视差滚动动画的小例子,代码跟我一如既往写的JavaScript代码一样是面向过程的。
最近在学习面向对象的JavaScript,结合之前看过的一个HTML5动画的实例,感觉在做动画,特别游戏的动画效果上面向过程不是特别好用。
以下这个Canvas动画实例基于面向对象的JavaScript实现,其结构和扩展性觉得都较好。
HTML代码:
<canvas id="canvas">
<p>Your browser does not support the canvas element!</p>
</canvas>
HTML代码依然只需要一个canvas标签即可,标签中的内容是canvas无法使用时的替代文本。
JavaScript代码:
首先是对内建对象Array的扩展,目的是让数组能够删除具体的对象:
Array.prototype.remove = function(object) {
for (var i = 0; i < this.length; i++) {
if (this[i] === object) {
this.splice(i, 1);
break;
}
}
}
然后,是动画基础对象,它包含了所需要绘制的:
function Spirit(image, x, y, order) {
this.image = image;
this.x = x;
this.y = y;
this.order = isNaN(order) ? 0 : order;
this.addTo = function(spirits) {
spirits.push(this);
spirits.sort(function(a, b) {return a.order - b.order;});
}
this.removeFrom = function(spirits) {
spirits.remove(this);
}
}
接着是继承自Spirit的实际绘制对象,其中包含了对象在每帧中形状改变的方法update和绘制方法draw。由于它是直接在画布上绘制圆形,所以调用父类的构造体时会将image置为null。
function Circle(x, y) {
Spirit.call(this, null, x, y); // 调用父类的构造体
this.radius = 1;
this.color = 255;
this.update = function(spirits) {
this.color -= 20;
this.radius += 10;
if (this.color <= 0) {
this.removeFrom(spirits);
}
}
this.draw = function(context) {
context.beginPath();
context.strokeStyle = "rgb("+ this.color +", "+ this.color +", "+ this.color +")";
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.stroke();
}
}
Circle.prototype = new Spirit(); //继承关系
完成对象的定义之后,接下来就是调用它来实现动画效果了。window.onload在本例中就相当于main方法,即程序入口。除了双缓冲的初始化以外,其中还包含了一个onmousemove事件的处理方法,用于向spirits数组中添加对象;以及需要循环执行的draw方法。spirits数组是需要在整个程序中维持的保存当前所需绘制的所有图形的数组,起初想把它作为全局变量来使用,但是这显然破坏了封装,所以将其在主方法中初始化,并在需要用到的地方作为参数来调用。
window.onload = function() {
var canvas = document.getElementById("canvas");
canvas.width = screen.width - 30;
canvas.height = screen.height - 100;
var context = canvas.getContext("2d");
var buffer = document.createElement("canvas");
buffer.width = canvas.width;
buffer.height = canvas.height;
var bufferCtx = buffer.getContext("2d");
var spirits = new Array();
const FPS = 20;
setInterval(draw, 1000 / FPS);
document.onmousemove = function(event) {
var circle = new Circle(event.clientX, event.clientY);
circle.addTo(spirits);
}
function draw() {
bufferCtx.fillRect(0, 0, buffer.width, buffer.height);
context.fillRect(0, 0, canvas.width, canvas.height);
for (x in spirits) {
if (spirits[x].update) {
spirits[x].update(spirits);
}
}
for (x in spirits) {
if (spirits[x].draw) {
spirits[x].draw(bufferCtx);
}
}
context.drawImage(buffer, 0, 0);
}
}
最终的效果是,当鼠标移动时,会在黑色的背景中绘制圆圈,圆圈逐渐变大变淡并最终消失。