canvas performance drop after a few seconds

三世轮回 提交于 2020-01-30 04:03:16

问题


I wrote the following 1000 bounding squares demo as a test of the capabilities of HTML5 canvas. It runs fine at first but then a noticeable drop in fps after a few seconds. I am not sure why. Any pointers would be appreciated.

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);

回答1:


There are a few issues with the code but the main reason for this to happen is this code:

This code will require you to use beginPath():

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

or to avoid it, you can simply modify the code to do this:

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

Live fiddle here

/// 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

The setInterval may cause the whole thing to stack calls if the browser is not fast enough processing the sprites within the time budget you give,.

By using rAF the browser will only request a frame when it can even if that means lower frame rates - you will at least not lock up/slow down the browser.

(as you didn't provide a link to the image you're using I substituted it with a temp canvas - you will still need to consider a onload event handler for the actual image).




回答2:


A few suggestions:

Use image.onload to be sure your square.png is fully loaded before it's used.

Put the image loading at the bottom of your code after you create your 1000 sprites.

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

Don't iterate using for(i in SpriteList). Do this instead:

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

Your draw functions are probably stacking--the current draw() isn't being completed before setInterval is requesting another draw().

Replace setInterval with requestAnimationFrame to stop your stacking problems.

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);
    }
}


来源:https://stackoverflow.com/questions/19801506/canvas-performance-drop-after-a-few-seconds

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!