2016-05-27 31 views
1

我有一些JavaScript代码在画布上使用基于sprite的动画,并试图查看是否可以使其更有效。我已经在使用​​,但由于它是基于交互的,我还试图弄清楚如何在设置新框架时只绘制框架。但是,无论我尝试什么,即使动画未运行,它仍然会绘制新帧。有时候改变代码甚至会增加CPU使用率。我对这里发生的事情感到非常困惑。仅当设置了新框架时才将动画绘制到画布上?

这里是原代码:

function CanvasSprite(canvas, width, height, sprite_url, rows, columns, total_frames) { 
 
    \t this.canvas = canvas; 
 
    this.width = width; 
 
    \t this.height = height; 
 
    \t this.rows = rows; 
 
\t this.columns = columns; 
 
\t this.total_frames = total_frames; 
 
    
 
    this.frameOld = null; //old frame for comparison 
 
\t this.frame = 0; 
 

 
    var scope = this, 
 
    \t func = function(){ 
 
    \t \t \t scope.onSpriteSheet.call(scope); 
 
     \t \t } 
 
    this.load('img', 'spritesheet', sprite_url, func); 
 
}; 
 

 
CanvasSprite.prototype.onSpriteSheet = function() { 
 
    \t this.sw = this.spritesheet.width/this.columns; 
 
    \t 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) { 
 
    \t this[prop] = document.createElement(type); 
 
    \t this[prop].addEventListener('load', callback); 
 
    this[prop].src = url; 
 
}; 
 
\t 
 
CanvasSprite.prototype.draw = function() { 
 
\t var relativeFrame = Math.round(this.frame * (this.total_frames-1)); \t 
 
\t var column_frame = relativeFrame % this.columns; 
 
    \t \t \t 
 
    \t var sx = this.sw * column_frame; 
 
    \t var sy = this.sh * Math.floor(relativeFrame/this.columns); 
 

 
\t var context = this.canvas.getContext('2d'); 
 
\t context.clearRect(0, 0, this.width, this.height); 
 
    \t context.drawImage(this.spritesheet, sx, sy, this.sw, this.sh, 0, 0, this.width, this.height); 
 
}; 
 
\t \t 
 
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 
 
\t this.frame = frame; //set new frame 
 
};

不管:

function CanvasSprite(canvas, width, height, sprite_url, rows, columns, total_frames) { 
 
    \t this.canvas = canvas; 
 
    this.width = width; 
 
    \t this.height = height; 
 
    \t this.rows = rows; 
 
\t this.columns = columns; 
 
\t this.total_frames = total_frames; 
 
\t this.frame = 0; 
 

 
    var scope = this, 
 
    \t func = function(){ 
 
    \t \t \t scope.onSpriteSheet.call(scope); 
 
     \t \t } 
 
    this.load('img', 'spritesheet', sprite_url, func); 
 
}; 
 

 
CanvasSprite.prototype.onSpriteSheet = function() { 
 
    \t this.sw = this.spritesheet.width/this.columns; 
 
    \t this.sh = this.spritesheet.height/this.rows; 
 
    this.tick(new Date().getTime()); 
 
}; 
 

 
CanvasSprite.prototype.load = function(type, prop, url, callback) { 
 
    \t this[prop] = document.createElement(type); 
 
    \t this[prop].addEventListener('load', callback); 
 
    this[prop].src = url; 
 
}; 
 
\t 
 
CanvasSprite.prototype.draw = function() { 
 
\t var relativeFrame = Math.round(this.frame * (this.total_frames-1)); \t 
 
\t var column_frame = relativeFrame % this.columns; 
 
    \t \t \t 
 
    \t var sx = this.sw * column_frame; 
 
    \t var sy = this.sh * Math.floor(relativeFrame/this.columns); 
 

 
\t var context = this.canvas.getContext('2d'); 
 
\t context.clearRect(0, 0, this.width, this.height); 
 
    \t context.drawImage(this.spritesheet, sx, sy, this.sw, this.sh, 0, 0, this.width, this.height); 
 
}; 
 
\t \t 
 
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) { 
 
\t this.frame = frame; 
 
    //this.tick(new Date().getTime()); 
 
    //putting tick() here actually makes it slower :p 
 
};

一个在进一步修改它的尝试我做了什么,控制台说即使我没有更新帧,它也会与系统时钟同步。 CPU配置文件也反映了这一点。似乎有什么我在这里失踪的重大事件。也许canvas已经在后台优化它了,所以我的JS没有改变或者只是减慢了不必要的逻辑?

+1

你可以用它设置为true,每一通电话了'isDirty'标志,会以某种方式改变框架。然后在主循环中只绘制标志是否为真并将其重置为假。 – K3N

+0

但是,这不仅仅是我在第二个代码示例中所做的另一个版本吗?我试过把条件放在绘制循环中,结果也是一样的。 –

+0

对不起,从头开始。与旧的帧相比并不适用,并且无论是非常低效的,因为这些值实际上通常对应于子帧间隔。现在我只是想弄清楚在哪里放置我的条件,因为我只能让它在实际的绘制循环内工作,因为我的tick函数在查询系统时钟并调用requestAnimationFrame迄今为止最大的CPU猪。 –

回答

0

自己解决!首先,使用isDirty标志而不是尝试比较帧。然后,由于交互实际上以子帧间隔更新动画,因此将计算要绘制的实际帧的线移动到setFrame函数中。还将tick函数同步动画到setFrame中,并在加载时使用调用以在第零帧开始。这样就不会不断地传递一个新的日期和时间。最后,测试是否绘制一个新框架的条件需要在调用draw函数的闭包内。这是因为​​实际上比任何对画布的调用都要大得多。

结果呢?没有CPU使用率,当它没有被更新,并大幅降低时,它是:)

function CanvasSprite(canvas, width, height, sprite_url, rows, columns, total_frames) { 
 
    \t this.canvas = canvas; 
 
    this.width = width; 
 
    \t this.height = height; 
 
    \t this.rows = rows; 
 
\t this.columns = columns; 
 
\t this.total_frames = total_frames; 
 
    this.isDirty = true; 
 

 
    var scope = this, 
 
    \t func = function(){ 
 
    \t \t \t scope.onSpriteSheet.call(scope); 
 
     \t \t } 
 
    this.load('img', 'spritesheet', sprite_url, func); 
 
}; 
 

 
CanvasSprite.prototype.onSpriteSheet = function() { 
 
    \t this.sw = this.spritesheet.width/this.columns; 
 
    \t this.sh = this.spritesheet.height/this.rows; 
 
    this.setFrame(0); 
 
}; 
 

 
CanvasSprite.prototype.load = function(type, prop, url, callback) { 
 
    \t this[prop] = document.createElement(type); 
 
    \t this[prop].addEventListener('load', callback); 
 
    this[prop].src = url; 
 
}; 
 
\t 
 
CanvasSprite.prototype.draw = function() { \t 
 
\t var column_frame = this.frame % this.columns; 
 
    \t var sx = this.sw * column_frame; 
 
    \t var sy = this.sh * Math.floor(this.frame/this.columns); 
 
    
 
\t 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); 
 
}; 
 
\t \t 
 
CanvasSprite.prototype.tick = function(time) { 
 
    var scope = this, 
 
    func = function(time){ 
 
     if (scope.isDirty) { 
 
      scope.draw(time || new Date().getTime()); 
 
      requestAnimationFrame(func, scope.id); 
 
      scope.isDirty = false; 
 
      //only draw to canvas when new frame differs from old 
 
     }; 
 
    }; 
 
    func(); 
 
}; 
 

 
CanvasSprite.prototype.setFrame = function(frame) { 
 
    var tempFrame = Math.round(frame * (this.total_frames-1));; 
 
    if(tempFrame != this.frame) { 
 
     this.frame = tempFrame; 
 
     this.isDirty = true; 
 
     this.tick(new Date().getTime()); 
 
    } 
 
};