HTML5 - Canvas,drawImage()绘制的图像模糊

39
我正在尝试将以下图像绘制到画布上,但尽管定义了画布的大小,它仍然模糊不清。如下所示,图像清晰明了,而在画布上,它却模糊且呈像素化状态。

enter image description here

这是它的外观(左边是原始的,右边是画在画布上并且模糊的)。

enter image description here

我做错了什么?

console.log('Hello world')

var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()

// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'
playerImg.width = 32
playerImg.height = 32

playerImg.onload = function() {
  ctx.drawImage(playerImg, 0, 0, 32, 32);
};
#canvas {
  background: #ABABAB;
  position: relative;
  height: 352px;
  width: 512px;
  z-index: 1;
}
<canvas id="canvas" height="352" width="521"></canvas>

6个回答

49
这是由于反锯齿功能导致的。只需将 imageSmoothingEnabled 设置为 false 即可。
context.imageSmoothingEnabled = false;

这是一个 jsFiddle 版本

jsFiddle : https://jsfiddle.net/mt8sk9cb/

var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()

// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'

playerImg.onload = function() {
  ctx.imageSmoothingEnabled = false;
  ctx.drawImage(playerImg, 0, 0, 256, 256);
};

2
你应该添加供应商前缀,因为这不是一个稳定的属性。 - Kaiido
@Canvas 很有意思。但是,尽管将图像从256x256设置为32x32,仍然会出现像素化的问题:\ ... 我当时使用的是Firefox浏览器.. - Zeng Cheng
@Canvas 在32x32大小下仍然看起来模糊...这是它的原始大小。 - Zeng Cheng
@DanF,就像我在上面的评论中所说的那样,你需要使用供应商前缀。请检查此更新的代码片段:https://jsfiddle.net/mt8sk9cb/2/ - Kaiido
@Kaiido 我知道,但我不想要256x256的图像,而是它原始的32x32大小,当我将其设置为32x32时...尽管原始图像没有模糊,但它仍然很模糊。 - Zeng Cheng

23

你的问题在于css限制了canvas{width:512},而画布属性为width=521,这将使浏览器重新采样整个画布。

要避免这种情况,移除那些css声明即可。

var c = document.getElementById('canvas')
var ctx = c.getContext('2d')
var playerImg = new Image()

// http://i.imgur.com/ruZv0dl.png sees a CLEAR, CRISP image
playerImg.src = 'http://i.imgur.com/ruZv0dl.png'
playerImg.width = 32
playerImg.height = 32

playerImg.onload = function() {
  ctx.drawImage(playerImg, 0, 0, 32, 32);
};
#canvas {
  background: #ABABAB;
  position: relative;
  z-index: 1;
}
<canvas id="canvas" height="352" width="521"></canvas>

另外,如果您需要将图像重新采样(从32x32调整为其他大小),@canvas的解决方案是可行的。


谢谢,那也很有帮助。 - Zeng Cheng
2
@DanF,你可以自行处理已接受的标记,但由于canvas'one没有解决你的问题,而这个问题是由CSS声明引起的,我认为对于未来的读者来说,接受这个答案并给canvas'one一个赏金会更好。 - Kaiido
2
+100,因为我在加上 ctx.imageSmoothingEnabled = false; 后来到这里,但图像仍然模糊。 - bklaric
这应该是被接受的答案,我遇到了同样的问题,我的图片不是像素艺术,而且也很大,仍然得到了相同的错误,直到我将style="width:x ; height:y"改为height="x" height="y"。 - Alejandro Cumpa

22

在遇到我的一些问题时,我找到了这篇较旧的帖子,这里有更多关于模糊图像的信息可以添加到“imageSmoothingEnabled”解决方案上面。

这更具体地针对监视器特定渲染的用例,只有一些人会遇到这个问题,如果他们一直试图将视网膜质量图形呈现到画布中并且结果令人失望。

基本上,高密度显示器意味着您的画布需要容纳额外的像素密度。如果您什么都不做,您的画布将仅将足够的像素信息呈现到其上下文中,以满足像素比为1的要求。

因此,对于许多具有比率> 1的现代监视器,您应该更改画布上下文以适应该额外信息,但保持画布的正常宽度和高度。

要做到这一点,您只需将呈现上下文的宽度和高度设置为:目标宽度和高度* window.devicePixelRatio。

canvas.width = target width * window.devicePixelRatio;
canvas.height = target height * window.devicePixelRatio;

然后您可以设置画布的样式,以使画布在正常维度中调整大小:

canvas.style.width = `${target width}px`;
canvas.style.height = `${target height}px`;

最后,您会将图像渲染为图像允许的最大上下文大小。在某些情况下(如呈现svg图像),通过以pixelRatio大小的尺寸渲染图像,您仍然可以获得更好的图像质量:

ctx.drawImage(
    img, 0, 0, 
    img.width * window.devicePixelRatio, 
    img.height * window.devicePixelRatio
  );

所以为了展示这个现象,我创建了一个代码片段。如果您在接近1像素比例的显示器上,则不会看到画布质量上的差异。

https://jsfiddle.net/ufjm50p9/2/


1
你让我的一天变得美好!感谢你发布这篇精彩的文章! - Théo Lavaux
1
你也帮了我大忙!我被这个问题卡了两个小时,而你刚好解决了它!谢谢! - Mitch

11
除了@canvas的回答之外,还有以下内容。
context.imageSmoothingEnabled = false;

功能完美。但在我的情况下,改变画布的大小会将此属性重置为true。

window.addEventListener('resize', function(e){
    context.imageSmoothingEnabled = false;
}, false)

1
以下代码适用于我:

(原文)

img.onload = function () {
  canvas.width = img.width;
  canvas.height = img.height;
  context.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
};
img.src = e.target.result;  // your src


-3
简单技巧:在x和y位置上用0.5进行绘制,比如drawImage(, 0.5, 0.5),这样可以获得清晰的边缘。:D

1
这更适合作为评论而不是答案。如果您想发布答案,请写详细的解释。 - Alexey Kamenskiy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接