在做canvas动画时,精灵封装的好坏,直接影响后续的编程体验。
下文的封装方法来自《HTML5 Canvas核心技术 图形、动画与游戏开发》,实现了精灵与绘制对象的解耦,很好用,平时自己也用这种写法。
一个完整的Sprite包含两部分:
Sprite的基本封装
Sprite Painter实现
拆开Sprite和Painter,实现了解耦,可以随意调整Painter功能。Pinter对象只需要实现: void paint(sprite, context)方法即可。这好像也叫策略模式。
Sprite 基本封装:
// name:精灵名字
// painter: 绘制器,需另外封装
// behaviors: 行为,精灵行为
var Sprite = function(name, painter, behaviors){
this.left = 0,
this.top = 0,
this.width = 10,
this.height = 10,
this.velocityX = 0,
this.velocityY = 0,
this.visible = true,
this.animating = false,
this.painter = undefined, // object with paint(sprite, context)
this.behaviors = [], // objects with execute(sprite, context, time)
if(name){
this.name = name;
}
if(painter){
this.painter = painter;
}
}
Sprite.prototype = {
// painter属性是一个指向Painter对象的引用,使用paint(sprite, context)方法来绘制精灵。
// behaviors属性指向一个对象数组,数组的每个对象都以execute(sprite, context, time)方法来对精灵进行操作
paint: function(context){
if(this.painter.paint !== undefined && this.visible){
this.painter.paint(this, context);
}
},
update: function(context, time){
for(var i = this.behaviors.length; i > 0; --i){
this.behaviors[i-1].execute(this, context, time);
}
}
}
behavior一开始有些难理解。书上的解释是:精灵的行为。
什么叫行为呢?
个人觉得,改变了Sprite基本属性的动作,都叫精灵的行为,改变了top、width、velocityX等balabala的都叫行为。
behavior里面会实现一个excute方法 void excute(sprite, context, time){}。在方法里面会修改各类的值。到Pianter绘制的时候,会实现修改后的效果。也就实现了精灵的多种行为。
精灵绘制器
即提供给Sprite对象的Painter对象,和Sprite解耦。
目前Painter对象分为三类:
描边及填充绘制器
图像绘制器
精灵图绘制器
描边绘制器可以随意控制,只要实现了 void paint(sprite, context)就可以了。
1.图像绘制器
var ImagePainter = function(imgUrl){
this.image = new Image();
this.image.src = imgUrl;
}
ImagePainter.prototype = {
paint: function(sprite, context){
if(this.image.complete){
context.drawImage(this.image, sprite.left, sprite.top, sprite.width, sprite.height);
} else{
this.iamge.onload = function(e){
context.drawImage(this.image, sprite.left, sprite.top, sprite.width, sprite.height);
}
}
}
}
2.精灵绘制器
var SpriteSheetPainter = function(cells){
this.cells = cells;
};
SpriteSheetPainter.prototype = {
cells: [],
cellIndex: 0,
advance: function(){
if(this.cellInde === this.cells.length -1 ){
this.cellIndex = 0;
} else{
this.cellIndex++;
}
},
paint: function(sprite, context){
var cell = this.cells[this.cellIndex];
context.drawImage(spritesheet, cell.x, cell.y, cell.w, cell.h,
sprite.left, sprite.top, cell.w, cell.h);
}
}
精灵动画制作器
SpriteAnimator包含两个参数,Painter数组和回调函数。
var SpriteAnimator = function(painters, elapsedCallback){
this.painters = painters;
if(elapsedCallback){
this.elapsedCallback = elapsedCallback;
}
};
SpriteAnimator.prototype = {
painters: [],
duration: 1000,
startTime: 0,
index: 0,
elapsedCallback: undefined,
end: function(sprite, originalPainter){
sprite.animating = false;
if(this.elapsedCallback){
this.elapsedCallback(sprite);
} else{
sprite.painter = originalPainter;
}
},
start: function(sprite, duration){
var endTime = +new Date() + duration,
period = duration / (this.painters.length),
interval = undefined,
animator = this, // for setInterval() function
originalPainter = sprite.painter;
this.index = 0;
sprite.animating = true;
sprite.painter = this.painter[this.index];
interval = setInterval(){
if(+new Date() < endTime){
sprite.painter = animator.painters[++animator.index];
} else{
animator.end(sprite, originalPainter);
clearIntercal(interval);
}
}, period);
}
}