canvas performance when drawing massive amount of images

Viewed 193

I am about to draw many stars, like 10000 in canvas and continuouslly creating new one and fade out the old one.

Here is my thinking:

  1. create some stars per frame
  2. draw all stars per frame
  3. when star count reaches threshold, create a new canvas and leave the old canvas alone, empty star list. And from now, drawing stars in new canvas. This will make browser only drawing the max count of stars and looks like there are much more stars.
  4. when canvas count reaches threshold, just fade out and remove the very first one.

So there are always new stars created and old stars faded out, also high performance.

But after running about 1 minute, the fps suddenly became very low, what's the problem in there?

Here are my code sample below (only run in full-page mode will cause the problem, and I don't know why. Tested in Chrome 58):

var container, star, starCtx,
    starList = [],
    starShapeList = [],
    canvasList = [],
    starSize = 22,
    maxCanvasCount = 10,
    starCountPerCanvas = 500;
var width = window.innerWidth, height = window.innerHeight;

init();

function init() {
    container = document.createElement('div');

    // for drawing stars
    starCtx = createCanvas();

    star = document.createElement('img');
    star.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAMAAADzapwJAAAAM1BMVEX/hAD/hQD1kA3+hQHykxH7iQbXsDDVsTHqnBnrmxjPtzjGwkTKvT7WsDC7zE++yUzNuTrg0dc+AAAAEXRSTlMBBhELFg0mMCAaOVNGH3psPn/xpHgAAACMSURBVBjTndBLDsMgDEXR5vFsbCCf/a+2CahYidRJ7vAIYcznXUvvadFdU294KCAAKN0nC1eCvuLyqUmo6q4KST+/FNRiuRhPDwZoR2vFnAgWrprbvrec1TEZpx5122q5cQLdSq3zkhhpJT9HLgJoNtPM8fA4rk5S2Q/HPiB6Y5vwJGep67+PjQJf9AUF8wQVfnj+ngAAAABJRU5ErkJggg==';

    star.onload = function () {
        var size = starSize;
        // init 60 stars in each rotate (for star is a regular hexagon)
        for (var i = 0; i < 60; i++) {
            var canvas = document.createElement('canvas');
            var ctx = canvas.getContext('2d');
            canvas.width = size;
            canvas.height = size;
            ctx.translate(size / 2, size / 2);
            ctx.rotate(i * Math.PI / 180);
            ctx.translate(-size / 2, -size / 2);
            ctx.drawImage(star, 0, 0);
            starShapeList.push(canvas);
        }

        // start animation
        animate();
    }

}

function createCanvas() {
    var canvas = document.createElement('canvas');
    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';
    canvas.width = width * window.devicePixelRatio;
    canvas.height = height * window.devicePixelRatio;

    container.appendChild(canvas);
    canvasList.push(canvas);
    // return the ctx of created canvas
    return canvas.getContext('2d');
}

function Star() {
    // pick a random shape
    this.canvas = starShapeList[_.random(starShapeList.length - 1)];

    this.size = starSize;
    this.scale = 0.01;
    // set a random max scale
    this.maxScale = _.random(60, 120) / 100;
    // set a random position
    this.position = {
        x: _.random(width),
        y: _.random(height)
    };

    // start a scale animation
    new TWEEN.Tween(this)
        .to({
            scale: this.maxScale
        }, 1000)
        .start()
}

function animate() {
    // log time per frame
    console.timeEnd('per frame');
    console.time('per frame');
    requestAnimationFrame(animate);
    render();
}

function render() {
    // update tween
    TWEEN.update();

    // clear
    starCtx.clearRect(0, 0, width, height);
    // draw each star in star list
    starList.forEach(function (item) {
        // get current size
        var size = item.size * item.scale;
        starCtx.drawImage(item.canvas, 0, 0, item.size, item.size, item.position.x - size / 2, item.position.y - size / 2, size, size);
    });

    // create 10 stars per frame
    for (var i = 0; i < 10; i++) {
        starList.push(new Star());
    }

    // when star count reached max star count per canvas
    if (starList.length > starCountPerCanvas) {
        // create a new canvas
        starCtx = createCanvas();
        // clear star list
        starList.length = 0;
        // when canvas count reached max canvas count, fade out and remove the very first one
        if (canvasList.length > maxCanvasCount) {
            var c = canvasList.shift();
            $(c).fadeOut(1000, function () {
                this.remove();
            });
        }
    }
}

window.onload = function () {
    document.body.appendChild(container);
}
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    background-color: black;
}

canvas {
    position: absolute;
    top: 0;
    left: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
</head>
<body>
  <script src="http://cdn.bootcss.com/underscore.js/1.8.3/underscore-min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/16.3.5/Tween.min.js"></script>
</body>
</html>

Confirmed it was a Chrome Bug, works fine in IE 11 and FireFox

0 Answers
Related