css 实现1个像素在不同分辨率屏幕上画网格线

发布于:2025-06-22 ⋅ 阅读:(20) ⋅ 点赞:(0)

实现网格线绘制,要考虑画布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>


网站公告

今日签到

点亮在社区的每一天
去签到