Better

Ethan的博客,欢迎访问交流

primitive canvas api

之前需要写一个在地图上进行图形绘制的组件,考虑到需求复杂度问题,不想导入 fabric 等 canvas 库,想直接使用 canvas 原生 api 进行编写,结果发现原生 api 比我想像中的要不易用一些,最后写下来感觉像是写了一个小型库,特意对比了一下 fabric 在这一块实现的源码,竟然有些相似,哈哈!这里做下简单的笔记,以及一些注意事项

context core api

CanvasRenderingContext2D 原生 API

  • 描边类绘制
    • strokeStyle
    • stroke(path2D?):描边当前子路径或指定 path2D
    • strokeXXX
  • 填充类绘制
    • fillStyle
    • fill(path2D?):填充当前子路径或指定 path2D
    • fillXXX
  • 路径
    • beginPath:清空子路径列表开始一个新的路径
    • moveTo
    • lineTo/rect/arc/arcTo/……
    • closePath:尝试闭合图形,如果已经封闭或者只有一个点,则不作任何操作
  • 线型
    • lineWidth
    • lineCap
    • lineJoin
    • setLineDash
  • 文本样式
    • font
    • textAlign
    • textBaseline
    • direction
  • 阴影
    • shadowBlur
    • shadowColor
    • shadowOffsetX
    • shadowOffsetY
  • 合成
    • globalAlpha
    • globalCompositeOperation
  • 像素控制
    • createImageData
    • getImageData
    • putImageData
  • 状态:线的样式、填充样式、阴影样式、文本样式
    • save
    • restore
  • 变换
    • rotate
    • scale
    • translate
    • transform 叠加变换
    • setTransform 设置变换矩阵
    • 特别注意:多次调用 rotate、scale、translate 是对当前状态的叠加效果
  • 其他
    • clearRect
    • clip:用于将当前正在构建的路径转换为当前的裁剪路径。默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果)。
    • toDataURL
    • toBlob

画布裁剪

画布清除

使用 clearRect 清空画布的场景

  • 没有使用 transform 的 context
  • 如果使用了 transform 的 context,则需要跟踪实际边界

或者使用如下方式

// Store the current transformation matrix
context.save();

// Use the identity matrix while clearing the canvas
context.setTransform(1, 0, 0, 1, 0, 0);
context.clearRect(0, 0, canvas.width, canvas.height);

// Restore the transform
context.restore();

重设 canvas 的 width、height 会重置 canvas 的状态,比如 transformations、lineWidth、strokeStyle 等。注意:不要使用这种方式去清空画布,因为相比 clearRect 而言,非常低效

性能优化

mdm 上有对 canvas 常见性能优化进行整理,这里也简单罗列一下

  • 在离屏 canvas 上预渲染相似的图形或重复的对象
  • 避免浮点数的坐标点,用整数取而代之
  • 不要在用 drawImage 时缩放图像
  • 使用多层画布去画一个复杂的场景:动静分离
  • 用 CSS 设置大的背景图
  • 用 CSS transforms 特性缩放画布:CSS transforms 使用 GPU,因此速度更快
  • 关闭透明度
  • 将画布的函数调用集合到一起(例如,画一条折线,而不要画多条分开的直线)
  • 渲染画布中的不同点,而非整个新状态
  • 尽可能避免 shadowBlur 特性
  • 尽可能避免 text rendering
  • 尝试不同的方法来清除画布(clearRect() vs. fillRect() vs. 调整canvas大小)
  • 有动画,请使用 window.requestAnimationFrame() 而非 window.setInterval()

注意事项

其他值得注意的点

  • 每次调用 getContext('2d') 获取 context,会是一个全新的 context
  • 当 canvas 容器 display 为 none 时,render 通常会导致异常


留言