【问题标题】:Is CSS Scaling More Efficient than drawImage Scaling?CSS 缩放比 drawImage 缩放更有效吗?
【发布时间】:2020-01-23 13:16:11
【问题描述】:

我想知道使用drawImage 缩放和CSS 之间的区别是什么。我已经尝试了这两种方法,CSS 似乎给出了稍微平滑的结果,而 drawImage 往往有点失真 - 特别是如果我平移图像,在几个动画帧的过程中从不同的源 x 和 y 进行 blitting。在对许多不同的比例因子进行测试后,我得出了这个结论。下面我有一个简单的例子。

var img = new Image();
var ctx = document.querySelector("canvas").getContext("2d");

img.src = "img1000x1000.png";// width and height are 1000

ctx.canvas.width  = 100;
ctx.canvas.height = 100;
ctx.imageSmoothingEnabled = false;

这在 img 完全加载后运行。

// method 1 uses drawImage to scale
ctx.drawImage(img, 0, 0, 100, 100, 0, 0, 200, 200);

// method 2 draws the img with no scaling and then uses css to scale
ctx.drawImage(img, 0, 0, 100, 100, 0, 0, 100, 100);
ctx.canvas.style.width  = 200;
ctx.canvas.style.height = 200;

请记住,我并不总是精确地缩放 2 倍大小,而是从 img 的不同区域进行 blitting。我只将整数提交给 drawImage 以使事情尽可能清晰。我还有image-rendering:pixelated 和其他在 CSS 画布上定义的变体,用于 CSS 缩放以保持清晰。

我知道对于我的具体示例,我可以将 img 放大一次并围绕最终缩放的 img 平移,但这不是问题所在。问题是哪个性能更高。对于我的应用程序,我必须在每一帧上放大一个动画平铺地图,因此我在 1:1 空间中从多个源到一个画布,然后使用 requestAnimationFrame 以每秒 30 到 60 次放大最终画布。

最后,它们给出了非常相似的结果。 CSS 对原始图像几乎没有更清晰和真实。 CSS 更快/更高效吗?还是使用 drawImage 进行缩放更高效?

注意/编辑:

我发现如果我使用<!DOCTYPE html> 的标准模式,我必须将字符串值传递给style.widthstyle.height 以用于CSS 方法。因此,如果您使用的是标准模式,请确保将其设置为 "200px" 而不仅仅是 200,否则您的画布将无法缩放。

【问题讨论】:

  • CSS 会更快。
  • 我倾向于同意,但我没有证据。我想它是硬件加速的,就像 CSS 所做的一切一样。这可能也是我会坚持的方法。但是,我无法在网上找到任何速度比较或解释。我看到的三个主要选项是ctx.drawImagectx.scale 和 CSS 样式。我还没试过scale。到目前为止,我只使用 CSS,因为它看起来更干净,并且每帧需要减少 1 次对 drawImage 的调用。
  • 似乎可以使用您可以创建和运行的测试来衡量...
  • @HereticMonkey,如果我找到答案,我会在这里发布或者其他人可能会。无论哪种方式,对于其他有此问题的人来说,它都会派上用场。就是这样。

标签: javascript html5-canvas scaling css-transforms drawimage


【解决方案1】:

Canvas Alpha 与众不同

为获得最佳效果,您应该保持画布大小与其分辨率相同。如果画布没有任何透明像素,您应该关闭 alpha。

使用const ctx = canvas.getContext("2d",{alpha: false})关闭画布阿尔法

缩放无关紧要

对于大多数设备,缩放并不比绘制未缩放的图像更昂贵。

渲染成本取决于绘制的像素数,而不是源中的像素数。

估算渲染成本

drawImage 的成本

如果画布分辨率为 200 x 200,并且您绘制了一个图像 ctx.drawImage 来填充它,需要渲染 40,000 像素。

合成成本

如果该画布是通过 CSS 规则缩放的,即使 alpha 关闭,画布元素也必须合成才能显示。成本将是画布大小占用的像素数。

如果画布的 alpha 为 true,则它必须与背景合成,因此渲染成本是画布的显示大小(以像素为单位)。

如果画布大小 (CSS) 与分辨率匹配并且 alpha 关闭,则不需要为最终呈现合成画布,并且没有额外的合成成本。

渲染成本示例

  • canvas 是一个可见的画布元素
  • image 是可以放入 GPU RAM 的图像

最好关闭 alpha 并且没有 CSS 缩放

canvas.width = canvas.height = 100;
canvas.style.width = canvas.style.height = "100px";
const ctx = canvas.getContext("2d", {alpha: false});
ctx.drawImage(image, 100, 100);

// Total rendering cost 100 * 100 = 10,000 pixels

通过 CSS 和 alpha 关闭缩放

canvas.width = canvas.height = 200;
canvas.style.width = canvas.style.height = "100px";
const ctx = canvas.getContext("2d", {alpha: false});
ctx.drawImage(image, 200, 200);

// Total rendering cost 200 * 200 + 100 * 100 = 50,000 pixels

开启 alpha 渲染,无 CSS 缩放

canvas.width = canvas.height = 100;
canvas.style.width = canvas.style.height = "100px";
const ctx = canvas.getContext("2d");  // alpha is on
// same as const ctx = canvas.getContext("2d", {alpha: true});
ctx.drawImage(image, 100, 100);

// Total rendering cost 100 * 100 + 100 * 100 = 20,000 pixels

注意事项

  • 由于页面设置、滚动和其他各种因素,存在不可避免的渲染成本。由于无法避免,这些成本被忽略了。
  • 并非所有浏览器都提供开启 Alpha 版的选项。
  • 软件渲染和设备功能将对渲染性能产生最显着的影响。

【讨论】:

  • 所以,你说的不是使用哪个函数来合成,而是需要合成的图像的大小。这就说得通了。尽管如此,我还是忍不住注意到,实际的缩放是由 CSS 完成的。它实际上比drawImage 更干净。这使我相信使用了不同的算法。我确实关闭了阿尔法。我正在实施developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/… 此处列出的所有提示,其中之一是不要在每一帧上都使用 drawImage。
  • @Frank 是的 Chrome(我知道)为画布使用不同的着色器。但是,您可以通过ctx.imageSmoothingQuality 将质量设置更改为"low"(默认)"medium",或"high",这将在与ctx.imageSmoothingEnabled = true结合时更改缩放图像的采样方法
  • 这是一个很好的提示。我几乎总是关闭 imageSmoothing,但我肯定会再次查看 CanvasRenderingContext2D 规范,看看是否还有其他可以提高性能的方法。
  • @Frank 为了获得最佳性能,您可以使用 WebGL 渲染 2D 内容。它有点复杂,但是因为 WebGL 让您可以更好地组织状态更改(CPU GPU 之间的通信),您可以获得巨大的性能提升。例如平均笔记本电脑... 2D 上下文峰值在 2000 旋转缩放的褪色透明精灵 60Hz(2000 状态变化)。 WebGL 批处理每次调用约 256 个精灵可以在 60Hz 下对同一精灵进行 32000 次(128 次状态更改)(如果主动冷却和外部风扇散热,则为 64000 次)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-14
  • 1970-01-01
  • 2014-06-08
  • 2020-06-08
相关资源
最近更新 更多