Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

Introduction to Fabric.js: Part 1

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

今天,我想向您介绍Fabric.js —一个功能强大的Javascript库,使使用HTML5 canvas的工作变得轻而易举。 Fabric提供了画布缺少的对象模型,以及SVG解析器,交互性层和一整套其他必不可少的工具。这是一个完全开放源代码的项目,已获得麻省理工学院(MIT)的许可,多年来有许多贡献。

在发现使用原生canvas API的烦恼之后,Fabric于2010年左右开始。最初的作者正在为printio.ru创建一个交互式设计编辑器-他的创业公司允许用户设计自己的服装。他们想要的那种交互性仅在那时存在于Flash应用程序中。即使是现在,也很少有人能接近Fabric所能提供的功能。

让我们仔细看看!

Why fabric?(什么是Fabric?)

如今,Canvas允许我们在网络上创建一些绝对惊人的图形。但是它提供的API是极令人失望地低级。如果我们只是想在画布上绘制一些基本形状而忘记它们,那是一回事。但是,只要需要进行任何形式的交互,随时更改图片或绘制更复杂的形状,情况就会发生巨大变化。

Fabric旨在解决这个问题。

原生canvas方法仅允许我们触发简单的图形命令,盲目地修改整个画布位图。想画一个矩形吗?使用fillRect(left,top,width,height)。要画一条线吗?使用moveTo(left,top)和lineTo(x,y)的命令组合。就像我们正在用刷子画画布一样,在很少的控制下将越来越多的油层涂在上面。

Fabric没有在如此低的级别上进行操作,而是在原生canvas方法之上提供了简单但功能强大的对象模型。它负责画布的状态和渲染,并让我们直接使用“对象”。

让我们看一个简单的例子来说明这种差异。假设我们要在画布上的某个地方绘制一个红色矩形。这是我们如何使用原生canvas API进行的操作。

<template>
  <div class="app">
    <canvas id="canvas" width="300" height="200"></canvas>
  </div>
</template>

<script>
export default {
  mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    ctx.fillRect(100,100,20,20);
  },
};
</script>
<style>
</style>

现在,让我们看一下使用Fabric进行相同的操作:

<template>
  <div class="app">
    <canvas id="canvas" width="300" height="200"></canvas>
  </div>
</template>

<script>
import {fabric} from "fabric"
export default {
  mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
    });
    canvas.add(rect);
  }
}
</script>

<style>

</style>

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

此时,大小几乎没有区别-这两个示例非常相似。但是,您已经可以看到使用画布的方法有多么不同。使用原生方法,我们可以在上下文上操作——一个表示整个画布位图的对象。在Fabric中,我们对对象进行操作——实例化它们,更改其属性并将其添加到画布。您会看到这些对象是Fabric土地上的一等公民。

但是渲染纯红色矩形实在太无聊了。我们至少可以从中获得一些乐趣!也许,稍微旋转一下?

让我们尝试45度。首先,使用原生canvas方法:

mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    // ctx.fillRect(100,100,20,20);

    //偏移角度
    ctx.translate(100,100);
    ctx.rotate(Math.PI/180*45);
    ctx.fillRect(-10,-10,20,20);
  },

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

现在使用Fabric:

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
      angle:45 //偏移角度
    });
    canvas.add(rect);
  }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

(注意这里两个是不一样的,下面作者说使用canvas时为了让为了矩形保持在原位需要偏移(-10,-10)但其实不需要这样做。最后直接ctx.fillRect(0,0,20,20)才是一样的效果。)

这里发生了什么?

我们在Fabric中要做的就是将对象的“角度”值更改为45。但是,使用原生canvas方法,事情会变得更加“有趣”。请记住,我们不能对对象进行操作。相反,我们调整整个画布位图的位置和角度(ctx.translate,ctx.rotate)以适合我们的需求。然后,我们再次绘制矩形,但要记住适当地偏移位图(-10,-10),以便它仍在(100,100)点位处渲染。作为一项额外的练习,在旋转画布位图时,我们必须将度数转换为弧度。

我相信您已经开始确切地了解为什么存在Fabric以及它隐藏了多少个低级样板。

但是,让我们看看另一个示例——跟踪画布状态。

如果在某个时候,我们想将现在熟悉的红色矩形移动到画布上稍有不同的位置,该怎么办?在无法对对象进行操作的情况下,我们将如何做到这一点?我们仅在画布位图上调用另一个fillRect吗?

不完全是。调用另一个fillRect命令实际上会在画布上已绘制的内容的上方绘制矩形。还记得我之前提到过用画笔绘画吗?为了“移动”它,我们需要首先擦除先前绘制的内容,然后在新位置绘制矩形。

mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    // ctx.fillRect(100,100,20,20);

    //位移
    ctx.strokeRect(100,100,20,20);
    ctx.clearRect(0,0,canvasEl.width,canvasEl.height);
    ctx.fillRect(20,50,20,20);
  },

那我们将如何使用Fabric做到这一点?

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
    });
    canvas.add(rect);
		// 位移
    rect.set({left:20,top:50});
    canvas.renderAll()
  }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

注意一个非常重要的区别。使用Fabric,在尝试修改任何内容前,我们不再需要删除内容。我们仍然使用对象,只需更改其属性,然后重新渲染画布即可获得“新图片”。

Objects(对象)

我们已经看到了如何通过实例化fabric.Rect构造函数来使用矩形。但是,Fabric当然也涵盖了所有其他基本形状——圆形,三角形,椭圆形等。它们全部都在fabric“命名空间”下暴露为fabric.Circle,circle.Triangle,fabric.Ellipse等。

Fabric提供的7中基本形状:

想画一个圆吗?只需创建一个圆形对象,然后将其添加到画布即可。与其他任何基本形状相同:

 mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    // 画圆
    var circle = new fabric.Circle({
      radius:20,
      fill:"green",
      left:100,
      top:100,
    })
    // 画三角形
    var triangle = new fabric.Triangle({
      width:20,
      height:30,
      fill:'blue',
      left:50,
      top:50
    })

    canvas.add(circle,triangle)
  }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

我们有一个在(100,100)位置绘制的绿色圆圈和在(50,50)位置绘制的蓝色三角形。

Manipulating objects(操作对象)

创建图形对象(矩形,圆形或其他形状)当然只是开始。在某个时候,我们可能会想要修改那些对象。也许某些动作将需要触发状态改变,或者播放某种形式的动画。或者,我们可能想更改某些鼠标交互时的对象属性(颜色,不透明度,大小,位置)。

Fabric负责我们的画布渲染和状态管理。我们只需要修改对象本身即可。

前面的示例演示了set方法以及如何调用set({left:20,top:50})从先前位置“移动”对象。以类似的方式,我们可以更改对象的任何其他属性。但是这些属性是什么?

好了,正如您所期望的,这些都与定位有关——left、top;尺寸——width、height;渲染——fill、opacity、stroke、strokeWidth;缩放和旋转——scaleX、scaleY、angle;甚至与翻转相关的内容——flipX、flipY和倾斜的skewX、skewY

是的,在Fabric中创建翻转对象就像将flip *属性设置为true一样容易。

您可以通过get方法读取任何这些属性,并通过set进行设置。让我们尝试更改红色矩形的一些属性:

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      width:20,
      height:20,
    });
    canvas.add(rect);

    rect.set("fill","red");
    rect.set({strokeWidth:5,stroke:"rgba(100,200,200,0.5)"});
    rect.set("angle",60).set("flipY",true);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

首先,我们将“填充”值设置为“红色”,实质上使对象变成红色。下一条语句同时设置“ strokeWidth”和“ stroke”值,为矩形提供5px浅绿色的边框。最后,我们要更改“ angle”和“ flipY”属性。请注意,这3条语句中的每条语句如何使用,略有不同的语法。

这证明了set是一种通用方法。您可能会经常使用它,因此它应该尽可能地方便。

我们已经介绍了setter,那么getter呢?很明显,既有通用的get方法,也有许多特定的get *方法。要读取对象的“宽度”值,可以使用get('width')getWidth()。要获取“ scaleX”值,请使用get('scaleX')getScaleX(),依此类推。对于每个“公共”对象属性(例如“ stroke”,“ strokeWidth”,“ angle”等),都有一种类似于getWidth或getScaleX的方法。

您可能会注意到,在先前的示例中,对象是使用与我们刚在set方法中使用过的配置相同的配置哈希值创建的。那是因为它是完全一样的。您可以在创建时“配置”对象,或在之后使用set方法:

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect({
      width: 10,
      height: 20,
      fill: "#f55",
      opacity: 0.7,
    });

    // or functionally identical

    //var rect = new fabric.Rect();
    //rect.set({ width: 10, height: 20, fill: "#f55", opacity: 0.7 });

    canvas.add(rect);
 }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

Default options(默认参数)

此时,您可能会问——当我们创建一个对象而不传递任何“配置”对象时会发生什么。它仍然具有那些属性吗?

是的当然。 Fabric中的对象始终具有一组默认属性。在创建过程中被忽略时,将“赋予”对象的此默认属性集。我们可以尝试自己看看:

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect()
    console.log(rect.get("width"));//0
    console.log(rect.get('height'));//0
    console.log(rect.get("left"));//0
    console.log(rect.get("top"));//0
    console.log(rect.get("fill"));//rgb(0,0,0)
    console.log(rect.get("stroke"));//null
    console.log(rect.get("opacity"));//1

    canvas.add(rect);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

我们的矩形具有一组默认属性。它的位置是(0,0),是黑色,完全不透明,没有笔触,没有尺寸(宽度和高度均为0)。由于没有尺寸,因此我们无法在画布上看到它。但是给它任何宽度/高度的正值肯定会在画布的左/上角显示一个黑色的矩形。

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect({width:20,height:20})
    console.log(rect.get("width"));//20
    console.log(rect.get('height'));//20
    console.log(rect.get("left"));//0
    console.log(rect.get("top"));//0
    console.log(rect.get("fill"));//rgb(0,0,0)
    console.log(rect.get("stroke"));//null
    console.log(rect.get("opacity"));//1

    canvas.add(rect);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

Hierarchy and Inheritance(层次结构和继承)

构造对象不仅彼此独立存在。它们形成了非常精确的层次结构。

大多数对象都从根fabric.Object继承。 fabric.Object几乎代表一个二维形状,位于二维画布平面中。它是一个具有left / top和width / height属性以及一系列其他图形特征的实体。我们在对象上看到的那些属性(fill、stroke、angle、opacity、flip *等)对于所有从fabric.Object继承的Fabric对象都是共有的。 这种继承使我们可以在fabric.Object上定义方法,并在所有子“类”之间共享它们。例如,如果要在所有对象上都具有getAngleInRadians方法,则只需在fabric.Object.prototype上创建它:

mounted() {
    fabric.Object.prototype.getAngleInRadians = function(){
      return this.get("angle")/180*Math.PI;
    };

    var rect =new fabric.Rect({angle:45});
    console.log(rect.getAngleInRadians());//0.7853981633974483

    var circle = new fabric.Circle({angle:30,radius:10});
    console.log(circle.getAngleInRadians());//0.5235987755982988

    console.log(circle instanceof fabric.Circle);//true
    console.log(circle instanceof fabric.Object);//true
    }

如您所见,方法立即在所有实例上可用。

虽然子“类”继承自fabric.Object,但它们通常还定义了自己的方法和属性。例如,fabric.Circle需要具有“半径”属性。稍后我们将要讨论的fabric.Image需要具有getElement/ setElement方法,以访问/设置图像实例所源自的HTML <img>元素。

对于高级项目,使用原型来获取自定义渲染和行为是很常见的。

Canvas(画布)

现在我们已经详细介绍了对象,让我们回到画布上。

如果创建画布对象,则可以在所有Fabric示例中看到的第一件事——new fabric.Canvas('...')。 fabric.Canvas用作<canvas>元素的包装,并负责管理该特定画布上的所有Fabric对象。它接受元素的ID,并返回fabric.Canvas的实例。

我们可以在其上添加对象,从中引用或删除它们:

mounted(){
		var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect();

//在canvas上添加一个对象
canvas.add(rect); 

//canvas上添加的第一个对象
canvas.item(0); 

//获取canvas上的所有对象
//这里需要注意,使用canvas.item或者canvas.item()获取所有对象是没有用的
canvas.getObjects(); 

//删除指定的对象
canvas.remove(rect); 
}

虽然管理对象是fabric.Canvas的主要目的,但它也可以用作配置画布本身。是否需要为整个画布设置背景颜色或图像?将所有内容都剪切到某个区域?设置不同的宽度/高度?指定画布是否是交互式的?所有这些选项(以及其他选项)都可以在创建时或之后在fabric.Canvas上设置:

mounted(){
		var canvas = new fabric.Canvas("canvas",{
      backgroundColor:'rgb(100,100,200)',
      selectionColor:'blue',
      selectionLineWidth:2
    })

    var rect = new fabric.Rect()
    canvas.add(rect);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

或者

mounted(){
		var canvas = new fabric.Canvas("canvas");
		canvas.setBackgroundColor("rgb(100,200,200)");
    canvas.onFpsUpdate = function(){
      console.log('success!');
    }

    var rect = new fabric.Rect();
    canvas.add(rect);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

Interactivity(互动性)

当我们讨论canvas元素时,让我们谈谈交互性。内置的Fabric的独特功能之一是,在我们刚刚查看的所有便捷对象模型之上,都具有一层交互性。

存在对象模型以允许以编程方式访问和操纵画布上的对象。但是在外部,在用户级别上,有一种方法可以通过鼠标(或触摸设备上的触摸)来操纵这些对象。通过new fabric.Canvas('...')初始化画布后,就可以选择对象,对其进行拖动,缩放或旋转它们,甚至将它们组合在一起以在一个块中进行操作!

mounted(){
		var canvas = new fabric.Canvas("canvas");
		canvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20});
    canvas.add(rect);
}

原始画布如下:

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

可以通过鼠标操作对象,如下:

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

如果我们希望用户允许在画布上拖动某些东西(例如图像),我们要做的就是初始化画布并在其上添加一个对象。无需其他配置或设置。

为了控制这种交互性,我们可以在画布上结合使用Fabric的“ selection”布尔属性和单个对象的“ selectable”布尔属性。

mounted(){
		var canvas = new fabric.Canvas("canvas");
    canvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20,left:100,top:50});
    var circle = new fabric.Circle({radius:30,fill:"rgb(100,100,200)"})

    //设置不能群体选择
    canvas.selection = false;
    //设置rect对象无法被选中
    rect.set("selectable",false);

    canvas.add(rect,circle);
}

这时候只有圆才能被选中操作。

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

但是,如果您根本不想要这样的交互层,该怎么办?如果是这种情况,您始终可以用fabric.StaticCanvas替换fabric.Canvas。初始化的语法完全相同。您只能使用StaticCanvas而不是Canvas

mounted(){
		var staticCanvas = new fabric.StaticCanvas("canvas");

    staticCanvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20,left:100,top:50});
    var circle = new fabric.Circle({radius:30,fill:"rgb(100,100,200)"})

    staticCanvas.add(rect,circle);
}

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

这将创建一个“较轻”版本的画布,而没有任何事件处理逻辑。请注意,您仍然可以使用整个对象模型-添加对象,删除或修改它们,以及更改任何画布配置-所有这些仍然有效。只是事件处理不见了。

稍后,当我们遍历自定义构建选项时,您会看到,如果只需要StaticCanvas,您甚至可以创建一个轻量级的Fabric。如果您需要非交互式图表或应用程序中带有过滤器的非交互式图像,这可能是一个不错的选择。

Images(图像)

说起图片… 在画布上添加矩形和圆形很有趣,但是为什么我们不玩一些图像呢?就像您现在想像的那样,Fabric使这变得容易。让我们实例化fabric.Image对象并将其添加到画布:

<template>
  <div class="app">
    <canvas id="canvas" width="400" height="300" ></canvas>
    <img src="../src/assets/logo.png" alt="picture" id="img" width="100" height="auto" >
  </div>
</template>

<script>
import { fabric } from "fabric";
export default {
  mounted() {
   	var canvas = new fabric.Canvas("canvas");
    var imgElement = document.getElementById("img");

    var imgInstance = new fabric.Image(imgElement,{
      left:100,
      top:10,
      angle:30,
      opacity:0.85
    });
    canvas.add(imgInstance);
  },
};
</script>

<style>
</style>

注意我们如何将图像元素传递给fabric.Image构造函数。这将创建一个fabric.Image实例,其外观与文档中的图像相似。此外,我们立即将left/top设置为100/10,角度设置为30,不透明度设置为0.85。一旦添加到画布上,图像将在100,10位置,30度角处渲染,并且稍微透明!不错。

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

现在,如果我们在文档中没有真正的图像,而只是图像的URL,该怎么办?没问题让我们看看如何使用fabric.Image.fromURL:

<template>
  <div class="imageFilter">
      <canvas id="canvas" width="300" height="300"></canvas>
  </div>
</template>

<script>
import {fabric} from "fabric"
export default {
    mounted(){
        var canvas = new fabric.Canvas("canvas",{backgroundColor:'#ddd'})

        fabric.Image.fromURL("https://ss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh=450,600/sign=093b8529572c11dfde84b72756174ee6/aa18972bd40735fa8d2f0c739e510fb30e240818.jpg",(img)=>{
            canvas.add(img);
        })
    }

}
</script>


<style>

</style>

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

看起来很简单,不是吗?只需使用图像的URL调用fabric.Image.fromURL,然后给它一个回调即可在加载和创建图像后调用。回调函数接收已创建的fabric.Image对象作为第一个参数。此时,您可以将其添加到画布,或者可能先更改,然后再添加到画布:

mounted(){
        var canvas = new fabric.Canvas("canvas",{backgroundColor:'#ddd'})

        fabric.Image.fromURL("https://ss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh=450,600/sign=093b8529572c11dfde84b72756174ee6/aa18972bd40735fa8d2f0c739e510fb30e240818.jpg",(img)=>{
      img.scale(0.5).set("flipX",true)
            canvas.add(img);
        })
    }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

Paths

我们研究了简单的形状,然后是图像。但是更复杂,更丰富的形状和内容呢?

结识实力雄厚的一对——路径和群组。

Fabric中的路径代表可以用其他方式填充,描边和修改的形状轮廓。路径由一系列命令组成,这些命令实际上模仿了笔从一个点到另一个点的过程。借助“移动”,“线”,“曲线”或“弧”等命令,路径可以形成难以置信的复杂形状。在路径组(PathGroup’s)的帮助下,可能性更大。

Fabric中的路径非常类似于SVG <path>元素。它们使用相同的命令集,可以从<path>元素创建它们,并将其序列化。稍后我们将更仔细地研究序列化和SVG解析,但是现在值得一提的是,您可能很少会手动创建Path实例。相反,您将使用Fabric的内置SVG解析器。但是要了解什么是Path对象,让我们尝试手动创建一个简单的对象:

<template>
  <div class="app">
    <canvas id="canvas" width="400" height="400" ></canvas>
  </div>
</template>

<script>
import { fabric } from "fabric";
export default {
  mounted() {
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var path = new fabric.Path("M 0 0 L 200 100 L 170 200 z");
    path.set({left:120,top:120});
    canvas.add(path);
  },
};
</script>

<style>
</style>

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

我们正在实例化fabric.Path对象,并向其传递一串路径指令。虽然看起来很神秘,但实际上很容易理解。 “ M”表示“移动”命令,并指示隐形笔移动到(0、0)点。 “ L”代表“线”,使笔画一条线到(200、100)点。然后,另一个“ L”创建一条到(170、200)的线。最后,“ z”告诉强制绘图笔关闭电流路径并最终确定形状。结果,我们得到一个三角形。

由于fabric.Path就像Fabric中的任何其他对象一样,我们也能够更改其某些属性。但是我们可以对其进行更多修改:

mounted() {
   var canvas = new fabric.Canvas("canvas",{backgroundColor:"#ddd"});
    var path = new fabric.Path("M 0 0 L 300 100 L 200 300 z");
    path.set({ fill: "red", stroke: "green", opacity: 0.5 });
    canvas.add(path);
  }

Introduction to Fabric.js. Part 1.(介绍Fabric.js第一部分)

出于好奇,让我们看一下稍微复杂一些的路径语法。您将看到为什么手动创建路径可能不是最好的主意。

mounted(){
		var canvas = new fabric.Canvas("canvas",{backgroundColor:"#ddd"});
  
    var path = new fabric.Path('M121.32,0L44.58,0C36.67,0,29.5,3.22,24.31,8.41\
c-5.19,5.19-8.41,12.37-8.41,20.28c0,15.82,12.87,28.69,28.69,28.69c0,0,4.4,\
0,7.48,0C36.66,72.78,8.4,101.04,8.4,101.04C2.98,106.45,0,113.66,0,121.32\
c0,7.66,2.98,14.87,8.4,20.29l0,0c5.42,5.42,12.62,8.4,20.28,8.4c7.66,0,14.87\
-2.98,20.29-8.4c0,0,28.26-28.25,43.66-43.66c0,3.08,0,7.48,0,7.48c0,15.82,\
12.87,28.69,28.69,28.69c7.66,0,14.87-2.99,20.29-8.4c5.42-5.42,8.4-12.62,8.4\
-20.28l0-76.74c0-7.66-2.98-14.87-8.4-20.29C136.19,2.98,128.98,0,121.32,0z');

canvas.add(path.set({ left: 100, top: 200 }));
}

哦,孩子,这是怎么回事?

嗯,“ M”仍然代表“移动”命令,因此笔在“ 121.32,0”点开始绘制过程。然后是“ L”命令,将其设置为“ 44.58,0”。到目前为止,一切都很好。下一步是什么? “ C”命令,代表“三次贝塞尔曲线”。它使笔绘制贝塞尔曲线从当前点到“ 36.67,0”之一。它以“ 29.5,3.22”作为控制点在行的开头,并以“ 24.31,8.41”作为控制点在行的末尾。然后,接下来是许多其他三次贝塞尔曲线命令,这些命令最终创建了一个漂亮的箭头形状。

很有可能,您不会直接使用此类“野兽”。相反,您可能想使用fabric.loadSVGFromStringfabric.loadSVGFromURL方法之类的方法来加载整个SVG文件,并让Fabric的SVG解析器完成遍历所有SVG元素并创建相应的Path对象的工作。

说到整个SVG文档,虽然Fabric的Path通常代表SVG <path>元素,但是SVG文档中经常出现的一组路径被表示为Groups(fabric.Group实例)。可以想象,Group只是一组Paths和其他对象。而且由于fabric.Group继承自fabric.Object,因此可以像其他任何对象一样将其添加到画布,并以相同的方式进行操作。

就像使用Paths一样,您可能不会直接使用它们。但是,如果您在解析SVG文档后偶然发现一个文档,您将确切知道它是什么以及它的作用。

Afterword(后记)

我们仅介绍了Fabric可能实现的功能。现在,您可以轻松创建任何简单的形状,复杂的形状,图像;将它们添加到画布上,然后以所需的任何方式进行修改(positions、dimensions、angles、colors、strokes、opacity),即可为其命名。

在本系列的下一部分中,我们将研究与groups的合作。动画片;文本; SVG解析,渲染,序列化;事件;图像滤镜;和更多。

同时,请随时查看带注释的演示基准,加入Google小组其他地方的讨论,或者直接获取文档Wiki源代码。 尝试使用Fabric玩得开心!

希望您喜欢。

阅读Part 2/第二部分

上一篇:翻译--Introduction to Authentication with ASP.NET Core


下一篇:Android数据库(SQLite)框架(3)——使用LitePal建立表关联