5.1 创建Animation类
由于HTML5的画布API未提供支持动画的方法,我们不得不创建我们自己的Animation类来操作动画。本节,我们将介绍动画的基础,并创建一个Animation类,该类适用于本书后面所有的动画项目。
准备工作
由于浏览器和计算机硬件条件并非完全相同,所以根据浏览器、计算机硬件、及动画算法的不同,每个动画的最佳FPS(Frames Per Second)的值也会不同,理解这点很重要。因此,开发者很难计算出每个用户的最佳FPS。幸运的是,浏览器会调用window对象的requestAnimationFrame方法,可以自动确定动画的最佳FPS(谢天谢地!)。本章后面我们将会看到,对一个平滑的动画,FPS的值一般是40到60之间的某个值。
请看上图。要创建动画,我们首先需要初始化舞台上的对象。我们可以把画布看做“舞台”,因为我们把画布上可以运动的对象,看做在舞台上表演的“演员”。而且,舞台提供类似这样的感觉,就是画布上的东西是突然出现的,而不是静静呆在那里的。一旦对象初始化完成,我们就可以启动一个动画循环,来更新舞台,清除画布,重绘舞台,再请求下一个新的动画帧。
由于这些特性可以定义任何类型的动画,我们可以创建一个Animation类,由这个类在幕后处理这些过程,这对我们很有意义。
操作步骤
按照以下步骤,来创建一个Animation类,该类将适用于本章的所有动画实例:
1. 定义Animation构造函数,并创建跨浏览器的requestAnimationFrame方法:
var Animation = function(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = this.canvas.getContext("2d");
this.t = 0;
this.timeInterval = 0;
this.startTime = 0;
this.lastTime = 0;
this.frame = 0;
this.animating = false;
// 由Paul Irish提供
window.requestAnimFrame = (function(callback){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
};
2. 定义getContext()方法:
Animation.prototype.getContext = function(){
return this.context;
};
3. 定义getCanvas()方法:
Animation.prototype.getCanvas = function(){
return this.canvas;
};
4. 定义clear()方法,用于清除画布:
Animation.prototype.clear = function(){
this.context.clearRect(0, 0, this.canvas.width, this.canvas. height);
};
5. 定义setStage()方法,用于设置stage()函数。该函数在每个动画帧都会执行:
Animation.prototype.setStage = function(func){
this.stage = func;
};
6. 定义isAnimating()方法:
Animation.prototype.isAnimating = function(){
return this.animating;
};
7. 定义getFrame()方法,用于返回帧号:
Animation.prototype.getFrame = function(){
return this.frame;
};
8. 定义start()方法,用于启动动画:
Animation.prototype.start = function(){
this.animating = true;
var date = new Date();
this.startTime = date.getTime();
this.lastTime = this.startTime;
if (this.stage !== undefined) {
this.stage();
}
this.animationLoop();
};
9. 定义stop()方法,用于停止动画:
Animation.prototype.stop = function(){
this.animating = false;
};
10. 定义getTimeInterval()方法,用于返回上一帧和当前帧间隔的毫秒数:
Animation.prototype.getTimeInterval = function(){
return this.timeInterval;
};
11. 定义getTime()方法,用于返回动画已经运行的毫秒数:
Animation.prototype.getTime = function(){
return this.t;
};
12. 定义getFps()方法,用于返回动画的当前FPS:
Animation.prototype.getFps = function(){
return this.timeInterval > 0 ? 1000 / this.timeInterval : 0;
};
13. 定义animationLoop()方法,用于处理动画循环:
Animation.prototype.animationLoop = function(){
var that = this;
this.frame++;
var date = new Date();
var thisTime = date.getTime();
this.timeInterval = thisTime - this.lastTime;
this.t += this.timeInterval;
this.lastTime = thisTime;
if (this.stage !== undefined) {
this.stage();
}
if (this.animating) {
requestAnimFrame(function(){
that.animationLoop();
});
}
};
工作原理
Animation类的思想是,通过封装和隐藏动画所需要的所有逻辑,如提供帧间隔、处理动画循环、清除画布等,来简化我们的动画项目。
Animation类的关键在其构造函数中,我们在构造函数中设置了window对象的requestAnimFrame方法,该方法看做是requestAnimationFrame方法的跨浏览器实现,requestAnimationFrame方法允许用户浏览器来决定动画的最佳FPS。FPS完全是动态的,并在整个动画过程中会发生变化。
我们的Animation类也提供了一些方便易用的方法,如getTimeInterval()用于返回上一帧和当前帧间隔的毫秒数,getTime()方法用于返回动画已经运行的毫秒数,stop()方法用于停止动画,clear()方法用于清除画布。
由于我们在第一时间准备好了可以工作的Animation类,本章其余的动画,及将来的动画项目,都将是小菜一碟。