我正在尝试使用画布将 1x 图像缩放到任意大小。然而,图像的每个像素的边缘总是有一些插值。我希望提供的图像与画布的外观完全匹配。不同尺度等的组合对我来说不起作用。即使右键单击 -> 在画布上保存图像也会产生相同的图像。
我也尝试在画布上使用
toDataURL
,但得到了相同的结果。
我在配备 2 倍显示屏的 macOS 上看到了这一点。在 Safari、Chrome 和 Firefox 中结果相同。
2并排:
生成的图像:
// This function is unimportant; it is used as an example for this question.
function generateImage(width, height, digitOffset) {
const pixelsCount = width * height
const imageData = new ImageData(width, height)
const digits = "0123456789"
.repeat(30)
.substring(digitOffset, digitOffset + pixelsCount)
for (const [index, digit] of [...digits].entries()) {
const colours = {
0: [0x00, 0x12, 0x19],
1: [0x00, 0x5f, 0x73],
2: [0x0a, 0x93, 0x96],
3: [0x94, 0xd2, 0xbd],
4: [0xe9, 0xd8, 0xa6],
5: [0xee, 0x9b, 0x00],
6: [0xca, 0x67, 0x02],
7: [0xbb, 0x3e, 0x03],
8: [0xae, 0x20, 0x12],
9: [0x9b, 0x22, 0x26],
}
const dataOffset = index * 4
const color = colours[digit]
imageData.data[dataOffset] = color[0]
imageData.data[dataOffset + 1] = color[1]
imageData.data[dataOffset + 2] = color[2]
imageData.data[dataOffset + 3] = 0xff
}
return imageData
}
/** The width/height of the 1x image. */
const size = 16
const imageData = generateImage(size, size, 0)
const scale = 10
/** The canvas containing the 1x image. */
const unscaledCanvas = document.getElementById("unscaledCanvas")
unscaledCanvas.width = size
unscaledCanvas.height = size
unscaledCanvas.style.width = `${size}px`
unscaledCanvas.style.height = `${size}px`
const unscaledContext = unscaledCanvas.getContext("2d")
unscaledContext.putImageData(imageData, 0, 0)
const scaledCanvas = document.getElementById("increasedScale")
scaledCanvas.width = size * scale
scaledCanvas.height = size * scale
scaledCanvas.style.width = `${size * scale}px`
scaledCanvas.style.height = `${size * scale}px`
scaledCanvas.style.imageRendering = "pixelated"
const scaledContext = scaledCanvas.getContext("2d")
scaledContext.imageSmoothingEnabled = false
scaledContext.mozImageSmoothingEnabled = false
scaledContext.webkitImageSmoothingEnabled = false
scaledContext.drawImage(unscaledCanvas, 0, 0, size * scale, size * scale)
const image = document.getElementById("output")
image.width = size * scale
image.height = size * scale
scaledCanvas.toBlob((blob) => {
const dataURL = URL.createObjectURL(blob)
image.onload = () => {
URL.revokeObjectURL(dataURL)
}
image.src = dataURL
})
<canvas id="unscaledCanvas"></canvas>
<canvas id="increasedScale"></canvas>
<img id="output" />
图像中没有您看到的编码的抗锯齿伪像,您的浏览器在放大图像以匹配 2 倍显示器的像素密度时会创建这些伪像。
我们可以通过在另一个
<canvas>
上再次放大相同的图像来证明这一点:
(async () => {
// using OP's produced image
const resp = await fetch("https://i.sstatic.net/7oZk5oeK.png");
const blob = await resp.blob();
const bmp = await createImageBitmap(blob);
const canvas = document.querySelector("canvas");
// Make the canvas the correct density
canvas.width *= devicePixelRatio;
canvas.height *= devicePixelRatio;
const ctx = canvas.getContext("2d");
ctx.scale(devicePixelRatio, devicePixelRatio);
// no smoothing
ctx.imageSmoothingEnabled = false;
// upscale
ctx.scale(10, 10);
ctx.drawImage(bmp, -100, -100);
})().catch(console.error);
canvas { width: 300px; height: 150px; }
<canvas></canvas>
请注意,对于此画布,我根据当前 dPR 调整
<canvas>
的 width
和 height
的大小,同时通过 CSS 缩小其尺寸,从而避免了调整大小问题。
要在
<img>
中正确渲染图像,您可以准备具有不同密度的多个版本,然后使用 srcset
的
<img>
属性:
<img
width=160 height=160
srcset="
https://i.sstatic.net/7oZk5oeK.png,
https://i.sstatic.net/OlFcowA1.png 2x,
https://i.sstatic.net/QsWfkvSn.png 3x,
">
或者您可以使用您在
image-rendering
上使用的相同 <canvas>
CSS 规则:
img { image-rendering: pixelated }
<img src="https://i.sstatic.net/7oZk5oeK.png">