canvas高清显示的关键是让width/height属性匹配设备物理像素,而非仅调大css尺寸;需用devicepixelratio动态设置缓冲区,并通过ctx.scale()补偿缩放以保持逻辑坐标一致。

canvas 要高清显示,关键不是“调大 CSS 尺寸”,而是让 width 和 height 属性匹配设备物理像素 —— 否则再 sharp 的线条也会被浏览器插值拉糊。
canvas.width / height 决定绘图缓冲区真实像素数
这两个属性不是样式,是内存分配指令。写成 <canvas width="800" height="600"></canvas>,浏览器就真给你一块 800×600 的像素格子;所有 ctx.lineTo()、ctx.font、ctx.getImageData() 都按这个网格算。不设?默认 300×150,哪怕你用 CSS 撑到全屏,也只是把 300×150 的位图硬拉伸,边缘发虚、圆变椭、点击坐标偏移几十像素都是必然。
-
canvas.width = 800会清空画布并重置上下文(包括ctx.scale()、裁剪路径、当前字体),这是预期行为,不是 bug - 值必须是整数:写成
"800px"或800.5会被忽略,回退到 300×150 - 导出 PNG 或截图尺寸,完全取决于
width/height,和 CSS 无关
仅用 CSS 设置 canvas 尺寸必然模糊
canvas.style.width = "800px" 只是缩放 DOM 元素的显示框,不改内部缓冲区。相当于把一张小照片塞进大相框——浏览器只能双线性插值拉伸,锯齿、毛边、文字发虚全来了。高 DPI 设备(如 MacBook、iPhone)上更致命:window.devicePixelRatio === 2 时,CSS 宽高 400×300 的画布若没提升 width/height,实际只有 200×150 物理像素,却要填满 400×300 显示空间,模糊无法避免。
- 常见错误现象:
ctx.lineWidth = 1画出来像 2 像素宽、isPointInPath()判断失效、鼠标clientX/clientY映射到画布坐标系错位 - 哪怕 JS 初始化对了,后续某处 CSS(比如全局
canvas { width: 100% })覆盖了显示尺寸,而没同步更新width/height,失真就静默存在
响应式高清适配的三步操作
核心是:用 CSS 控制“看起来多大”,用 width/height 控制“画得多精细”,再靠 ctx.scale() 把逻辑坐标映射回来。标准流程如下:
- 先读取当前显示尺寸:
const w = canvas.clientWidth;、const h = canvas.clientHeight; - 按设备像素比设置缓冲区:
canvas.width = w * window.devicePixelRatio;、canvas.height = h * window.devicePixelRatio; - 用 CSS 锁定显示尺寸:
canvas.style.width = w + "px";、canvas.style.height = h + "px"; - 最后补偿缩放:
const ctx = canvas.getContext("2d"); ctx.scale(window.devicePixelRatio, window.devicePixelRatio);(否则你画的 100×100 矩形会变成 200×200)
注意:backingStorePixelRatio 可能影响精度,更稳妥的 DPR 计算是 (window.devicePixelRatio / (ctx.backingStorePixelRatio || 1))。
容易被忽略的初始化时机与覆盖风险
脚本必须在 DOMContentLoaded 后执行,或放在
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











