跳到主要内容

绘制

2024年06月19日
柏拉文
越努力,越幸运

一、认识


注意: canvas 绘图时, 会从两个物理像素的中间位置开始绘制并向两边扩散 0.5 个物理像素。当设备像素比为 1 时,一个 1px 的线条实际上占据了两个物理像素(每个像素实际上只占一半),由于不存在 0.5 个像素,所以这两个像素本来不应该被绘制的部分也被绘制了,于是 1 物理像素的线条变成了 2 物理像素,视觉上就造成了模糊

解决: 设置 Canvas 的缩放比例,确保在高密度屏幕上有更好的显示效果

二、关闭抗锯齿


context.mozImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;

三、 获取 Canvas 缩放比例


const getPixelRatio = (context)=>{
const backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
}
  • devicePixelRatio: 用于表示设备上物理像素与 CSS 像素之间比率的属性。它告诉你在渲染页面时,一个 CSS 像素对应多少个物理像素。例如,如果 devicePixelRatio 的值为 2,表示每个 CSS 像素对应设备上的 2 个物理像素,这通常出现在高密度屏幕(如Retina屏幕)上。

  • backingStorePixelRatio: 用于表示绘图表面(如Canvas)的缓冲区像素比率。这个属性通常用于在高分辨率设备上绘制图形时,确保图形质量和性能的平衡。在 Retina 屏幕上,使用这个属性可以绘制高清晰度的图形,而不会降低性能。

四、基于 Canvas 缩放比例, 绘制图像


const draw = config => {
const { canvas, context, imgEl, width, height, ratio } = config;
canvas.width = width * ratio;
canvas.height = height * ratio;
context.drawImage(imgEl, 0, 0);
};

五、完整代码


index.html
<div class="input-container">
<img id="input-img" src="" />
<input type="file" id="input-file" accept="image/*" />
</div>
<div class="output-container">
<canvas id="output-canvas"></canvas>
</div>

<script src="./index.js"></script>
index.js
const getPixelRatio = context => {
const backingStore =
context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio ||
1;
return (window.devicePixelRatio || 1) / backingStore;
};

const draw = config => {
const { canvas, context, imgEl, width, height, ratio } = config;
context.mozImageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.msImageSmoothingEnabled = false;
context.imageSmoothingEnabled = false;
canvas.width = width * ratio;
canvas.height = height * ratio;
context.drawImage(imgEl, 0, 0);
};

const inputImgEl = document.querySelector('#input-img');
const inputFileEl = document.querySelector('#input-file');
const outputCanvasEl = document.querySelector('#output-canvas');
const outputCanvasCtx = outputCanvasEl.getContext('2d');
const ratio = getPixelRatio(outputCanvasCtx);

inputFileEl.addEventListener('change', e => {
const file = e.target.files[0];
inputImgEl.src = URL.createObjectURL(file);
});

inputImgEl.addEventListener('load', () => {
const ratio = getPixelRatio(outputCanvasCtx);
draw({
ratio,
imgEl: inputImgEl,
canvas: outputCanvasEl,
context: outputCanvasCtx,
width: inputImgEl.naturalWidth,
height: inputImgEl.naturalHeight
});
});