Canvas when drawing a huge amount of images

I am going to paint as many stars as 10,000 on canvas and constantly create new ones and fade out of the old.

Here's my thinking:

  • create several stars per frame
  • draw all the stars per frame
  • when the number of stars reaches the threshold, create a new canvas and leave only the old canvas, an empty list of stars. And from now on, painting stars in a new canvas. This will force the browser to only draw as many stars as possible and looks like there are many more.
  • when the number of canvases reaches the threshold, it just disappears and removes the first one.

In this way, new stars always appear and old stars disappear, as well as high performance.

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

Here is my sample code below ( only works in full screen mode, this will cause a 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>
      

Run codeHide result


Confirmed this is a Chrome bug, works fine in IE 11 and FireFox

+3


source to share





All Articles