Skip to content Skip to sidebar Skip to footer

Best Practice For Zooming And Panning Html5 Canvas With > 10k Objects

I need to build kind of a map in canvas which displays over 10.000 elements (circles) and needs to be zoom- and panable. I described my approach here Android significantly slower i

Solution 1:

My approach would probably be:

  • Create an on-screen canvas the size of the "viewport" (the size of the browser window for instance)

  • Store the objects to draw in a data structure that lets you quickly determine which objects are visible at any given time (given the current viewport position and zoom).

  • Make sure the objects to draw (circles) are available as bitmaps (either loaded from image file or pre-rendered to a canvas if you draw them with canvas).
  • The circles's bitmap should be in the correct size given the zoom level, so pre-render the images to an off-screen canvas with the correct size when the zoom level changes. I'm assuming not all 10 000 points have unique images, that they all look the same or that there's only a handful variations.

Then on each render:

  • Clear the canvas
  • Call drawImage() for each circle being visible within the viewport, but only specify position, not width/height or use any transforms. The point being is that the image should only be "copied" to the viewport canvas 1-1. This is really fast.
  • I would suggest to not redraw on every mousemove event (or window resize etc.). Instead, use window.requestAnimationFrame() to schedule a redraw. That way you're not redrawing more than the browser's "refresh" rate, typically 60fps.
  • Panning in the viewport should be really fast since you're just calling drawImage() without any transformations for each visible circle. When you render and the zoom level has changed, there will be the cost of redrawing the pre-rendered images used as source for drawImage.

Solution 2:

I second @Strilles answer.

here is a filtering example that switches between computing all sprites and computing visible-only sprites ever 5 seconds:

var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 100;
canvas.height = canvas.width;
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgba(255,0,0,0.1)";
;
var sprites = [];
while (sprites.length < 100000) {
    sprites.push({
        x: Math.round(Math.random() * 10000 - 5000),
        y: Math.round(Math.random() * 10000 - 5000)
    });
}
var drawAll = true;
functiondraw() {
    var targets;
    if (drawAll == true) {
        targets = sprites.slice(0);
    }
    else {
        targets = sprites.filter(function (sprite) {
            return sprite.x > -10 && sprite.x < 110 && sprite.y > -10 && sprite.y < 110;
        });
    }
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    for (var t = 0; t < targets.length; t++) {
        var target = targets[t];
        ctx.fillRect(target.x - 5, target.y - 5, 10, 10);
        ctx.strokeRect(target.x - 5, target.y - 5, 10, 10);
    }
}
functionmain() {
    requestAnimationFrame(main);
    for (var i = 0; i < sprites.length; i++) {
        var sprite = sprites[i];
        sprite.y++;
        if (sprite.y > 110) {
            sprite.y -= 200;
        }
    }
    draw();
}
setInterval(function () {
    drawAll = !drawAll;
    console.log(drawAll ? "Draw all" : "Draw filtered");
}, 5000);
main();

Post a Comment for "Best Practice For Zooming And Panning Html5 Canvas With > 10k Objects"