Fabric.js

541 阅读6分钟

Fabric.js 是一个基于 HTML5 canvas 的开源JavaScript工具库。

fabric.js的使用

Fabric提供了7种基础图形api

   const canvas = new fabric.Canvas("canvas", {
      backgroundColor: "green",
      selectionColor: "red",
      selectionLineWidth: 1,
    });
  • fabric.Circle 圆
var circle = new fabric.Circle({
  radius: 50, fill: 'red', left: 10, top: 10
});
canvas.add(circle);

  • fabric.Ellipse椭圆

var Ellipse =  fabric.Ellipse({
    rx:number,//指定水平半径
    ry:number,//指定垂直半径
    fill:string,//指定填充椭圆的颜色。
});
canvas.add(Ellipse);
  • fabric.Line线段
let line = new fabric.Line([10, 10, 100, 100], {
    fill: 'green', //填充颜色 
    stroke: 'green', //边框颜色
    strokeWidth: 2, //宽度
  })
 canvas.add(line)

  • fabric.Polygon多边形
    // 多边形
    let polygon = new fabric.Polygon(
      [
        {
          x: 100,
          y: 10,
        },
        {
          x: 250,
          y: 10,
        },
        {
          x: 250,
          y: 180,
        },
        {
          x: 150,
          y: 180,
        },
      ],
      {
        left: 100,
        top: 50,
        fill: "red",
        strokeWidth: 4,
        stroke: "green",
        perPixelTargetFind: true,
      }
    );
    canvas.add(polygon);
  • fabric.Polyline 多段线
var polygon = new fabric.Polyline(
         [
            { x: 0, y: 0 },
            { x: 50, y: 0 },
            { x: 50, y: 50 },
            { x: 0, y: 50 },
         ],
         {
            top: 50,
            left: 50,
            fill: "green",
         }
      );
  canvas.add(polygon);
  • fabric.Rect矩形
var rect = new fabric.Rect({
  fill: 'yellow',
  width: 10,
  height: 10,
});
canvas.add(rect);
  • fabric.Triangle三角形
var triangle = new fabric.Triangle({
  width: 20, height: 30, fill: 'blue', left: 50, top: 50
});
canvas.add(triangle);

fabric中的获取默认属性

var rect = new fabric.Rect(); // 这里没有传递任何参数

rect.get('width'); // 0
rect.get('height'); // 0

rect.get('left'); // 0
rect.get('top'); // 0

rect.get('fill'); // rgb(0,0,0)
rect.get('stroke'); // null

rect.get('opacity'); // 1

fabric对象常用属性

  1. 定位:topleft
  2. 尺寸:widthheight
  3. 渲染:fill,opacity,stroke,strokeWidth
  4. 缩放和旋转:scaleX,scaleY,angle;
  5. 翻转:flipXflipY
  6. 歪斜:skewXskewY
var rect = new fabric.Rect({
  left: 10,
  top: 10,
  fill: 'yellow',
  width: 10,
  height: 10,
  angle: 120 //需要旋转我们只需要配置即可
});
canvas.add(rect);
rect.set('fill', 'red');
rect.set({ strokeWidth: 5, stroke: 'rgba(100,200,200,0.5)' });
rect.set('angle', 15).set('flipY', true);

属性set还支持链式调用,并且,在canvas.add(triangle);之前或者之后调用,都是可以的: triangle.set("fill", "red").set("stroke", "red");//改变某个形状的填充和边框颜色 或者obj.set传入属性对象也是可以的 obj.set({属性名:属性值})

我们想在画布上画一个黄色的矩形。

var canvas = new fabric.Canvas('idkey');
// 创建一个矩形对象
var rect = new fabric.Rect({
  left: 10,
  top: 10,
  fill: 'yellow',
  width: 10,
  height: 10,
  angle: 120 //需要旋转我们只需要配置即可
});
// 然后再将矩形添加到画布上
canvas.add(rect);

我们想在画布上创建一个包含两个Fabric对象的组合,圆和文本

var circle = new fabric.Circle({  
  radius: 100,  
  fill: '#eef',  
  scaleY: 0.5,  
  originX: 'center',  
  originY: 'center'  
});  
  
var text = new fabric.Text('hello world', {  
  fontSize: 30,  
  originX: 'center',  
  originY: 'center'  
});  
  
var group = new fabric.Group([ circle, text ], {  
  left: 150,  
  top: 100,  
  angle: -10  
});  
  
canvas.add(group);

我们想要修改让canvas画布上的这个组合

group.item(0).setFill('green');  
group.item(1).set({  
  text: 'not hello',  
  fill: 'black'  
});

组合时可能用到的方法

  1. getObjects()————返回一个包含组合中所有对象的数组
  2. size()————返回组合中所有对象的数量。
  3. contains()————可以检查某个对象是否在组合中。
  4. item()————可以检索组中的特定对象。
  5. forEachObject————可以遍历每个组合中的对象。
  6. add()————组合中添加对象。
  7. remove()————组合中删除对象。
group.add(new fabric.Rect({  
  ...  
  originX: 'center',  
  originY: 'center'  
}));
group.addWithUpdate(new fabric.Rect({  
  ...  
  left: group.get('left')+10,  
  top: group.get('top')+10,  
  originX: 'center',  
  originY: 'center'  
}));

和原生canvas对比起来,在fabric中,我们只需要操作对象,实例化,改属性,添加就可以实现。 使用fabric.js 在需要编辑之前,我们不再需要清除画布。我们只需更改对象属性,然后重新绘制画布即可。

使用fabric加载图片

加载多个图片,要考虑到前面图片加载完毕的情况。

fabric.Image.fromURL('1.png', function(img) {  
  let img1 = img.scale(0.5).set({ left: 10, top: 10 });  

  fabric.Image.fromURL('2.png', function(img) {  
    let img2 = img.scale(0.5).set({ left: 100, top: 100 });  
  
    fabric.Image.fromURL('3.png', function(img) {  
      let img3 = img.scale(0.5).set({ left: 200, top: 200 });  
  
      canvas.add(new fabric.Group([ img1, img2, img3], { left: 100, top: 100 }))  
    });  
  });  
});

fabric中的canvas序列化方法

  • toObject()
  • toJSON()

我们先来看一个简单的例子

let canvas = new fabric.Canvas('id');  
canvas.backgroundColor = 'red';
JSON.stringify(canvas);  
// '{"objects":[],"background":"red'

我们使用ES5的JSON.stringify()方法,如果toJSON方法存在,则会隐性调用。
因为fabric中的canvas实例已经具有toJSON方法,就相当于我们调用了canvas.toJSON()。

// 创建一个矩形对象
var rect = new fabric.Rect({
  left: 10,
  top: 10,
  fill: 'yellow',
  width: 10,
  height: 10,
  angle: 120 
});
canvas.add(rect);
console.log(JSON.stringify(canvas));
//{"objects":[{"type":"rect","left":10,"top":10,"width":10,"height":10,"fill":"yellow","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":120,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}
console.log(canvas.toObject())
//{ "background" : "rgba(0, 0, 0, 0)",  
  "objects" : [  
    {  
      "angle" : 120,  
      "fill" : "yellow",  
      "flipX" : false,  
      "flipY" : false,  
      "hasBorders" : true,  
      "hasControls" : true,  
      "hasRotatingPoint" : false,  
      "height" : 10,  
      "left" : 10,  
      "opacity" : 1,  
      "overlayFill" : null,  
      "perPixelTargetFind" : false,  
      "scaleX" : 1,  
      "scaleY" : 1,  
      "selectable" : true,  
      "stroke" : null,  
      "strokeDashArray" : null,  
      "strokeWidth" : 1,  
      "top" : 10,  
      "transparentCorners" : true,  
      "type" : "rect",  
      "width" : 10  
    }  
  ]  
}

转SVG图

canvas.add(new fabric.Rect({  
  left: 10,  
  top: 10,  
  height:10,  
  width: 10,  
  fill: 'red'  
}));  
console.log(canvas.toSVG());//直接调用toSVG即可。
输出结果:
'<?xml version="1.0" standalone="no" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="700" xml:space="preserve"><desc>Created with Fabric.js 0.9.21</desc><rect x="-10" y="-10" rx="0" ry="0" width="10" height="10" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: red; opacity: 1;" transform="translate(10 10)" /></svg>'

反序列化,SVG解析

如果反序列表是json时使用canvas.loadFromJSON和canvas.loadFromDatalessJSON

var canvas = new fabric.Canvas();  
  
canvas.loadFromJSON('{"objects":[{"type":"rect","left":50,"top":50,"width":20,"height":20,"fill":"green","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"radius":50}],"background":"rgba(0, 0, 0, 0)"}');

如果反序列表是svg时使用fabric.loadSVGFromURL和fabric.loadSVGFromString

  fabric.loadSVGFromURL('要加载的svg地址', (objects, options) => {
    let img1 = fabric.util.groupSVGElements(objects, options);
    img1.set({
      left: 100,
      top: 100,
    });
    canvasContainer.add(img1);
  });
fabric.loadSVGFromString('./1.svg', function(objects, options) {  
  var obj = fabric.util.groupSVGElements(objects, options);  
  canvas.add(obj).renderAll();  
});

fabric的子类

在 fabric.js 中创建类,可以使用 fabric.util.createClass() 方法。

let Point = fabric.util.createClass({
  initialize: function(x, y) {
    this.x = x || 0
    this.y = y || 0
  },
  toString: function() {
    return `${this.x}/${this.y}`
  }
})

fabric.util.createClass 接受一个对象参数,并基于该对象的配置创建一个“类”。

需要留意的是 initialize 属性,initialize 的值是一个方法,用于初始化。 在 initialize 中接收实例化时传进来的参数。

let point = new Point(10, 20)

console.log(point.x) // 10
console.log(point.y) // 20

console.log(point.toString()) // "10/20"

在创建类时,只需在 fabric.util.createClass 中传入1个对象即可。

如果创建子类时需要继承某个父类,则要在 fabric.util.createClass 中传入2个参数。 fabric.util.createClass(parentopt, propertiesopt)

  • parentopt: 父类
  • propertiesopt 创建子类的对象(和前面创建类的对象一样) 在前面的创建类时,我们创建了一个 Point 的类,这个类只接受 x 和 y 属性以及内部的toString() 方法。

假设需要在 Point 类的基础上再创建一个 jcPoint 类(也就是 jcPoint 继承 Point),可以这样写。

let jcPoint = fabric.util.createClass(
  // 参数1:父类
  Point,
  // 参数2:子类
  {
    initialize: function(x, y, color) {
      this.callSuper('initialize', x, y) // 传给父类的
      this.color = color || '#000' // 新增的 color 属性
    }
  }
)

此时 jcPoint 就继承了 Point ,并在 Point 的基础上多了 color 属性了。

let redPoint = new jcPoint(10, 10, 'red')
console.log(redPoint.x) // 10
console.log(redPoint.y) // 10
console.log(redPoint.color) // "red"
console.log(redPoint.toString()) // "10/10"

Point 里有 toString() 方法, redPoint.toString() 会先 jcPoint 找,找不到就从 Point 找。一层层往上找。

如果在 jcPoint 中又定义了 toString() 方法,就会覆盖 Point 里定义的。


// 父类
let Point = new fabric.util.createClass({
  initialize(x, y) {...},
  toString() {
    return this.x + '/' + this.y
  }
})

// 子类
let jcPoint = fabric.util.createClass(
  // 参数1:父类
  Point,
  // 参数2:子类
  {
    initialize(x, y, color) {...},
    toString() {
      return 'hello'
    }
  }
)

// 实例化
let redPoint = new jcPoint(15, 33, '#f55')

console.log(redPoint.toString()) // "hello"

如果在子类中想继承父类的方法可以使用 callSuper 。

// 父类
let Point = new fabric.util.createClass({...})

// 省略部分代码
let jcPoint = fabric.util.createClass(
  // 参数1:父类
  Point,
  // 参数2:子类
  {
    initialize(x, y, color) {...},
    toString() {
      return this.callSuper('toString')
    }
  }
)

// let redPoint = new jcPoint(12, 13, 'red')

console.log(redPoint.toString()) // "12/13"