fabricjs文档阅读(三)

群组

群组是Fabric最强大的功能之一。它们就是它们听起来的样子——一种将任何fabric对象组合成单个实体的简单方法。我们为什么要这么做?当然,能够将这些对象作为一个对象来工作!

还记得canvas上的任意数量的Fabric对象是如何用鼠标分组的吗?一旦分组,所有对象都可以一起移动甚至修改。他们组成一个团体。我们可以缩放这个组,旋转,甚至改变它的表示属性——颜色、透明度、边框等等。

这正是组的作用,每次您在画布上看到这样的选择时,Fabric都会在幕后隐式地创建一组对象。只有以编程方式提供对使用组的访问才有意义。

让我们创建一组2个对象,圆圈和文本:

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);

首先,我们创建了一个“hello world”文本对象。将originXoriginY设置为center'将使其位于组的中心;默认情况下,组成员是相对于组的左上角定向的。然后,以100px为半径,填充“#eef”颜色,垂直缩小(scaleY=0.5)。然后我们创建了一个fabric.Group,将这两个对象的数组传递给它,并将其位置设置为150/100-10deg。最后,将组添加到canvas中,就像添加其他对象一样(使用canvas.add())。

你在画布上看到一个物体,看起来像一个带标签的椭圆。请注意,为了修改该对象,我们只是更改了组的属性,并赋予它自定义的左值、顶值和角值。现在可以将此对象作为单个实体来处理。

fabricjs文档阅读(三)_第1张图片

现在我们在画布上创建了一个群组,让我们稍微修改一下:

// 为了使用setFill命名setter,您需要添加可选的命名setter/getter
// code from src/util/named_accessors.mixins.js
group.item(0).set('fill', 'red');
group.item(1).set({
  text: 'trololo',
  fill: 'white'
});

这是怎么回事?我们通过item()方法访问组中的各个对象,并修改它们的属性。第一个对象是缩小圆,第二个对象是文本。让我们看看会发生什么:fabricjs文档阅读(三)_第2张图片

到目前为止,您可能已经注意到的一件重要事情是,组中的对象都相对于组的中心进行定位。当我们改变文本对象的文本时,即使改变了它的宽度,它仍然保持居中。如果不需要此行为,则需要指定对象的左/顶坐标。在这种情况下,它们将根据这些坐标组合在一起。

让我们创建并分组3个圆圈,让它们一个接一个地水平放置:

var circle1 = new fabric.Circle({
  radius: 50,
  fill: 'red',
  left: 0
});
var circle2 = new fabric.Circle({
  radius: 50,
  fill: 'green',
  left: 100
});
var circle3 = new fabric.Circle({
  radius: 50,
  fill: 'blue',
  left: 200
});

var group = new fabric.Group([ circle1, circle2, circle3 ], {
  left: 200,
  top: 100
});

canvas.add(group);

在处理组时要记住的另一件事是对象的状态。例如,当使用图像组成一个组时,您需要确保这些图像已被完全加载。由于Fabric已经提供了帮助方法来确保加载图像,这变得相当容易:

fabric.Image.fromURL('/assets/pug.jpg', function(img) {
  var img1 = img.scale(0.1).set({ left: 100, top: 100 });

  fabric.Image.fromURL('/assets/pug.jpg', function(img) {
    var img2 = img.scale(0.1).set({ left: 175, top: 175 });

    fabric.Image.fromURL('/assets/pug.jpg', function(img) {
      var img3 = img.scale(0.1).set({ left: 250, top: 250 });

      canvas.add(new fabric.Group([ img1, img2, img3], { left: 200, top: 200 }))
    });
  });
});

fabricjs文档阅读(三)_第3张图片

那么,在处理组时,还有哪些方法可用呢?有getObjects()方法,它的工作原理与fabric.Canvas#getObjects()完全相同,返回一个组中所有对象的数组。size()表示组中所有对象的数量。contains()允许检查特定对象是否在组中。我们前面看到的item()允许检索组中的特定对象。还有forEachObject(),它再次镜像了fabric.Canvas#forEachObject,仅与组对象相关。最后是add()remove()方法,用于相应地从组中添加和删除对象。

要在组的中心添加矩形:

group.add(new fabric.Rect({
  ...
  originX: 'center',
  originY: 'center'
}));

若要添加距离组中心100px的矩形:

group.add(new fabric.Rect({
  ...
  left: 100,
  top: 100,
  originX: 'center',
  originY: 'center'
}));

若要在组的中心添加矩形并更新组的尺寸:

group.addWithUpdate(new fabric.Rect({
  ...
  left: group.get('left'),
  top: group.get('top'),
  originX: 'center',
  originY: 'center'
}));

添加距离组中心100px的矩形,并更新组的尺寸:

group.addWithUpdate(new fabric.Rect({
  ...
  left: group.get('left') + 100,
  top: group.get('top') + 100,
  originX: 'center',
  originY: 'center'
}));

最后,如果你想创建一个对象已经存在于画布上的组,首先你需要克隆它们:

// create a group with copies of existing (2) objects
var group = new fabric.Group([
  canvas.item(0).clone(),
  canvas.item(1).clone()
]);

// remove all objects and re-render
canvas.clear().renderAll();

// add group onto canvas
canvas.add(group);

Serialization序列化

这个功能是将canvas的添加的图像对象进行序列化不同的数据,可以变成JSONimageURL(base64)格式object对象

  • toObjecttoJSON这2个是一样的,返回值都是canvas画布的数据
    例子:

    var canvas = new fabric.Canvas('c');
    JSON.stringify(canvas); // '{"objects":[],"background":"rgba(0, 0, 0, 0)"}'
    

    发现返回一个对象,object保存的是图像对象数据,后面参数是canvas自己的属性,其他没有设置就按照默认设置.
    我们添加一些图像在canvas画布上看看.

    canvas.add(new fabric.Rect({
      left: 50,
      top: 50,
      height: 20,
      width: 20,
      fill: 'green'
    }));
    console.log(JSON.stringify(canvas));
    
    '{"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}],"background":"rgba(0, 0, 0, 0)"}'
    

    需要注意,由于性能考虑,图像不是所有属性都可以进行序列化,fabric只是包装了一些常用的属性,所以如果设置了一些比较少用的属性,在加载JSON数据的时候需要重新修改图像的属性.
    fabricjs文档阅读(三)_第4张图片这是对象可以进行序列化的属性,可以清楚看到我们已经设置了的属性和值,没有设置的属性都是0或者null.但是有一些属性比如:hasRotatingPointtransparentCorners等这些属性,需要自己重新设置

    • toDataURL
      这个是将画布转化成图片(base64格式),标签image是支持这个格式的,所以我们可以做一个canvas的预览窗口
      fabricjs文档阅读(三)_第5张图片
      调用这个方法直接返回图片的数据.我们直接在img标签就可以使用
      ,imgUrl保存的是base64格式的图片.

如何使用这些JSON数据?

当你获取了JSON格式之后,我们只需要调用一个fabricloadFormJSON方法即可.
需要注意还有一个方法,loadFormDatalessJSON,这个加载的也是canvas的对象数据,但是不同的是获取的方法不一致.
loadFormDatalessJSON对应的是toDatalessJSON
loadFromJSON对应的是toJSON或者是toObject


详细的还需要大家看文档,文档说的比较基础,还有一个toSVG的方法,但是感觉使用的场景比较局限,就不在这里介绍了,有兴趣的可以去文档看看.

Subclassing自定义图形对象(继承)

由于Fabric是以面向对象的方式构建的,它的设计使子类化和扩展变得简单和自然,正如您在本系列的第1部分中所了解的,Fabric中存在对象层次结构。所有2D对象(路径、图像、文本等)都继承自fabric。对象,以及一些“类”,如fabric.IText 甚至形成3级继承。

这个任务需要fabric.util.createClass实用方法。createClass只是对Javascript原型继承的一个简单抽象。让我们首先创建一个简单的“类”:

var Point = fabric.util.createClass({
  initialize: function(x, y) {
    this.x = x || 0;
    this.y = y || 0;
  },
  toString: function() {
    return this.x + '/' + this.y;
  }
});

createClass接受一个对象,并使用该对象的属性创建具有实例级属性的“类”。惟一经过特殊处理的属性是“initialize”,它用作构造函数。当初始化Point时,我们会创建一个实例,带有"x"和"y"属性,以及"toString"方法:

var point = new Point(10, 20);

point.x; // 10
point.y; // 20

point.toString(); // "10/20"

如果我们想创建一个POINT类的子类——比如一个彩色的POINT,我们会像这样使用createClass:

var ColoredPoint = fabric.util.createClass(Point, {
  initialize: function(x, y, color) {
    this.callSuper('initialize', x, y);
    this.color = color || '#000';
  },
  toString: function() {
    return this.callSuper('toString') + ' (color: ' + this.color + ')';
  }
});

注意,现在如何将具有实例级属性的对象作为第二个参数传递。第一个参数接收点“class”,它告诉createClass使用它作为这个类的父“类”。为了避免重复,我们使用callSuper方法,它调用父类的方法。这意味着如果我们改变点,变化也会传播到色点1。要查看ColoredPoint的作用:

var redPoint = new ColoredPoint(15, 33, '#f55');

redPoint.x; // 15
redPoint.y; // 33
redPoint.color; // "#f55"

redPoint.toString(); "15/33 (color: #f55)"

现在我们已经创建了自己的“类”和“子类”,让我们看看如何使用已经存在的Fabric类。例如,让我们创建一个LabeledRect“类”,它本质上是一个矩形,具有某种与之关联的标签。当在画布上呈现时,该标签将表示为矩形内的文本。类似于前面使用圆圈和文本的组示例。在使用Fabric时,您会注意到,这样的组合抽象可以通过使用组或自定义类来实现。

var LabeledRect = fabric.util.createClass(fabric.Rect, {

  type: 'labeledRect',

  initialize: function(options) {
    options || (options = { });

    this.callSuper('initialize', options);
    this.set('label', options.label || '');
  },

  toObject: function() {
    return fabric.util.object.extend(this.callSuper('toObject'), 	{
      label: this.get('label')
    });
  },

  _render: function(ctx) {
    this.callSuper('_render', ctx);
    ctx.font = '20px Helvetica';
    ctx.fillStyle = '#333';
    ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
  }
});

看起来有很多事情要做,但其实很简单。

  • 首先,我们将父类指定为fabric。使用它的渲染能力。

  • 接下来,我们定义“type”属性,将其设置为“labeledRect”。这只是为了保持一致性,因为所有的Fabric对象都具有类型属性(rect、circle、path、text等)

  • 所以我们再次使用callSuper时已经使用了熟悉的构造函数(initialize)

  • 此外,我们将对象的标签设置为通过选项传递的任何值。最后,我们剩下两个方法——toObject_render

  • 正如您在序列化一章中已经知道的,toObject负责对象(和JSON)表示实例。由于LabeledRect具有与常规rect相同的属性,而且也是一个标签,所以我们正在扩展parenttoObject方法,并简单地将标签添加到其中。最后但并非最不重要的是,_rendermethod负责实际绘制实例。其中还有另一个callSuper调用,它呈现矩形,以及额外的3行文本呈现逻辑。

var labeledRect = new LabeledRect({
  width: 100,
  height: 50,
  left: 100,
  top: 100,
  label: 'test',
  fill: '#faa'
});
canvas.add(labeledRect);

fabricjs文档阅读(三)_第6张图片

labeledRect.set({
  label: 'trololo',
  fill: '#aaf',
  rx: 10,
  ry: 10
});

fabricjs文档阅读(三)_第7张图片

当然,在这一点上,您可以随意修改这个“类”的行为。例如,将某些值设置为默认值,以避免每次将它们传递给构造函数。或在实例上提供某些可配置属性。如果你确实使额外的属性可配置,你可能想要解释他们在toObject和初始化:

...
initialize: function(options) {
  options || (options = { });

  this.callSuper('initialize', options);

  // give all labeled rectangles fixed width/heigh of 100/50
  this.set({ width: 100, height: 50 });

  this.set('label', options.label || '');
}
...
_render: function(ctx) {

  // make font and fill values of labels configurable
  ctx.font = this.labelFont;
  ctx.fillStyle = this.labelFill;

  ctx.fillText(this.label, -this.width/2, -this.height/2 + 20);
}
...

你可能感兴趣的:(fabric,新手入门,一些学习的研究)