封装canvas-2D使用库:基于前端图形处理的实践

942 阅读3分钟

canvas作为Html5标准的新特性,在前端诸如海报、动画、图片编辑、文档编辑、数据可视化等落地场景都有用武之地;在学习canvas的过程中,通过从零实现一个自己使用的调用库,基于canvas的整体视角,感受它的神通广大与魅力;借此,将canvas的学习经验分享给大家。

canvas标准能力

这里我们所说的canvas,指的是canvas-2D渲染上下文,主要聚焦于2D图形处理:从canvas2D标准来看,它支持:

  • 文本处理: 支持自定义字体,文本方向,对齐方式,字体间距以及文本填充;
  • 图像处理: 支持将图片资源,绘制到canvas画布上,支持将图片资源生成图案,用于复用;支持从canvas画布上截取图形像素数据;
  • 绘制图形、线段路径:支持矩形、圆角矩形、圆的绘制,支持线段(直线、曲线)的绘制与填充(填充为图形),同时基于Path2D对象保存路径信息,方便后续基于同样的路径绘制相同的图形或线段;
  • 填充渲染: 支持线段绘制与图形填充;
  • 特殊效果: 如阴影、模糊、焦点、曲线绘制、变化、平移、旋转、缩放等;
  • 状态保存: 支持保存当前设置的canvas绘制状态,绘制状态包括已设置的canvas绘制属性或一些渲染样式

为了方便调用canvas,需要将不同模块的功能拆分为不同的调用功能函数; 首先需要我们先对canvas的所有可执行绘制API有一个基本的认识和了解,在这个基础上,抽象出我们封装的上层函数,如定义文本的绘制函数useText,定义图形的绘制函数-useGraphics;所有的功能调用函数通过传入配置参数,调用渲染上下文执行绘制;同时,还需要支持用户自定义直接操作canvas2D渲染上下文的能力,因此需要定义一个内部变量,支持用户侧获取指向2D上下文的该变量,用于自定义操作。

image.png

path路径的封装调用

由于path的可复用性,当我们处理2D路径绘制时,通过path2D构建出可复用的路径并返回给上层函数,由调用方决定是否需要保存;同时,对于路径的绘制点位,我们以数组的形式接收一个用于描述path的点位坐标信息,同时,需要接收绘制的类型参数,比如是直线还是曲线,做一个条件分支处理。

export function paintStraightLine(conf: PaintStraConf) {
    const { path, initX, initY, fillColor } = conf
    this.fillStyle = fillColor || 'black'
    // this.strokeStyle = fillColor || 'black'
    // 使用直线
    this.beginPath()
    this.moveTo(initX, initY)
    for (let i = 0; i < path.length; i++) {
        const { x, y } = path[i]
        this.lineTo(x, y)
    }
    this.fill()
    // this.stroke()
    // this.closePath() // stroke需要手动结束path
}

通过将canvas-2D的相关API完整的抽象成上层调用函数,会更加方便我们对于底层绘制的理解,当我们使用其他canvas库的时候,也更有一个全局的使用思路。

扩展:canvas工作原理

以chromium内核为例,web环境下的canvas的渲染是基于skia的2D矢量图形渲染引擎实现:

  1. 通过Canvas.getContext('2d')创建2D上下文;
  2. 通过该2D上下文调用Canvas Binding API(即通过jsBridge实现的调用系统底层的api的能力),即调用skia图形渲染引擎的绘图api,并记录绘图命令,此时没有执行绘制操作;
  3. 系统收到图像刷新信号,会调度到JS线程通知到Canvas,canvas收到信号后停止记录绘制,先生成用于渲染的图层,并添加到浏览器的layerTree上,同时发送给GPU线程;
  4. GPU执行绘制渲染,并刷新到显示器的帧缓冲区

封装的canvas库的地址:easy-canvas/github地址