使用>缩放和平移HTML5画布的最佳做法10k物件

时间:2017-04-21 13:12:33

标签: javascript canvas html5-canvas

我需要在画布中构建一种地图,它显示超过10,000个元素(圆圈),需要缩放和平移。我在这里描述了我的方法Android significantly slower in resizing and moving multiple canvas elements,并根据评论中提出的建议改变了我的实施方式。

要平移地图setTransform现在在画布上下文中使用,然后在删除画布后重绘视口中的所有元素。 (我把它们从R树中取出来)。每次mousemove事件都会发生这种情况。

虽然当我有一个约200个要绘制的对象的缩放地图时,这非常快,但是当缩小时需要绘制超过10k个对象时平移非常慢。我显然也需要快速。

满足此要求的最佳做法是什么? 我的方法如下:

  • 在画布上放置一个视口div,使画布更大(每边50%)
  • 使用topleft样式移动div中的画布并减少重绘次数(当画布靠近视口边框时)

2 个答案:

答案 0 :(得分:4)

我的方法可能是:

  • 创建一个大小为“viewport”的屏幕画布(例如浏览器窗口的大小)

  • 将对象存储在数据结构中,以便您可以在任何给定时间快速确定哪些对象可见(给定当前视口位置和缩放)。

  • 确保要绘制的对象(圆圈)可用作位图(如果使用画布绘制,则从图像文件加载或预渲染到画布)。
  • 在给定缩放级别的情况下,圆的位图应该是正确的大小,因此在缩放级别更改时,将图像预渲染到具有正确大小的离屏画布。我假设不是所有10000个点都有独特的图像,它们看起来都一样,或者只有少数变化。

然后在每个渲染上:

  • 清除画布
  • 为视口中可见的每个圆调用drawImage(),但仅指定位置,而不是宽度/高度或使用任何变换。关键是图像应该只“复制”到视口画布1-1。这真的很快。
  • 我建议不要在每次mousemove事件(或窗口调整大小等)上重绘。而是使用window.requestAnimationFrame()来安排重绘。这样你就不会重新绘制浏览器的“刷新”率,通常为60fps。
  • 视口中的平移应该非常快,因为您只是调用drawImage()而不对每个可见圆进行任何转换。渲染并且缩放级别已更改时,将重新绘制用作drawImage源的预渲染图像的成本。

答案 1 :(得分:1)

我第二次回答@Strilles。

这是一个过滤示例,可以在计算所有精灵和计算仅可见精灵之间切换5秒:

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;
function draw() {
    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);
    }
}
function main() {
    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();