gpt4 book ai didi

javascript - 仅当设置新帧时才将动画绘制到 Canvas 上?

转载 作者:行者123 更新时间:2023-11-28 05:58:12 27 4
gpt4 key购买 nike

我有一些在 Canvas 上使用基于 Sprite 的动画的 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 配置文件也反射(reflect)了这一点。看来我在这里缺少一些重要的东西。也许 Canvas 已经在幕后对其进行了优化,因此我的 JS 没有产生任何影响,或者只是用不必要的逻辑减慢了速度?

最佳答案

我自己解决了!首先,使用 isDirty 标志而不是尝试比较帧。然后,由于交互实际上以子帧间隔更新动画,因此将计算要绘制的实际帧的行移至 setFrame 函数中。还将同步动画的 tick 函数移至 setFrame 中,并在加载时调用该函数以从零帧开始。这样就不会不断地传递新的日期和时间。最后,测试是否绘制新帧的条件需要位于调用绘制函数的闭包内。这是因为 requestAnimationFrame 实际上比对 Canvas 的任何调用更消耗 CPU。

结果呢?不更新时不会占用 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.isDirty = true;

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.setFrame(0);
};

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 column_frame = this.frame % this.columns;
var sx = this.sw * column_frame;
var sy = this.sh * Math.floor(this.frame / 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){
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());
}
};

关于javascript - 仅当设置新帧时才将动画绘制到 Canvas 上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37480507/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com