响应式 Canvas 图表需动态重设 width/height 属性适配 DPR,用 ctx.scale(dpr, dpr) 校正坐标系,并通过 ResizeObserver 监听容器尺寸变化,结合离屏缓存与局部重绘优化性能。

Canvas 绘图本身不自动响应尺寸变化,所谓“响应式图表”不是靠 CSS 拉伸实现的,而是要同步更新画布的逻辑分辨率、缩放绘图坐标系,并适配设备像素比(DPR),再配合容器监听机制完成的。
画布尺寸必须用 JS 动态重设 width/height 属性
Canvas 的 width 和 height 属性决定其内部绘图缓冲区的像素数量,这是实际渲染精度的基础。仅用 CSS 设置宽高(如 style.width = "100%")只会拉伸图像,导致模糊或变形。
- 每次尺寸变化时,需重新赋值 canvas.width 和 canvas.height,数值应为容器实际像素 × devicePixelRatio
- 同时用 CSS 将 canvas.style.width / height 设为对应 CSS 像素值(例如 container.clientWidth + "px"),使其视觉上填满父容器
- 避免在 HTML 中硬编码 width/height 属性,否则初始化后会被 JS 覆盖,但初始值可能影响首次渲染
用 ctx.scale() 对齐高清屏坐标系
设置高 DPI 分辨率后,如果不做坐标校正,所有绘图命令(如 fillRect(10,10,20,20))会按物理像素执行,图形会变小且位置偏移。必须用 ctx.scale(dpr, dpr) 把绘图单位“还原”为 CSS 像素逻辑单位。
- scale 后,原来写 x=10 就真代表左起第 10 个 CSS 像素位置,开发者无需手动乘 DPR
- scale 应在每次重设尺寸后立即调用,且必须在清空画布和重绘前执行
- 若后续需叠加未缩放内容(如文字测量、鼠标坐标转换),记得用 ctx.getTransform() 或手动逆向换算
监听容器而非窗口尺寸变化
现代布局(Flex/Grid/resize handle)常使 canvas 父容器尺寸变化而 window 不触发 resize 事件。依赖 window.onresize 容易漏掉关键更新。
立即学习“前端免费学习笔记(深入)”;
- 首选 ResizeObserver:监听 canvas.parentElement,回调中获取 getBoundingClientRect().width/height
- 兼容性降级方案:setInterval 检查 container.clientWidth 是否变化,间隔建议 150ms 左右
- 监听到变化后,先保存当前绘图状态(ctx.save()),再重设尺寸、scale、清空、重绘;避免直接 ctx.clearRect(0,0,canvas.width,canvas.height) 后未重设 scale 导致后续绘制错位
重绘策略要兼顾性能与一致性
响应式重绘不是简单地“清空+重画全部”,尤其在动画或高频更新场景下,全量重绘易卡顿。
- 静态部分(坐标轴、网格线、标签)可缓存到离屏 canvas,复用绘制,减少主画布计算压力
- 动态部分(数据点、移动轨迹)只局部重绘,用 ctx.clearRect(x,y,w,h) 清除旧区域再绘制新位置
- 使用 requestAnimationFrame 控制重绘节奏,避免连续触发;若数据更新频率高于帧率,可合并多次变更再统一重绘
- 重绘前务必 resetTransform() 或 restore(),防止 scale 累积或 transform 状态残留影响后续绘制



















