逻辑像素与物理像素——canvas缩放后绘图区域的长宽究竟是多少

bug描述

最近在基于 canvas写一个页面,涉及在画布中绘制网格。为了适配高分辨率的屏幕,给画布做了缩放,用缩放后的canvas长宽去计算网格的行列数。
以下是代码

 	// 获取设备像素比
     const devicePixelRatio = window.devicePixelRatio || 1;
     // 获取CSS宽高
     const styleWidth = backgroundCanvas.clientWidth;
     const styleHeight = backgroundCanvas.clientHeight;

     // 设置画布的实际绘图分辨率
     backgroundCanvas.width = styleWidth * devicePixelRatio;
     backgroundCanvas.height = styleHeight * devicePixelRatio;
     // 缩放绘图上下文,适配设备像素比
     backCtx.scale(devicePixelRatio, devicePixelRatio);
  	//计算网格行列
     cols = Math.floor(styleWidth / gridSize);
     rows = Math.floor(styleHeight / gridSize);;

结果很奇怪啊,计算得 cols = 27,但屏幕只显示了 21 列。


问题分析

  • Canvas 的逻辑宽高 vs. 物理分辨率

    • canvas.widthcanvas.height 定义了画布的实际绘图分辨率,单位是 物理像素
    • CSS 设置的 widthheight 是画布的 逻辑显示尺寸,单位是 逻辑像素
    • 当设置 canvas.width = styleWidth * devicePixelRatio 时,画布的绘图区域被放大,但 CSS 控制的逻辑显示尺寸未改变。
  • 导致问题的核心原因

    1. 网格行列数基于物理分辨率计算:
      const cols = Math.floor(backgroundCanvas.width / gridSize);
      
      由于 backgroundCanvas.width 是物理像素,计算的网格行列数和逻辑尺寸中实际能够显示的会不同。

解决方案

解决该问题的关键在于 在逻辑尺寸范围内计算网格

网格绘制需要基于逻辑宽高进行坐标计算:

 // 获取CSS宽高
const styleWidth = backgroundCanvas.clientWidth;
const styleHeight = backgroundCanvas.clientHeight;
 ....
cols = Math.floor(styleWidth / gridSize);
rows = Math.floor(styleHeight / gridSize);

完整代码示例

以下是修复后整理的完整代码:

const devicePixelRatio = window.devicePixelRatio || 1;
const gridSize = 20;

const backgroundCanvas = document.getElementById('background');
const styleWidth = backgroundCanvas.clientWidth; // CSS 逻辑宽度
const styleHeight = backgroundCanvas.clientHeight; // CSS 逻辑高度

// 设置物理分辨率
backgroundCanvas.width = styleWidth * devicePixelRatio;
backgroundCanvas.height = styleHeight * devicePixelRatio;

// 确保逻辑显示尺寸不变
backgroundCanvas.style.width = `${styleWidth}px`;
backgroundCanvas.style.height = `${styleHeight}px`;

// 获取上下文并缩放
const ctx = backgroundCanvas.getContext('2d');
ctx.scale(devicePixelRatio, devicePixelRatio);

// 计算逻辑行列数
const cols = Math.floor(styleWidth / gridSize);
const rows = Math.floor(styleHeight / gridSize);

// 绘制网格
function drawGrid() {
    ctx.clearRect(0, 0, backgroundCanvas.width, backgroundCanvas.height);
    ctx.strokeStyle = "#ccc";

    for (let x = 0; x <= cols; x++) {
        ctx.beginPath();
        ctx.moveTo(x * gridSize, 0);
        ctx.lineTo(x * gridSize, styleHeight);
        ctx.stroke();
    }

    for (let y = 0; y <= rows; y++) {
        ctx.beginPath();
        ctx.moveTo(0, y * gridSize);
        ctx.lineTo(styleWidth, y * gridSize);
        ctx.stroke();
    }
}

drawGrid();

总结

  • Bug 的本质:物理分辨率(canvas.widthcanvas.height)与逻辑显示尺寸(CSS 控制的宽高)不匹配,导致网格计算和绘制错位。
  • 解决方案:缩放后,使用canvas的尺寸计算应该基于逻辑尺寸
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值