阅读 329

fabric

fabric是什么?

它是一个封装了canvas api的库,Canvas提供一个好的画布能力, 但是Api不够友好。绘制简单图形其实还可以, 不过做一些复杂的图形绘制,就没那么方便了,fabric可以解决这个问题,它在原生方法之上提供了一个强大的对象模型。

fabric的简单应用对比

简单应用对比:绘制一个简单的矩形

image.png

canvas的实现方式:
 drawRect () {
      const canvas = document.getElementById('canvas')
      const ctx = canvas.getContext('2d')
      ctx.fillStyle = 'red'
      // 创建一个坐标100,100,尺寸是20,20的矩形
      ctx.fillRect(100, 100, 20, 20)
    }
    
fabric的实现方式
 drawRect () {
      // 用原生canvas元素创建一个fabric实例
      const canvas = new fabric.Canvas('canvas')
      // 创建一个矩形对象
      const rect = new fabric.Rect({
        left: 100,
        top: 100,
        fill: 'red',
        width: 20,
        height: 20
      })
      // 将矩形添加到canvas画布上
      canvas.add(rect)
    }
复制代码

区别:

  1. 在原生的API中,我们是直接对canvas生成的context进行操作的
  2. 在Fabric中,我们操作对象,实例化它们,更改其属性,并将其添加到画布

更进一步:如果想要矩形增加一定弧度、并且可以被拖动

rect.gif

//fabric
  const canvas = new fabric.Canvas('canvas')
  const rect = new fabric.Rect({
    ...
    angle: 45,
    evented: true,
    hasControls: false, // 选中时没有四边操作位
  })
  canvas.add(rect)
  
//canvas
 const canvas = document.getElementById('canvas')
 const ctx = canvas.getContext('2d')
       ...
 ctx.translate(100, 100);
 ctx.rotate(Math.PI / 180 * 45); // 弧度的转化
 ctx.fillRect(-10, -10, 20, 20);
 canvas.addEventListener('mousemove', (e)=>{
    //移动的相关处理
  })
 
复制代码

区别:fabric的实现方式只需要通过对象属性的简单配置即可实现,原生api需要进行弧度转发、手动实现事件监听的一些处理逻辑,fabric提供了相对完整的事件系统。

fabric还封装了一些其它的基本图形类供直接调用

  • fabric.Circle //圆形
  • fabric.Ellipse //椭圆
  • fabric.Line //线
  • fabric.Polygon //多边形
  • fabric.Polyline //交叉线
  • fabric.Rect //矩形
  • fabric.Triangle//三角形

fabric原理

整体结构

f-content.png

渲染逻辑

f-rend.jpeg

创建画布主要做了下面几件事情:

 const canvas = new fabric.Canvas("canvasId", options);
复制代码
  1. 创建缓存canvas:在canvas官方文档中提到的canvas优化方面也包括了在离屏canvas上预渲染相似图形,fabric中的缓存也是一样的,可以通过设置objectCaching是否缓存,如果图形比较复杂的话,建议使用缓存的方法。下面是在复杂图形中两种方式的对比,可以明显感觉到在使用缓存时,fps值大一些。

下图是没有使用缓存的效果图,fps值要小一些,每秒的动画帧要小

no-cache.gif

使用缓存的效果图如下图,明显动画帧要大一些

cache (1).gif

使用缓存和不适用缓存的区别: 使用缓存:在渲染之前先用一个离屏 canvas 来做预渲染,在渲染的时候通过调用drawImage把离屏canvas画到真实画布中。不使用缓存: 在渲染的时候直接绘制图形。

  1. 构建两层canvas元素

当我们执行new fabric.Canvas('canvasId')的时候,页面的dom元素就发生了改变,我们可以通过调试工具发现有两层canvas元素:

2021230154852802dom333.jpeg

fabric这样设计的原因是将渲染层和交互层做分离:lower-canvas 只负责渲染元素;所有的交互,比如框选,事件处理都在 upper-canvas 上。如果我们不需要任何交互,可以使用静态画布:new fabric.StaticCanvas('canvasId', options),这样dom结构就只有一个canvas,没有upper-canvas.

3:事件的绑定等其他处理。

多个图层一起使用的demo(背景图缩放、点拖拽)‘

fabricZoom.gif

  1. canvas对象的实例化及背景图的设置
 createCanvas() {
      this.canvas = new fabric.Canvas("canvas", {
        width: this.containerWidth,
        height: 500,
      });
      this.getImgSize();
      this.canvas.setBackgroundImage(
        floor,
        this.canvas.renderAll.bind(this.canvas),
        {
          scaleX: this.canvas.width / this.imgWidth, // 背景图片自适应处理
          scaleY: this.canvas.height / this.imgHeight,
        }
      );
    }
复制代码
  1. 画布的移动处理逻辑:需要使用fabric的relativePan()函数来对画布的位置进行修正,通过官方文档可以知道它接收一个fabric.Point类型的参数。
   handleMouseMove(e) {
      if (this.moving && e && e.e) {
        // 获取当前画布中被选中的图层
        if (!this.canvas.getActiveObject()) {
          const delta = new fabric.Point(e.e.movementX, e.e.movementY);
          this.canvas.relativePan(delta);
        }
      }
    },
复制代码
  1. 画布的缩放处理:主要缩放的位置和缩放的倍数处理,同时在官方实例中也能找到以鼠标为中心点进行缩放的示例
 handleMouseWheel(e) {
      let zoom =
        e.e.deltaY > 0
          ? -0.1 + this.canvas.getZoom()
          : 0.1 + this.canvas.getZoom();
      zoom = Math.max(0.1, zoom); //最小为原来的1/10
      zoom = Math.min(3, zoom); //最大是原来的3倍
      const zoomPoint = new fabric.Point(e.pointer.x, e.pointer.y);
      this.canvas.zoomToPoint(zoomPoint, zoom);
    }
复制代码
  1. 文本图层的添加
  pointArray: [
        { test: "AAA1", x: 550, y: 50 },
        { test: "BBB1", x: 650, y: 450 },
        { test: "CCC1", x: 400, y: 300 },
        { test: "DDD1", x: 250, y: 50 },
        { test: "EEE", x: 1050, y: 450 },
        { test: "FFF", x: 550, y: 250 },
        { test: "HHH", x: 650, y: 350 },
      ]
  addPoint() {
      this.pointArray.forEach((item) => {
        const textPoint = new fabric.Text(item.test, {
          left: item.x,
          top: item.y,
          fontSize: 30,
          fontWeight: 600,
          hasControls: false,
          hasBorders: false,
        });
        // this.initPointEvent(textPoint)
        this.canvas.add(textPoint);
      });
    },
复制代码
文章分类
前端
文章标签