谈谈canvas
- canvas相关场景
- canvas相关优化
Canvas 和 SVG 有什么区别
<canvas>标签本身是 DOM 元素,但是通过canvas绘制出来的内容不是DOM元素,而是像素
| 对比 | Canvas | SVG / DOM |
|---|---|---|
| 外层本身 | <canvas>是 DOM | <svg> 是 DOM |
| 内部图形 | 不是 DOM,是像素 | 是 DOM 节点 |
| 事件绑定 | 绑定在 canvas 上,自己做命中检测 | 每个图形可单独绑定 |
| 样式控制 | 通过 JS 重绘 | 可用 CSS/属性控制 |
| 适合 | 大量图形、游戏、视频帧、画板 | 图标、流程图、可交互图形 |
| 缩放 | 位图,可能模糊 | 矢量,缩放清晰 |
Canvas 为什么会模糊?怎么解决?
-
Canvas 会模糊,主要是因为 Canvas 绘制结果是位图。如果 canvas 的实际像素尺寸和 CSS 显示尺寸不一致,浏览器会缩放这张位图,导致模糊
-
解决方案:
-
通过设备像素比
DPR来适配 (devicePixelRatio) -
通过
window.devicePixelRatio获取屏幕像素比,将 Canvas 的宽度和高度放大相应倍数,并使用scale()方法适配
-
核心代码
function resizeCanvas(canvas, width, height) {
const dpr = window.devicePixelRatio || 1
const ctx = canvas.getContext('2d')
canvas.style.width = width + 'px'
canvas.style.height = height + 'px'
canvas.width = Math.round(width * dpr)
canvas.height = Math.round(height * dpr)
ctx.scale(dpr, dpr)
return ctx
}
Canvas 如何做动画
- 标准做法是用 requestAnimationFrame,不要用 setInterval。MDN 的 Canvas 优化建议里也明确提到:动画应使用 Window.requestAnimationFrame() 而不是 setInterval()。
- requestAnimationFrame 会和浏览器刷新频率同步,页面不可见时通常会降频或暂停,减少无效渲染
Canvas 怎么绑定点击事件
- Canvas 内部图形不是 DOM 元素,不能直接给某个圆或矩形绑定事件。通常做法是
- 给 canvas 本身绑定事件
- 获取鼠标相对 canvas 的坐标。
Canvas 如何导出图片
- 通过toDataURL方法
- 如果 canvas 绘制了跨域图片,并且图片没有正确设置 CORS,canvas 会被污染,调用 toDataURL / getImageData 会报安全错误
- 图片的 crossorigin 属性需要结合合适的 CORS 响应头,才能让跨源图片像同源图片一样安全地用于 Canvas
Canvas 如何绘制跨域图片并导出?
- 只是告诉浏览器,希望用 CORS 模式加载这张图片
- anonymous(匿名):告知浏览器以匿名方式获取资源
const img = new Image()
img.crossOrigin = 'anonymous'
img.src = url
- 服务器也必须允许跨域,比如图片服务器需要返回
Access-Control-Allow-Origin: *
Canvas在音视频业务应用
- canvas来绘制时间轴
- canvas处理视频画面帧
- canvas实现截图、封面生成
绘制时间轴
在视频剪辑、音频剪辑、录课回放、直播回放、字幕编辑器中,时间轴通常会用 Canvas 来绘制
- Canvas 适合时间轴的原因是:时间轴通常包含大量刻度、片段、辅助线,如果用 DOM 渲染大量节点,性能和布局成本会比较高;Canvas 可以把这些内容一次性绘制到位,来提高性能
视频截图 / 封面生成
- Canvas 可以把视频当前帧截出来,常用于生成视频封面。
核心方法:drawImage 、toDataURL
Canvas相关优化
Canvas 优化主要从减少绘制、减少计算、减少状态切换、减少主线程压力几个方向入手。动画使用 requestAnimationFrame;静态内容用离屏 canvas 缓存;复杂场景用多 canvas 分层;只重绘变化区域;大量图形按样式分组批量绘制;避免频繁 getImageData、putImageData、toDataURL、shadowBlur 和文本绘制;图片提前加载和预缩放
- 减少绘制次数
- 不要每次状态变化都立刻画,应该合并到一帧中
- 只重绘变化区域
- 尽量只渲染屏幕差异,而不是每次都渲染完整新状态
- 分层 Canvas
- 把一个复杂画布拆成多个 canvas 叠在一起
- 好处:某一层变化时,只重绘那一层。