canvas功能拓展实践

日期:2018-07-05 17:19作者:Bougie

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

以下功能都已实现,详情请看我的开源库Bvas

实现图层功能

用过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
         }
     })
    }
    
  2. 方法二:数学计算判断是否点击在图形区域内
    该方法也是大多数canvas采用的方法。任何二维图形都可以抽象成三种基本图形,即点(Point)、线(Line)、面(PolyGon)。从数学公式的角度我们需要对不同的类型进行不同的判断。例如圆形,椭圆形,贝塞尔曲线组成的形状等等。用这种方法需准备一大堆数学公式函数。
    function isPointInXXX(p, shape) {
     // ...
    } 
    

自定义图形鼠标形状

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