canvas功能拓展实践

V3f3GR.png

原生 Canvas 仅有一些最基本的 API,使用起来很不方便。就像简单的 DOM API 能封装成功能强大的 JQuery 一样,对原生的 canvas API 进行封装一样能封装成功能强大的库。

以下功能都已实现,详情请看我的开源库Bvas (opens new window)

# 实现图层功能

用过 PhotoShop 的童鞋都知道,在 PhotoShop 中,上面的图层会盖住下面的图层,当一个图层移除时,图层中的内容全部移除。在实际的 canvas 开发中我们往往也会有背景层和内容层。在 canvas 中则没有图层的概念,canvas 的 API 就像画笔,后画的永远盖在先画的上面。
我们可以先定义一个Layer类,并设置zIndex属性

class Layer{
    constructor(...) {
        //...
    }
}

渲染前先根据zIndexlayer排序

function sortLayerByZIndex(layerInstaceList) {
  //...
}

然后渲染layer

layerInstaceList.forEach((layerInstance) => layerInstance._render())

# 自定义图形事件

任何一帧 canvas 都是一个静态图片,它不像 DOM 和 SVG 每个形状是独立的。这里有两种方法判断元素是否被点击。

  1. 方法一:调用 canvas 原生 API,isPointInPath
    该方法的缺点是仅能对绘制的前一个图形进行判断,若画布上有大于等于两个图形则需要在每次点击时清除所有图形再对图形进行重绘,每绘制一个图形调用一次该 API,故每次画布被点击时应
function click() {
  let e = window.event
  let { offsetX, offsetY } = e
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  shapeList.forEach((shape) => {
    shape._render()
    if (ctx.isPointInPath(offsetX, offsetY)) {
      // callback function
    }
  })
}
  1. 方法二:数学计算判断是否点击在图形区域内
    该方法也是大多数 canvas 采用的方法。任何二维图形都可以抽象成三种基本图形,即点(Point)、线(Line)、面(PolyGon)。从数学公式的角度我们需要对不同的类型进行不同的判断。例如圆形,椭圆形,贝塞尔曲线组成的形状等等。用这种方法需准备一大堆数学公式函数。
function isPointInXXX(p, shape) {
  // ...
}

# 自定义图形鼠标形状

基本方法同自定义图形事件,需监听 canvas 鼠标移动事件,鼠标每次移动时进行判断。