-1

我编写了以下 1000 个边界方块演示,以测试 HTML5 画布的功能。起初它运行良好,但几秒钟后 fps 明显下降。我不确定为什么。任何指针将不胜感激。

var c = document.getElementById("canvas");
var context = c.getContext("2d");

var WIDTH = 600;
var HEIGHT = 800;

c.width = WIDTH;
c.height = HEIGHT;

image = loadImage("square.png");

function loadImage(imageName){
    var i = new Image();
    i.src = imageName;
    return i;
}


function clear(){
    context.fillStyle = "#d0e7f9";
    context.rect(0,0,WIDTH,HEIGHT);
    context.fill();
}
clear();

var SpriteList = [];

var Sprite = (function() { //javascript class(?)... shredders
    function Sprite(){  //constructor
        this.x = Math.random()*WIDTH;
        this.y = Math.random()*HEIGHT;
        this.vx = Math.random()*10;
        this.vy = Math.random()*10;
        SpriteList.push(this);
    }

    Sprite.prototype.update = function(){
        this.x += this.vx;
        this.y += this.vy;

        if (this.x<0 || this.x>WIDTH){
            this.vx *= -1;
        }
        if (this.y<0 || this.y>HEIGHT){
            this.vy *= -1;
        }
    };

    return Sprite;
})();

for (var i = 0;i<1000;i++){
    new Sprite();
}

function draw(){
    clear();
    for (i in SpriteList)
    {
        var s = SpriteList[i];
        s.update();
        context.drawImage(image, s.x, s.y);
    }
}

setInterval(draw,1000/60);
4

2 回答 2

2

代码有一些问题,但发生这种情况的主要原因是这段代码:

此代码将要求您使用beginPath()

function clear(){
    context.fillStyle = "#d0e7f9";
    context.beginPath();
    context.rect(0,0,WIDTH,HEIGHT); /// this will require beginPath();
    context.fill();
}

或者为了避免它,你可以简单地修改代码来做到这一点:

function clear(){
    context.fillStyle = "#d0e7f9";
    context.fillRect(0,0,WIDTH,HEIGHT); /// this does not require beginPath();
}

现场小提琴在这里

/// use a var here    
var image = loadImage("square.png");

/// your image loader is missing - image may not show up
function loadImage(imageName){
    var i = new Image();
    i.onload = nextStep; /// something like this
    i.src = imageName;
    return i;
}

var SpriteList = [];

/// create this as an object
function Sprite(){  //constructor
    this.x = Math.random()*WIDTH;
    this.y = Math.random()*HEIGHT;
    this.vx = Math.random()*10;
    this.vy = Math.random()*10;
    return this;
}

Sprite.prototype.update = function(){
    this.x += this.vx;
    this.y += this.vy;

    if (this.x<0 || this.x>WIDTH){
        this.vx *= -1;
    }
    if (this.y<0 || this.y>HEIGHT){
        this.vy *= -1;
    }
};

/// separate pushing of the instances
for (var i = 0;i<1000;i++){
    SpriteList.push(new Sprite());
}

var oldTime = 0;

function draw(timeElapsed){ /// in milliseconds
    clear();

    var diffTime = timeElapsed - oldTime;

    /// use vars here too
    for (var i = 0, s; s = SpriteList[i]; i++ )
    {
        s.update();
        context.drawImage(image, s.x, s.y);
    }

    oldTime = timeElapsed;

    /// use rAF here
    requestAnimationFrame(draw);
}

draw(0); /// start

setInterval如果浏览器在您给定的时间预算内处理精灵的速度不够快,这可能会导致整个调用堆栈。

通过使用 rAF,浏览器只会在可以请求帧时才请求帧,即使这意味着较低的帧速率 - 您至少不会锁定/减慢浏览器。

(因为您没有提供指向您正在使用的图像的链接,所以我用临时画布替换了它 - 您仍然需要考虑onload实际图像的事件处理程序)。

于 2013-11-06T00:20:28.173 回答
1

几点建议:

使用 image.onload 确保您的 square.png 在使用前已完全加载。

创建 1000 个精灵后,将图像加载放在代码的底部。

var image=new Image();
image.onload=function(){
    draw();
}
image.src="square.png";

不要使用 for(i in SpriteList) 进行迭代。改为这样做:

for(var i=0;i<SpriteList.length;i++)

您的绘图函数可能正在堆叠——在 setInterval 请求另一个 draw() 之前,当前的 draw() 尚未完成。

将 setInterval 替换为 requestAnimationFrame 以停止您的堆叠问题。

function draw(){

    // request another animation frame
    requestAnimationFrame(draw);

    // draw the current frame
    clear();
    for(var i=0;i<SpriteList.length;i++)
    {
        var s = SpriteList[i];
        s.update();
        context.drawImage(image, s.x, s.y);
    }
}
于 2013-11-06T00:14:10.917 回答