У меня есть код JavaScript, который использует анимацию на основе спрайтов на холсте и пытаюсь увидеть, могу ли я сделать ее более эффективной. Я уже использую requestAnimationFrame
, но так как это взаимодействие, я также пытаюсь понять, как заставить его рисовать только кадры, когда установлен новый. Однако, неважно, что я пытаюсь сделать, по-прежнему, кажется, рисует новые кадры, даже когда анимация не работает. Иногда изменение кода даже, по-видимому, увеличивает использование ЦП. Я очень смущен, что происходит здесь.Рисование анимации на холсте только при установке новых кадров?
Вот исходный код:
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
};
Одна из попыток изменить его дальше:
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
};
Независимо от того, что я делаю, консоль говорит, что он синхронизируется с системными часами, даже когда я не обновляю фреймы. Профили CPU также отражают это. Похоже, здесь есть что-то важное. Может быть, холст уже оптимизирует его за кулисами, поэтому мой JS не имеет никакого значения или только замедляет его с помощью ненужной логики?
Вы можете использовать 'isDirty' флаг, который устанавливается истина от каждого вызова, который изменил бы кадр как-то , Затем в основном цикле нужно только рисовать, если флаг является истинным, а также сбросить его на false. – K3N
Но разве это не совсем другая версия именно того, что я делаю во втором примере кода? Я попытался поставить условие в цикл ничьей, а результаты совпадают. –
Извините, поцарапайте это. Сравнение с старыми кадрами не работало, и, несмотря на это, было ужасно неэффективно, поскольку значения фактически часто соответствуют интервалам подкадра. Теперь я просто пытаюсь выяснить, где поставить свой условный, потому что я могу заставить его работать внутри реального цикла рисования, что не повышает эффективность, так как моя функция тика, где я запрашиваю системные часы и вызываю 'requestAnimationFrame', безусловно, самый большой процессор. –