【发布时间】:2016-09-25 14:49:54
【问题描述】:
我有一些 JavaScript 代码在画布上使用基于精灵的动画,我正在尝试看看我是否可以让它更高效。我已经在使用requestAnimationFrame,但由于它是基于交互的,我还试图弄清楚如何让它在设置新框架时只绘制框架。但是,无论我尝试什么,即使动画没有运行,它似乎仍然在绘制新帧。有时更改代码甚至似乎会增加 CPU 使用率。我真的很困惑这里发生了什么。
这是原始代码:
function CanvasSprite(canvas, width, height, sprite_url, rows, columns, total_frames) {
this.canvas = canvas;
this.width = width;
this.height = height;
this.rows = rows;
this.columns = columns;
this.total_frames = total_frames;
this.frame = 0;
var scope = this,
func = function(){
scope.onSpriteSheet.call(scope);
}
this.load('img', 'spritesheet', sprite_url, func);
};
CanvasSprite.prototype.onSpriteSheet = function() {
this.sw = this.spritesheet.width / this.columns;
this.sh = this.spritesheet.height / this.rows;
this.tick(new Date().getTime());
};
CanvasSprite.prototype.load = function(type, prop, url, callback) {
this[prop] = document.createElement(type);
this[prop].addEventListener('load', callback);
this[prop].src = url;
};
CanvasSprite.prototype.draw = function() {
var relativeFrame = Math.round(this.frame * (this.total_frames-1));
var column_frame = relativeFrame % this.columns;
var sx = this.sw * column_frame;
var sy = this.sh * Math.floor(relativeFrame / this.columns);
var context = this.canvas.getContext('2d');
context.clearRect(0, 0, this.width, this.height);
context.drawImage(this.spritesheet, sx, sy, this.sw, this.sh, 0, 0, this.width, this.height);
};
CanvasSprite.prototype.tick = function(time) {
var scope = this,
func = function(time){
scope.draw(time || new Date().getTime());
requestAnimationFrame(func, scope.id);
//console.log("drawing");
};
func();
};
CanvasSprite.prototype.setFrame = function(frame) {
this.frame = frame;
//this.tick(new Date().getTime());
//putting tick() here actually makes it slower :p
};
尝试进一步修改它:
function CanvasSprite(canvas, width, height, sprite_url, rows, columns, total_frames) {
this.canvas = canvas;
this.width = width;
this.height = height;
this.rows = rows;
this.columns = columns;
this.total_frames = total_frames;
this.frameOld = null; //old frame for comparison
this.frame = 0;
var scope = this,
func = function(){
scope.onSpriteSheet.call(scope);
}
this.load('img', 'spritesheet', sprite_url, func);
};
CanvasSprite.prototype.onSpriteSheet = function() {
this.sw = this.spritesheet.width / this.columns;
this.sh = this.spritesheet.height / this.rows;
if(this.frame != this.frameOld) {
this.tick(new Date().getTime()); //only call tick when new frame differs from old
};
};
CanvasSprite.prototype.load = function(type, prop, url, callback) {
this[prop] = document.createElement(type);
this[prop].addEventListener('load', callback);
this[prop].src = url;
};
CanvasSprite.prototype.draw = function() {
var relativeFrame = Math.round(this.frame * (this.total_frames-1));
var column_frame = relativeFrame % this.columns;
var sx = this.sw * column_frame;
var sy = this.sh * Math.floor(relativeFrame / this.columns);
var context = this.canvas.getContext('2d');
context.clearRect(0, 0, this.width, this.height);
context.drawImage(this.spritesheet, sx, sy, this.sw, this.sh, 0, 0, this.width, this.height);
};
CanvasSprite.prototype.tick = function(time) {
var scope = this,
func = function(time){
scope.draw(time || new Date().getTime());
requestAnimationFrame(func, scope.id);
console.log("drawing");
};
func();
};
CanvasSprite.prototype.setFrame = function(frame) {
this.frameOld = this.frame; //update frameOld with previous one
this.frame = frame; //set new frame
};
无论我做什么,控制台都会说它与系统时钟同步绘制,即使我没有更新帧。 CPU 配置文件也反映了这一点。似乎我在这里缺少一些重要的东西。也许画布已经在幕后对其进行了优化,所以我的 JS 没有产生任何影响,或者只是用不必要的逻辑减慢了它的速度?
【问题讨论】:
-
您可以使用
isDirty标志,该标志在每次调用时都设置为true,以某种方式改变框架。然后在主循环中仅在标志为真时绘制并将其重置为假。 -
但这不正是我在第二个代码示例中所做的另一个版本吗?我也尝试将条件放在绘图循环中,结果是一样的。
-
对不起,从头开始。与旧帧相比不起作用,并且无论如何效率都非常低,因为这些值实际上通常对应于子帧间隔。现在我只是想弄清楚我的条件放在哪里,因为我只能让它在实际的绘制循环中工作,这并没有提高效率,因为我查询系统时钟并调用
requestAnimationFrame的刻度函数是迄今为止最大的 CPU 消耗者。 -
问题是我有同步所有动画作为闭包实现的功能......这意味着它只通过范围一次,因此没有接收到
isDirty标志的更新。我不敢相信我真的希望 JS 中存在指针...
标签: javascript html animation canvas requestanimationframe