javascript - HTML5 Canvas 性能和优化技巧、技巧和编码最佳实践

标签 javascript performance optimization canvas

关闭。这个问题需要更多focused .它目前不接受答案。




7年前关闭。










锁定。这个问题及其答案是locked因为这个问题是题外话,但具有历史意义。它目前不接受新的答案或互动。








你知道 Canvas 的更多最佳实践吗??
请将您所知道的、所学的或在线阅读的任何和所有 Canvas 最佳实践、性能提示/技巧添加到此线程
由于 Canvas 对 Internet 来说仍然很新,而且我在 future 看不到它会变老的迹象,因此没有太多记录在案的“最佳实践”或其他真正重要的技巧,这些技巧对于开发来说是“必须知道的”它在任何一个特定的地方。像这样的事情在鲜为人知的网站上多次散布。
人们需要了解的东西太多了,还有很多东西需要学习。

我想分享一些东西来帮助那些正在学习 Canvas 的人,也许一些已经很了解它的人,我希望从其他人那里得到一些反馈,让他们知道在 HTML5 中使用 Canvas 的一些最佳实践或其他技巧和窍门.
我想从我个人认为对开发人员来说非常有用但非常罕见的事情开始。
1. 缩进你的代码
就像您在其他任何时候一样,无论情况如何,都可以使用任何其他语言。这是其他所有方面的最佳实践,我发现在复杂的 Canvas 应用程序中,在处理多个不同的上下文和保存/恢复状态时,事情可能会变得有点困惑。更不用说代码更具可读性,整体看起来也更干净。
例如:

...
// Try to tell me this doesn't make sense to do
ctx.fillStyle = 'red';
ctx.fill();
ctx.save();
    if (thing < 3) {
        // indenting
        ctx.beginPath();
            ctx.arc(2, 6, 11, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.beginPath();
            ctx.moveTo(20, 40);
            ctx.lineTo(10, 200);
            ctx.moveTo(20, 40);
            ctx.lineTo(100, 40);
        ctx.closePath();
        ctx.save();
            ctx.fillStyle = 'blue'
            ctx.fill();
        ctx.restore();
    } else { 
        // no indenting
        ctx.drawImage(img, 0, 0, 200, 200);
        ctx.save();
        ctx.shadowBlur();
        ctx.beginPath();
        ctx.arc(2, 60, 10, 0, Math.PI*2, false);
        ctx.closePath();
        ctx.fillStyle 'green';
        ctx.fill();
        ctx.restore();
    }
ctx.restore();
ctx.drawRect();
ctx.fill();
...
IF 语句不是比 ELSE 语句更容易和更清晰地阅读并了解立即发生的事情吗?你能看到我在这里说什么吗?我认为这应该是开发人员应该继续练习的一种方法,就像他们在编写普通的 'ol javascript 或任何其他语言时一样。
使用 requestAnimationFrame 而不是 setInterval/setTimeout
setInterval 和 setTimeout 从未打算用作动画计时器,它们只是在时间延迟后调用函数的通用方法。如果您将来将间隔设置为 20 毫秒,但您的函数队列需要比执行时间更长的时间,则在这些函数完成之前,您的计时器不会触发。这可能需要一段时间,这在动画方面并不理想。 请求动画帧 是一种告诉浏览器正在发生动画的方法,因此它可以相应地优化重绘。它还会限制非事件选项卡的动画,因此如果您将其在后台打开,它不会耗尽移动设备的电池。
Nicholas Zakas 写了一篇非常详细且内容丰富的 article about requestAnimationFrame在他的博客上,非常值得一读。如果你想要一些硬性和快速的实现说明,那么 Paul Irish has written a requestAnimationFrame shim – 这就是我最近在我制作的每个 Canvas 应用程序中使用的内容。
实际上
比使用 requestAnimationFrame 代替 setTimeout 和 setInterval 更好,Joe Lambert 写了一个 NEW and improved shim称为 requestInterval 和 requestTimeout,他解释了使用 requestAnimFrame 时存在哪些问题。
您可以查看 gist of the script .
实际上 x2
现在所有的浏览器都 catch 了这个规范,有一个 update to the requestAnimFrame() polyfill , 可能仍然是用于覆盖所有供应商的一种。
使用多个 Canvas
一种用于重动画游戏的技术 @nicolahibbert写在 post of hers on optimizing Canvas games提到最好将多个 Canvas 叠加使用,而不是在一个 Canvas 中完成所有操作。 Nicola 解释说,“在同一 Canvas 上同时绘制太多像素会导致帧率下降。以 Breakout 为例。尝试绘制砖块、球、桨、任何电源或武器, 然后是背景中的每个星星——这根本行不通,依次执行这些指令需要很长时间。通过将星空和游戏的其余部分拆分到单独的 Canvas 上,您可以确保体面的帧率。”
在屏幕外渲染元素
我不得不为我制作的一些应用程序执行此操作,包括 Samsung's Olympic Genome Project facebook app .了解并利用它是否需要,这是一件非常有用的事情。它极大地减少了加载时间,而且它可以是一种非常有用的技术,可以将图像加载到屏幕外,因为它们有时需要一段时间。
var tmpCanvas = document.createElement('canvas'),
    tmpCtx = tmpCanvas.getContext('2d'),
    img = document.createElement('img');

img.onload = function() {
    tmpCtx.drawImage(thumbImg, 0, 0, 200, 200);
};
img.src = '/some/image/source.png';
请注意,图像的 src 是在加载后设置的。这也是要记住的关键事情。一旦图像完成加载并绘制到这些临时 Canvas 中,您就可以使用相同的 ctx.drawImage() 将它们绘制到主 Canvas 上,但不是将图像作为第一个参数,而是使用 'tmpCtx.canvas'引用临时 Canvas 。
其他提示、技巧和资源
  • Canvas test cases
  • Some more canvas and JS tests
  • HTML5Rocks performance Improving
  • ** requestAnimFrame to Optimize Dragging Events

  • Canvas 有一个反向引用
    2d 上下文具有对其关联 DOM 元素的反向引用:
    var ctx = doc.getElementById('canvas').getContext('2d');
    console.log(ctx.canvas);    // HTMLCanvasElement
    
    我很想从其他人那里听到更多关于这方面的信息。我正在制定一份 list ,列出我们应该标准化的内容,以便在我公司的 Front-end Code Standards and Best Practices 中添加一个新部分。 .我很想得到尽可能多的反馈。

    最佳答案

    重绘区域

    动画的最佳 Canvas 优化技术是限制在每帧上清除/绘制的像素数量。最容易实现的解决方案是重置整个 Canvas 元素并重新绘制所有内容,但这对于您的浏览器来说是一项代价高昂的操作。

    在帧之间重复使用尽可能多的像素。这意味着每帧需要处理的像素越少,程序运行得越快。例如,在使用 clearRect(x, y, w, h) 方法删除像素时,仅清除和重绘已更改的像素而不是整个 Canvas 是非常有益的。

    程序 Sprite

    按程序生成图形通常是可行的方法,但有时这不是最有效的方法。如果您要绘制带有实心填充的简单形状,那么按程序绘制它们是最好的方法。但是,如果您使用笔触、渐变填充和其他对性能敏感的构成来绘制更详细的实体,则最好使用图像 Sprite 。

    两者混合使用是可能的。应用程序启动时,在 Canvas 上按程序绘制图形实体一次。之后,您可以通过绘制它们的副本来重用相同的 Sprite ,而不是重复生成相同的阴影、渐变和笔触。

    状态堆栈和转换

    Canvas 可以通过旋转和缩放等转换进行操作,从而改变 Canvas 坐标系。这是了解状态堆栈的重要之处,有两种方法可用:context.save()(将当前状态推送到堆栈)和 context.restore()(恢复到先前的状态)。这使您可以对绘图应用变换,然后恢复到之前的状态,以确保下一个形状不受任何先前变换的影响。状态还包括诸如填充和笔触颜色之类的属性。

    合成

    使用 Canvas 时,手头的一个非常强大的工具是合成模式,除其他外,它允许蒙版和分层。有多种可用的复合模式,它们都是通过 Canvas 上下文的 globalCompositeOperation 属性设置的。复合模式也是状态堆栈属性的一部分,因此您可以应用复合操作,堆叠状态并应用不同的状态,然后恢复到您创建第一个状态之前的状态。这可能特别有用。

    抗锯齿

    为了允许子像素绘图, Canvas 的所有浏览器实现都采用抗锯齿(尽管这似乎不是 HTML5 规范中的要求)。如果您想绘制清晰的线条并注意到结果看起来很模糊,请务必记住抗锯齿。发生这种情况是因为浏览器将插入图像,就好像它实际上是在这些像素之间一样。它会产生更流畅的动画(每次更新您可以真正移动半个像素),但它会使您的图像显得模糊。

    要解决此问题,您需要四舍五入为整数值或偏移半个像素,具体取决于您是绘制填充还是描边。

    对 drawImage() x 和 y 位置使用整数

    如果在 Canvas 元素上调用 drawImage,将 x 和 y 位置四舍五入为整数会快得多。

    Here's a test case on jsperf显示使用整数比使用小数快多少。

    因此,在渲染之前将 x 和 y 位置四舍五入为整数。

    比 Math.round() 快

    Another jsperf test shows Math.round() 不一定是舍入数字的最快方法。使用按位 hack 实际上比内置方法更快。

    Canvas Sprite Optimization

    清除 Canvas

    要清除整个 Canvas 上的任何现有像素,通常使用 context.clearRect(x, y, w, h) - 但还有另一个选项可用。每当设置 Canvas 的宽度/高度时,即使将它们重复设置为相同的值,也会重置 Canvas 。在使用动态大小的 Canvas 时,了解这一点很有好处,因为您会注意到绘图消失了。

    计算分布

    Chrome 开发者工具分析器对于找出您的性能瓶颈非常有用。根据您的应用程序,您可能需要重构程序的某些部分以提高性能以及浏览器如何处理代码的特定部分。

    Optimization techniques

    关于javascript - HTML5 Canvas 性能和优化技巧、技巧和编码最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8205828/

    相关文章:

    java - Elasticsearch 可以流式传输 SearchResponse 吗?

    javascript - 优化 if 条件语句

    python - 优化两个列表之间的比较,给出不同的索引

    javascript - 将线和点与折线图中的 x 轴值对齐

    sql - 与WHERE子句一起使用时优化Oracle CONNECT BY

    mysql - PDO、MySQL - 使用单个 CASE 高效地更新/增加数组中的值

    带条件的 R 累积和

    javascript - AngularJS:让用户识别图像内矩形对象的 Angular 点

    javascript - 调用PhantomJs函数

    javascript - 有没有办法给变量(数字)添加一个原型(prototype),并且原型(prototype)可以改变变量本身?