【问题标题】:Canvas: Resizing image without blurring画布:调整图像大小而不模糊
【发布时间】:2018-04-23 08:42:52
【问题描述】:

我正在尝试在画布中表示图像,并希望画布为其父容器宽度的某个百分比。问题是我在将图像添加到画布时以某种方式模糊了图像,我不想模糊图像。

我认为添加 ctx.imageSmoothingEnabled = false 会禁用模糊,但它似乎没有帮助。

有谁知道如何防止下面的图像在此画布中模糊?任何想法将不胜感激!

var img = new Image();
img.src = 'https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg';

img.onload = function() { 
  var canvas = document.querySelector('canvas');
  canvas.width = hidden.clientWidth;
  canvas.height = hidden.clientHeight;
  var ctx = canvas.getContext('2d');
  ctx.webkitImageSmoothingEnabled = false;
  ctx.mozImageSmoothingEnabled = false;
  ctx.imageSmoothingEnabled = false;
  ctx.drawImage(hidden,
    0, 0, this.naturalWidth, this.naturalHeight,
    0, 0, hidden.clientWidth, hidden.clientHeight)

}
#container {
  width: 800px;
  background: #efefef;
}

#hidden {
  position: absolute;
  top: -1000%;
}

#hidden,
#canvas {
  width: 60%;
}
<div id='container'>
  <img src='https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg' id='hidden'>
  <canvas></canvas>
</div>

【问题讨论】:

  • 如果画布分辨率与显示设备分辨率不匹配,例如画布像素大于显示像素,则会出现伪像。 imageSmoothingEnabled(平滑是双线性过滤)仅适用于画布内容。然后画布独立于其渲染上下文被渲染到页面。您可以使用 CSS 规则 image-rendering: pixelated 来防止浏览器使用双线性过滤显示画布。
  • @Blindman67 我承认我不知道该怎么评价你的评论。我确实知道图像(#hidden)和画布具有相同的大小,而后者的颗粒感比前者大得多(请参阅下面的 Hugo 帖子)。是否可以在画布内实现图像的细节层次?

标签: javascript html css canvas


【解决方案1】:

画布分辨率和显示尺寸不一样。

查看图像,我看到它的大小为 444 像素 x 800 像素,分辨率非常低。它的显示方式很大程度上取决于显示设备。

在您的问题中,将画布显示宽度设置为容器大小的 60%,并将画布分辨率设置为图像 (444by800)。

很有可能是显示宽度和画布分辨率不匹配。

画布显示尺寸

  • canvas.style.widthcanvas.style.height 代表页面上图像将填充的区域,与画布分辨率无关。

画布分辨率

  • canvas.widthcanvas.height 属性表示画布的分辨率(以像素为单位),用于存储图像内容的实际像素数

浏览器正在添加模糊。

假设 60%(#hidden {width : 60%})是 1920 像素宽的高清显示器,画布将被拉伸到比画布大 2.6 倍的60% * 1920 = 1152px

画布内容没有模糊,您已经正确绘制了图像。是浏览器以大于画布分辨率的尺寸将画布渲染到页面上,从而添加了模糊效果。

简单修复

要阻止浏览器模糊画布的内容,请使用 CSS 属性 image-rendering: pixelated;,如下例所示。

var img = new Image();
img.src = 'https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg';

img.onload = function() { 
  canvas.width = this.naturalWidth
  canvas.height = this.naturalHeight
  var ctx = canvas.getContext('2d');
  // ctx.imageSmoothingEnabled = false;  // not needed as you are not streaching the image
  ctx.drawImage(this, 0, 0);

}
.big {
  width:100%;
  image-rendering: pixelated;
}
&lt;canvas class="big" id="canvas"&gt;&lt;/canvas&gt;

简单锐化两种色调的黑白。

图像的分辨率非常低,因此在大多数情况下您无法提高质量。

但由于它是黑白两种色调(不是黑色、灰色和白色),您可以在更大的画布上拉伸它并增加对比度以消除渲染到画布时引入的一些模糊。

p>

它是通过使用屏幕和颜色燃烧合成操作将画布多次渲染到自身之上来完成的。增加对比度以减少模糊,同时保持较少像素化的外观。

为了突出差异,使用此方法仅对图像的左半部分进行了锐化。

var img = new Image();
img.src = 'https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg';

img.onload = function() { 
  const bounds = canvas.getBoundingClientRect(); //Get the canvas display sise
  
  canvas.width = bounds.width;
  canvas.height = this.naturalHeight * (bounds.width / this.naturalWidth);
  var ctx = canvas.getContext('2d');
  // draw to canvas 
  ctx.drawImage(this, 0, 0,canvas.width,canvas.height);
  // draw again using screen to up white areas
  ctx.globalCompositeOperation = "screen";
  ctx.drawImage(canvas, 0, 0,canvas.width/ 2,canvas.height,0, 0,canvas.width/ 2,canvas.height);
  // draw again to pukl grays down some 
  ctx.globalCompositeOperation = "color-burn";
  ctx.drawImage(canvas, 0, 0,canvas.width/ 2,canvas.height,0, 0,canvas.width/ 2,canvas.height);
  // draw again using screen to up white areas
  ctx.globalCompositeOperation = "screen";
  ctx.globalAlpha = 0.5;
  ctx.drawImage(canvas, 0, 0,canvas.width/ 2,canvas.height,0, 0,canvas.width/ 2,canvas.height);
  // draw again to pukl grays down some 
  ctx.globalAlpha = 1;
  ctx.globalCompositeOperation = "color-burn";
  ctx.drawImage(canvas, 0, 0,canvas.width/ 2,canvas.height,0, 0,canvas.width/ 2,canvas.height);
  
}
.big {
  width:100%;
}
&lt;canvas class="big" id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

  • 感谢这个非常有用的概述。我有一个快速澄清的问题:画布分辨率是否可能为 1000 像素 x 1000 像素,而画布显示尺寸仅为 10 像素 x 10 像素?如果是这样,在这种情况下画布像素代表什么?我认为像素有某种直接映射到底层硬件,但我对这个主题知之甚少......
  • 如果您有一个显示尺寸较小的高分辨率画布,硬件将显示显示像素中包含的所有像素的近似平均值。很多信息会被忽略。现代显示硬件通过 GPU 渲染所有像素。实际上,您的显示器使用的代码与 3D 游戏使用的代码相同,只是没有深度坐标。 @duhaime 每个图像、面板等都被渲染到纹理缓冲区,这些缓冲区又被组合成更大的缓冲区,只有在每个显示周期(~60Fps)的最后,最终渲染才会将像素移动到 1 比 1 的缓冲区与显示像素匹配。
【解决方案2】:

我不确定这是否是您所需要的,但我对顶部感到惊讶:-1000%。例如,在这种情况下,如果容器给出“位置:相对”,则具有 % 的子项的属性将相对于父项。

var img = new Image();
img.src = 'https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg';

img.onload = function() { 
  var canvas = document.querySelector('canvas');
  canvas.width = hidden.clientWidth;
  canvas.height = hidden.clientHeight;
  var ctx = canvas.getContext('2d');
  ctx.webkitImageSmoothingEnabled = false;
  ctx.mozImageSmoothingEnabled = false;
  ctx.imageSmoothingEnabled = false;
  ctx.drawImage(hidden,
    0, 0, this.naturalWidth, this.naturalHeight,
    0, 0, hidden.clientWidth, hidden.clientHeight)

}
 

#container {
  width: 800px;
  background: #efefef;
  position:relative;
}

#hidden {
  position: absolute;
  top: 0;
  right: 0;
}
#hidden, canvas {
  width: 50%;
}
<div id='container'>
  <img src='https://gist.githubusercontent.com/duhaime/019f2ccb98391adbf460a10463059683/raw/c4c2bc1fa923d905aba0ef88e05c0760823d2cca/000630070000650.jpg' id='hidden'>
  <canvas></canvas>
</div>

【讨论】:

  • top: -1000% 的目的是隐藏图像本身。如您所见,图像本身并不像画布那么模糊(在您的要点中,画布在左侧,img 在右侧)。我的目标是隐藏 img 并保持画布清晰。
猜你喜欢
  • 2013-09-04
  • 2013-05-29
  • 1970-01-01
  • 2012-05-21
  • 2018-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多