简体   繁体   English

几秒钟后画布性能下降

[英]canvas performance drop after a few seconds

I wrote the following 1000 bounding squares demo as a test of the capabilities of HTML5 canvas. 我编写了以下1000个边界正方形演示,以测试HTML5 canvas的功能。 It runs fine at first but then a noticeable drop in fps after a few seconds. 首先,它运行良好,但几秒钟后,fps明显下降。 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);

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() : 这段代码将要求您使用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,. 如果浏览器在给定的时间预算内不够快地处理精灵,则setInterval可能会导致整个事件堆叠起来。

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. 通过使用rAF,浏览器只会在可能时请求帧,即使这意味着较低的帧速率-您至少不会锁定/降低浏览器的速度。

(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). (由于您未提供所使用图像的链接,因此我将其替换为临时画布-您仍然需要考虑实际图像的onload事件处理程序)。

A few suggestions: 一些建议:

Use image.onload to be sure your square.png is fully loaded before it's used. 使用image.onload来确保您的square.png在使用前已完全加载。

Put the image loading at the bottom of your code after you create your 1000 sprites. 创建1000个精灵后,将图像加载置于代码的底部。

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

Don't iterate using for(i in SpriteList). 不要使用for(SpriteList中的i)进行迭代。 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(). 您的绘制函数可能正在堆叠-当前的draw()在setInterval请求另一个draw()之前尚未完成。

Replace setInterval with requestAnimationFrame to stop your stacking problems. 将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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM