实现网格线绘制,要考虑画布style尺寸和画布像素大小的缩放关系
单像素绘制主要出现的问题是会模糊,从像素角度看就是出现绘制两个像素,实际就是要做偏移
核心就是:按物理像素绘制,首先要对齐物理像素,计算物理像素与css像素之间的关系,然后绘制的时候如果绘制的线宽是奇数就要考虑做偏移,不缩放的情况下是0.5个css像素,缩放就需要计算比例,核心代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
</head>
<body>
<canvas id="canvas"> </canvas>
<script>
// 设置 Canvas 的物理像素缩放,确保在高分屏下清晰绘制
const canvas = document.getElementById("canvas");
const context = canvas.getContext("2d");
const ratio = window.devicePixelRatio || 1;
// 设置画布css大小
const width = 300;
const height = 200;
// 设置 CSS 尺寸, 这里设置的css大小,在绘制的时候,会根据ratio进行缩放,必须保证比例精确,否则绘制出来的线会模糊
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
// 设置高分辨率画布尺寸
canvas.width = width * ratio;
canvas.height = height * ratio;
// 缩放上下文,保持绘图逻辑不变
context.setTransform(ratio, 0, 0, ratio, 0, 0);
const deviceLineWidth = 1
const devicePxWidth = 1 / ratio
const gridLineWidth = 1 / ratio
const offset = 0.5 // 偏移量,用于对齐像素
// 现在 context 中坐标单位是 CSS 像素
// 对齐设备像素,当浏览器或系统设置非整数分辨率时,单个像素也不是整数,不能简单的取整
function alignDevicePixel(px) {
return Math.round(px / devicePxWidth) * devicePxWidth;
}
// 绘制一条真正 1px 的线(在物理像素中)
function draw1pxGridLine() {
context.lineWidth = deviceLineWidth / ratio;
context.strokeStyle = 'black';
const halfLineWidth = deviceLineWidth / 2;
const _needTranslateOffset = Math.abs(Math.round(halfLineWidth) - halfLineWidth) > 1e-4; // 这里在算是奇数还是偶数
const dprOffset = _needTranslateOffset ? offset / ratio : 0
// 对齐像素,+0.5 使线落在像素中心
for (let x = 0; x <= width; x += 30) {
context.beginPath();
x = alignDevicePixel(x)
x -= gridLineWidth
context.moveTo(x + dprOffset, 0);
context.lineTo(x + dprOffset, height);
context.stroke();
}
for (let y = 0; y <= height; y += 20) {
context.beginPath();
y = alignDevicePixel(y)
y -= gridLineWidth
context.moveTo(0, y + dprOffset);
context.lineTo(width, y + dprOffset);
context.stroke();
}
}
draw1pxGridLine();
</script>
</body>
</html>