【问题标题】:Canvas causes fuzzy images when resized调整大小时画布会导致图像模糊
【发布时间】:2021-04-01 02:10:54
【问题描述】:

在画布上绘制图像时,devicePixelRatio 等于 2 时它们看起来很好。比率设置为 1 时,图像模糊。如果我将显示缩放设置为 200%(devicePixelRatio = 2),它会再次变得清晰。它也仅在调整图像大小时发生。如果您跳过fitToBox 调用并将画布放大以便您可以看到它,则图像看起来很好。这是一个演示https://jsfiddle.net/7oc1e3ja/

const getImageFromUrl = (context, url, coords) => {
  return new Promise(resolve => {
    let img = new Image();
    img.src = url;

    img.onload = () => {
      fitToBox(img, coords);
      context.drawImage(img, coords.left, coords.top, img.width, img.height);
      resolve(img);
    }
  });
};

const fitToBox = (item, box) => {
    let ratio = Math.min(box.width / item.width, box.height / item.height);
    item.width *= ratio;
    item.height *= ratio;
};

const config = {
  imageUrl: 'https://i.imgur.com/y6viWyU.png',
  canvas: { width: 300, height: 300 },
};

let box = { ...config.canvas, top: 0, left: 0 },
    dpr = window.devicePixelRatio || 1,
    canvas = document.getElementById('c'),
    context = canvas.getContext('2d');

canvas.width = config.canvas.width * dpr;
canvas.height = config.canvas.height * dpr;
canvas.style.width = config.canvas.width + 'px';
canvas.style.height = config.canvas.height + 'px';
context.scale(dpr, dpr);

getImageFromUrl(context, config.imageUrl, box)
  .then(img => document.body.appendChild(img));

【问题讨论】:

  • 在成为这个社区的活跃成员 9 年后,您应该更了解我们的标准。说我的代码有问题并且只显示结果的屏幕截图并不是一个好问题。 minimal reproducible example 必须可以从帖子本身访问,而不是外包。对于您的问题,您确定 Windows 机器上的 dpr 变量是 1 吗? IIRC 在操作系统级别有一个默认的 ×1.25 缩放。
  • 是的,我确定 dpr 说的是 1。它在 1.25、1.5、小于 2 的任何地方看起来也很糟糕。
  • 只是不要设置canvas.style.width 或高度。仅使用 HTML 宽度和高度
  • canvas 只是一个包含像素的图像。调整大小时,它被视为图像。如果要保持图像的清晰度,则必须使用widthheight 属性更改大小以将像素添加到画布,更改CSS 尺寸只是拉伸像素,然后重绘所有项目使用适当比例的画布。
  • 更改 CSS 宽度/高度纯粹是为了截图。与问题没有任何关系。注释掉样式更改并运行代码。结果相同。

标签: javascript canvas


【解决方案1】:

只需简单地更新您的逻辑以计算 dpr 值:

dpr = Math.max(window.devicePixelRatio || 2, 2),

在这种情况下,您将拥有 HiDPI 画布。

【讨论】:

  • 在这种情况下,也可以在任何地方设置dpr = 2。我的意思是,它在这种情况下确实有效,但并不能真正解决问题。更多的解决方法。不敢相信越来越多的人没有遇到这个问题。
【解决方案2】:

在 w3 规范中搜索了几个小时后,我发现这可能是由几个原因造成的。

上下文的输出位图(我们看到的)与临时位图不同。画布被允许存储绘图动作而不是在临时位图上而不是进行真正的渲染和更新(出于性能原因)。

暂存位图,当它与输出的位图不同时 位图,只有在读取时才能直接观察到,因此 实现可以,而不是更新这个位图,只需记住 已经应用到它的绘图操作序列,直到 需要位图的实际数据等时间(例如,因为 调用 commit()、drawImage() 或 createImageBitmap() 工厂 方法)。在许多情况下,这会更节省内存。

另外没有严格的规范如何编写缩放图像的算法

本规范未定义何时使用的精确算法 缩小图像,或放大图像时

在不同的引擎中似乎也有不同的实现。我使用 chrome 和 edge(都是 chrome 引擎)获得的图像比在 Firefox 中更平滑。

非常令人困惑的是 context.imageSmoothingEnabled 在任何引擎中都没有效果(设置为 true 和 false)。

所有这些都让我想到,缩放算法在每个引擎中的处理方式都不同,以奇怪的输出结束。

我认为在所有设备上实现相同体验的唯一方法是编写自己的算法,加载图像,使用该算法对其进行缩放(并对其进行平滑处理),然后插入新生成的图像。

但我认为这不值得。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-04
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-27
    • 1970-01-01
    相关资源
    最近更新 更多