【问题标题】:Redrawing HTML5 canvas incredibly slow重绘 HTML5 画布非常慢
【发布时间】:2015-05-07 23:46:41
【问题描述】:

我刚开始玩 HTML5 画布,希望能用它制作几款游戏。然而,当我开始向它渲染鼠标坐标时,它几乎停止了:

http://jsfiddle.net/mnpenner/zHpgV/

我所做的只是渲染 38 行和一些文本,它应该能够处理,不是吗?

我做错了吗?我希望能够以至少 30 FPS 的速度进行渲染,但对于这样的事情,我希望它能够绘制 1000 次。

或者我只是在工作中使用了错误的工具? WebGL 能胜任这项任务吗?为什么一个会比另一个慢这么多?

String.prototype.format = function() {
    var args = arguments;
    return this.replace(/\{(\d+)\}/g, function(m, n) {
        return args[n];
    });
};
var $canvas = $('#canvas');
var c = $canvas[0].getContext('2d');
var scale = 20;
var xMult = $canvas.width() / scale;
var yMult = $canvas.height() / scale;
var mouseX = 0;
var mouseY = 0;
c.scale(xMult, yMult);
c.lineWidth = 1 / scale;
c.font = '1pt Calibri';

function render() {
    c.fillStyle = '#dcb25c';
    c.fillRect(0, 0, scale, scale);
    c.fillStyle = '#544423';
    c.lineCap = 'square';
    for (var i = 0; i <= 19; ++i) {
        var j = 0.5 + i;
        c.moveTo(j, 0.5);
        c.lineTo(j, 19.5);
        c.stroke();
        c.moveTo(0.5, j);
        c.lineTo(19.5, j);
        c.stroke();
    }
    c.fillStyle = '#ffffff';
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5);
}
render();
$canvas.mousemove(function(e) {
    mouseX = e.clientX;
    mouseY = e.clientY;
    render();
});
&lt;canvas id="canvas" width="570" height="570"&gt;&lt;/canvas&gt;

【问题讨论】:

    标签: html canvas


    【解决方案1】:

    这里的代码变得更好了。

    http://jsfiddle.net/zHpgV/3/

    以下是我更改后您应该考虑的事项的细分:

    • 继续添加到路径而不是停止并使用beginPath 创建新路径。这是迄今为止最大的性能杀手。您最终会得到一条包含成千上万条永远不会被清除的线段的路径。
    • 在初始化时只需要创建一次就不断地创建相同的路径。也就是说,您唯一需要在render 内部调用的是stroke。您无需再拨打lineTo/moveTo,当然也不必连续​​拨打。见注 1。
    • 为一条路径抚摸两次
    • 在 for 循环中抚摸
    • 重绘背景而不是设置 CSS 背景
    • 反复设置线路上限

    注意 1: 如果您计划在应用程序中拥有多个路径,那么您可能应该缓存这样的路径,因为它们永远不会改变。我有一个关于如何做到这一点的教程here

    当然,如果您这样做只是为了制作背景,则应该将其保存为 png,并且您应该使用 CSS 背景图像。

    像这样:http://jsfiddle.net/zHpgV/4/

    然后你的渲染程序突然变得相当小:

    function render() {
        c.clearRect(0, 0, scale, scale);
        c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5);
    }
    

    【讨论】:

    • 我不知道路径会起作用!我认为拥有path 对象会更直观。不过,为什么现在这么慢是有道理的,谢谢!
    • HTML5 Canvas 规范中现在有一个路径对象,您将来可以创建一个路径并调用drawPath。但目前还没有浏览器实现它,可能需要几个月的时间才能使用它。总有一天买!
    • 我正在使用用 adobe cc 制作的画布。所以我看不到像'$canvas[0].getContext('2d');'这样的东西。在哪里可以添加“beginPath”命令?
    • @SimonSarris 你能帮我解决这个问题吗:stackoverflow.com/questions/63873735/…
    • 你教程的链接指向404。你能更新一下吗?
    【解决方案2】:

    正如我在 cmets 中所说,我对这段代码的缓慢感到惊讶,因为我用非常快的动画绘制了更复杂的东西,甚至没有考虑双缓冲。

    于是又看了看,果然发现了一个bug。

    主要问题是绘制路径的堆积。

    每次绘制一条路径时添加c.beginPath();

    这是一个fast rendering of the same thing,以证明它现在成功了。

    画布绘制速度快,可用于动画。

    【讨论】:

    • 这个解决方案很好,但是当你有 500 或 1000 次迭代的循环时不起作用。
    【解决方案3】:

    您不必在每个动画帧中绘制整个网格。将它放在另一个底层画布上(通常称它们为“层”,但它们只是单独的画布元素),因此您只能重绘坐标。

    <div id="canv">
     <canvas id="bgLayer" width="500" height="500" style="z-index: 0"></canvas>
     <canvas id="fgLayer"  width="500" height="500" style="z-index: 1"></canvas>
    </div>
    

    这里是the example 我一直在玩分层画布。表格绘制在底部画布上,球绘制在顶部画布上。它只是一个游乐场,因此有很多需要修复和优化的地方,例如在另一个隐藏的画布上只绘制每个球一次并使用getImageData/putImageData 来提高性能。

    另外,建议使用requestAnimationFrame 来更新画布。相反,您的示例利用了每次鼠标移动,这比需要更频繁(当然,当鼠标移动时)。

    有一个很好的article 可以提高画布性能。此外,关于这个主题还有一个很棒的SO post

    【讨论】:

    • 我花了一段时间才弄清楚你的字面意思是对画布元素进行分层。我认为“层”是画布上下文中的一个概念。这是个好主意。感谢您的提示!
    • 抱歉,我改了它以避免混淆其他人。
    • 这些评论很好,但在这种情况下我没有发现另一个问题。我用非常快的动画绘制了更复杂的东西,甚至不用担心双缓冲。
    • 我被画布卡住了。我的动画不适用于画布。你能帮我解决一下吗。这是我的问题:stackoverflow.com/questions/37208156/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-18
    • 1970-01-01
    • 2012-09-30
    • 2017-03-26
    • 2013-08-27
    • 1970-01-01
    相关资源
    最近更新 更多