画布渲染优化

时间:2016-10-16 09:47:26

标签: javascript performance canvas d3.js optimization

我有一个迷宫,我想要的是在每个单元格上创建一个圆圈,然后将这些圆圈移动到迷宫的右侧作为树。我使用d3过渡和html5画布。我的问题是,如果有任何优化,我可以做。我不知道画布表现,所以我不知道我能期待多少。

对于1600个元素(600 px x 600 px和15px cellSize),动画很流畅。对于3600个元素,它不是。

mazeSelection.selectAll('cell')
    .data(root.descendants())
    .enter()
    .append('cell')
    .attr('radius', 0)
    .attr('cx', d => d.data.ix * cellSize + 0.5 * cellSize,)
    .attr('cy', d => d.data.iy * cellSize + 0.5 * cellSize)
    .transition().duration(2000)
    .attr('radius', cellSize/2)
    .transition().duration(3000)
    .attr('radius', cellSize/4)
    .tween('position', function(d) {
        const i = d3.interpolate([this.getAttribute('cx'), this.getAttribute('cy')], [width + d.x, cellSize * 0.5 + d.y]);
        return (t) => {
            [d.cx, d.cy] = i(t);
            this.setAttribute('cx', d.cx);
            this.setAttribute('cy', d.cy);
        };
    });

context.fillStyle = "white";

let timer = d3.timer(function redraw() {
    // clear maze
    context.beginPath();
    context.fillRect(0, 0, width * 2, height);

    //drawMaze(grid, context, width, height, cellSize);
    context.beginPath();

    // here we must use a function to have access to "this"
    mazeSelection.selectAll('cell')
        .each(function (d) {
            const radius = this.getAttribute('radius'),
                x = this.getAttribute('cx'),
                y = this.getAttribute('cy');

            context.moveTo(x, y);
            context.arc(x, y, radius * 0.5, 0, 2 * Math.PI);

            (d.children || []).forEach(child => {
                context.moveTo(x, y);
                context.lineTo(child.cx, child.cy);
            });
        });

    context.stroke();
    context.fill();
});

1 个答案:

答案 0 :(得分:0)

你必须上学才能获得最佳表现。

我会忽略上半场因为我不确定你在做什么。我猜测.each迭代

中的内容

您在下面的代码中评论的性能方面的错误

// why use a let in global scope????
let timer = d3.timer(function redraw() {
    context.beginPath();
    context.fillRect(0, 0, width * 2, height);
    context.beginPath();

    // You wrote "here we must use a function to have access to "this""
    // What 'this'? I assume it is the iterated cell `this`. Just use the 
    // cell referance, you dont have to use `this`
    mazeSelection.selectAll('cell')
        .each(function (d) {     // dont use callbacks to iterate. For loops are much quicker
            // on chrome const and let are half the speed of var 
            const radius = this.getAttribute('radius'),  // why use getAttribute, a pointless and very CPU expensive way to get an object's property
                x = this.getAttribute('cx'),
                y = this.getAttribute('cy');
            // why even assign the above properties to a const when you can use them directly

            context.moveTo(x, y);
            context.arc(x, y, radius * 0.5, 0, 2 * Math.PI);

            // use a for loop and don't test for the existence of children 
            // by creating a new Array if they don't exist and then calling
            // the array method forEach, a complete waste of CPU time
            (d.children || []).forEach(child => {
                context.moveTo(x, y);
                context.lineTo(child.cx, child.cy);
            });
        });

    context.stroke();
    context.fill();
});

如果您尝试以下方法来迭代对象,您将获得一些额外的性能。然而,3000+可能仍然遥不可及。

var timer = d3.timer(function redraw() {
    context.beginPath();
    context.fillRect(0, 0, width * 2, height);
    context.beginPath();

    var cells = mazeSelection.selectAll('cell')
    for(var i = 0; i < cells.length; i ++){
        var d = cell[i];
        var x = d.x;  // create a local referance as x & y may be used many times
        var y = d.y;
        context.moveTo(x, y);
        context.arc(x, y, d.radius * 0.5, 0, 2 * Math.PI);
        if(d.children){
            var c = d.children;
            for(var j = 0; j < c.length; j++){    
                context.moveTo(x, y);
                context.lineTo(c[j].cx, c[j].cy);
            }
        }

        context.stroke();
        context.fill();
    };

如果您渲染的弧大小相同,则可以通过将弧渲染到屏幕外画布(精灵表)然后使用drawImage(spriteSheet,...绘制弧来获得性能提升,具体取决于硬件/浏览器这可能是次要的或显着的性能提升