2016-05-27 34 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()); 
 
    } 
 
};