Fabric入门Fabric.js是一个强大的Javascript库,它使使用HTML5画布变得轻而易举。Fabric为
Fabric.js是一个强大的Javascript库,它使使用HTML5画布变得轻而易举。Fabric为画布提供了一个缺失的对象模型,以及SVG解析器、交互层和一整套其他不可或缺的工具。
为什么需要Fabric
Canvas
让我们可以在网页上创建一些绝对令人惊叹的图形。但是它提供的API很低级,令人失望。如果我们只是想在画布上画几个基本的形状,然后忘记它们,这是一回事。但是一旦需要任何形式的交互,在任何点改变图片,或者绘制更复杂的形状,情况就会发生巨大变化。
Fabric旨在解决这个问题。
原生画布方法只允许我们发出简单的图形命令,盲目地修改整个画布位图。想画一个矩形吗?使用fillRect(左,顶,宽,高)。想要划清界限吗?使用moveTo(左,上)和lineTo(x, y)的组合。这就好像我们用画笔在画布上作画,在上面分层越来越多的油,几乎没有控制。
Fabric没有在这么低的层次上进行操作,而是在原生方法之上提供了简单但功能强大的对象模型。它负责画布状态和渲染,让我们直接处理“对象”。
让我们看一个简单的示例来演示这种差异。假设我们想在画布上画一个红色矩形。下面是我们如何使用原生<canvas>
API来做这件事。
// 获取dom元素
var canvasEl = document.getElementById('canvas');
// 获取2d上下文(前面提到的“位图”)
var ctx = canvasEl.getContext('2d');
// 设置上下文的填充颜色
ctx.fillStyle = 'red';
// 在100,100点处创建矩形,尺寸:20x20
ctx.fillRect(100, 100, 20, 20);
现在,让我们看看在Fabric中做同样的事情:
// 在原生画布元素周围创建一个包装器
var canvas = new fabric.Canvas('canvas');
// 创建一个矩形对象
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// 将矩形添加到画布上
canvas.add(rect);
在这一点上,大小几乎没有区别—两个示例非常相似。但是,已经可以看到使用canvas的方法有多么不同。使用原生<canvas>
方法,我们对上下文进行操作——一个表示整个画布位图的对象。在Fabric中,我们对对象进行操作——实例化它们,改变它们的属性,并将它们添加到画布中。可以看到,这些对象是Fabric领域的一等公民。
但是渲染普通的红色矩形太无聊了。可以用它做点有趣的事!也许,稍微旋转一下?
我们试试45度。首先,使用原生<canvas>
方法:
var canvasEl = document.getElementById('canvas');
var ctx = canvasEl.getContext('2d');
ctx.fillStyle = 'red';
ctx.translate(100, 100);
ctx.rotate(Math.PI / 180 * 45);
ctx.fillRect(-10, -10, 20, 20);
现在使用Fabric:
// 在原生画布元素周围创建一个包装器
var canvas = new fabric.Canvas('canvas');
// 创建一个角为45的矩形
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20,
angle: 45
});
// 将矩形添加到画布上
canvas.add(rect);
这里发生了什么?
在Fabric中所要做的就是将对象的“角度”值更改为45。然而,使用原生<canvas>
方法,事情变得更加“有趣”。记住,我们不能对对象进行操作。相反,我们调整整个画布位图(ctx)的位置和角度。(ctx.变换,ctx.旋转)以适应我们的需要。然后我们再次绘制矩形,但记住适当地偏移位图(-10,-10),这样它仍然在100,100点呈现。作为额外的练习,我们必须在旋转画布位图时将角度转换为弧度。
相信已经开始明白Fabric存在的确切原因以及它隐藏了多少低级的样板文件。
但是让我们来看另一个例子——跟踪画布状态。
如果在某个时候,我们想要将这个熟悉的红色矩形移动到画布上的一个稍微不同的位置呢?如果不能操作对象,我们该怎么做呢?我们会在画布位图上调用另一个fillRect吗?
不完全是。调用另一个fillRect命令实际上是在画布上已经绘制的东西的顶部绘制一个矩形。还记得我之前说过用刷子画画吗?为了“移动”它,我们需要首先擦除之前绘制的内容,然后在新位置绘制矩形。
var canvasEl = document.getElementById('canvas');
...
ctx.strokRect(100, 100, 20, 20);
...
// 擦除整个画布区域
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
ctx.fillRect(20, 50, 20, 20);
如何用Fabric来实现这一点呢?
var canvas = new fabric.Canvas('canvas');
...
canvas.add(rect);
...
rect.set({ left: 20, top: 50 });
canvas.renderAll();
注意一个非常重要的区别。使用Fabric,我们不再需要在尝试“修改”任何内容之前擦除内容。我们仍然使用对象Ract,简单地改变它们的属性,然后重新渲染画布以获得“新的画面”。
Objects 类
已经看到了如何通过实例化fabric.Rect
来处理矩形。矩形的构造函数。当然,Fabric也包括所有其他的基本形状——圆形、三角形、椭圆等等。所有这些都作为fabric“命名空间”暴露在fabric下。fabric.Circle
, fabric.Triangle
, fabric.Ellipse
等。
Fabric中提供的7种基本形状:
想画一个圆吗?只需创建一个圆形对象,并将其添加到画布。与其他基本形状相同:
// 在原生画布元素周围创建一个包装器
var canvas = new fabric.Canvas("canvas");
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);
这里有一个绿色的圆圈,画在100,100的位置蓝色的三角形画在50,50的位置。
操纵物体
创建图形对象——矩形、圆形或其他东西——当然只是一个开始。在某些时候,我们可能需要修改这些对象。也许某些动作需要触发状态变化,或者播放某种动画。或者我们可能希望在某些鼠标交互上更改对象属性(颜色、不透明度、大小、位置)。
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
方法设置它们。让我们试着改变一些红色矩形的属性:
var canvas = new fabric.Canvas('canvas');
// 创建一个矩形对象
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 30,
height: 30
});
// 将矩形添加到画布上
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);
首先,我们将“填充”值设置为“红色”,本质上是使物体呈红色。下一个语句设置了" strokeWidth "和" stroke "的值,给矩形一个5px的淡绿色描边。最后,我们将改变“angle”和“flipY”属性。请注意,这3条语句使用的语法略有不同。
默认选项
在这一点上,可能会问-当我们创建一个对象而不传递任何“配置”对象时会发生什么。它还具有这些性质吗?
当然。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
我们的矩形有一组默认属性。它的位置为0,0,颜色为黑色,完全不透明,没有描边和尺寸(宽度和高度为0),因为没有尺寸,我们无法在画布上看到它。但是给它的宽度/高度任何正值肯定会显示一个黑色矩形在画布的左上角。
层次和继承
Fabric objects不是相互独立存在的。他们形成了一个非常精确的层级。
大多数对象都继承自根fabric.Object。
这种继承允许我们在结构上定义方法。对象,并在所有子“类”中共享它们。例如,如果你想在所有对象上使用getAngleInRadians方法,你只需在fabric.Object.prototype上创建它:
fabric.Object.prototype.getAngleInRadians = function() {
return this.get('angle') / 180 * Math.PI;
};
var rect = new fabric.Rect({ angle: 45 });
rect.getAngleInRadians(); // 0.785...
var circle = new fabric.Circle({ angle: 30, radius: 10 });
circle.getAngleInRadians(); // 0.523...
circle instanceof fabric.Circle; // true
circle instanceof fabric.Object; // true
方法立即在所有实例上可用。
在个别项目中,使用原型来获得自定义渲染和行为是非常常见的。
Canvas
详细地介绍了对象,让我们回到Canvas。
在所有的Fabric示例中,你可以看到的第一件事是创建画布对象——new fabric.Canvas('...')
。fabric.Canvas作为<canvas>
元素的包装器,负责管理特定画布上的所有fabric对象。它接受一个元素的id,并返回一个fabric.Canvas
的实例。
我们可以在上面add
对象,从上面引用对象,或者删除对象:
var canvas = new fabric.Canvas('canvas');
var rect = new fabric.Rect();
canvas.add(rect); // 添加对象
canvas.item(0);
canvas.getObjects(); // 获取画布上的所有对象(rect将是第一个也是唯一的)
canvas.remove(rect); // 删除以前添加的矩形
而管理对象是fabric.Canvas
的主要目的。画布还可以作为主要配置。需要为整个画布设置背景颜色或图像?将所有内容剪贴到某个区域?设置不同的宽度/高度?指定画布是否交互式?所有这些选项(以及其他选项)都可以在fabric.Canvas
上设置。在创建时或之后:
var canvas = new fabric.Canvas('c', {
backgroundColor: 'rgb(100,100,200)',
selectionColor: 'blue',
selectionLineWidth: 2
// ...
});
// or
var canvas = new fabric.Canvas('c');
canvas.setBackgroundImage('http://...');
canvas.onFpsUpdate = function(){ /* ... */ };
// ...
交互性
在讨论画布元素的同时,让我们来谈谈交互性。Fabric的一个独特特性——它是内置的——是在我们刚才看到的所有方便的对象模型之上的一层交互性。
对象模型的存在是为了允许对画布上的对象进行编程访问和操作。但在外部,在用户层面上,有一种方法可以通过鼠标来操作那些对象(或触摸,在触摸设备上)。只要你通过new fabric.Canvas('…')
初始化画布,就可以选择对象,拖动它们,缩放或旋转它们,甚至组在一起在一个块中操作!
如果我们想让用户允许在画布上拖拽一些东西——比如说一个图像——我们所需要做的就是初始化画布并在上面添加一个对象。不需要额外的配置或设置。
为了控制这种交互性,我们可以在画布上使用Fabric的“selection”布尔属性,并结合单个对象的“selectable”布尔属性。
var canvas = new fabric.Canvas('c');
...
canvas.selection = false; // 禁用组选择
rect.set('selectable', false); // 使对象不可选择
但是如果你根本不想要这样的交互层呢?如果是这样的话,你可以替换fabric.Canvas
与fabric.StaticCanvas
。初始化的语法是完全相同的;你只使用StaticCanvas
而不是Canvas
。
var staticCanvas = new fabric.StaticCanvas('c');
staticCanvas.add(
new fabric.Rect({
width: 10, height: 20,
left: 100, top: 100,
fill: 'yellow',
angle: 30
}));
这创建了一个“轻量级”版本的canvas,没有任何事件处理逻辑。请注意,您仍然有一个完整的对象模型要处理—添加对象、删除或修改对象,以及更改任何画布配置—所有这些都仍然有效。只有事件处理没有了。
Images
在画布上添加矩形和圆形很有趣,但我们为什么不尝试一些图像呢?正如您现在所想象的那样,Fabric使这变得很容易。让我们实例化fabric.Image
对象,并将其添加到画布:
<canvas id="c"></canvas>
<img src="my_image.png" id="my-image">
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-image');
var imgInstance = new fabric.Image(imgElement, {
left: 100,
top: 100,
angle: 30,
opacity: 0.85
});
canvas.add(imgInstance);
注意我们是如何将图像元素传递给fabric.Image
的。图像的构造函数。这将创建一个fabric.Image
实例。看起来与文档中的图像一样的图像。此外,我们立即将左/顶值设置为100/100,角度设置为30,不透明度设置为0.85。一旦添加到画布上,图像将呈现在100,100位置,30度角,并且是微透明的!
如果文档中没有图像,只有图像的URL呢?没问题。让我们看看如何使用fabric.Image.fromURL
。
fabric.Image.fromURL('my_image.png', function(oImg) {
canvas.add(oImg);
});
看起来很简单,不是吗?只需使用图像的URL调用fabric.Image.fromURL,并在加载和创建图像时给它一个回调。回调函数接收已经创建的结构。图像对象作为第一个参数。此时,你可以将其添加到canvas中,或者先更改,然后添加到canvas中:
fabric.Image.fromURL('my_image.png', function(oImg) {
// 在将图像添加到画布之前,缩放图像并翻转它
oImg.scale(0.5).set('flipX', true);
canvas.add(oImg);
});
Paths
我们先看了简单的形状,然后是图像。更复杂、更丰富的形状和内容呢?
这是一对强大的结合——路径和群组。
Fabric中的路径表示形状的轮廓,可以用其他方式填充、描边和修改。路径由一系列命令组成,本质上模仿一支笔从一个点到另一个点。在“移动”、“直线”、“曲线”或“弧线”等命令的帮助下,路径可以形成令人难以置信的复杂形状。在路径组(PathGroup’s)的帮助下,可能性会更大。
Fabric中的路径与SVG 元素非常相似。它们使用相同的命令集,可以从元素创建它们,并序列化到它们中。稍后我们将更仔细地研究序列化和SVG解析,但现在值得一提的是,您可能很少手工创建Path实例。相反,您将使用Fabric的内置SVG解析器。但是为了理解Path对象是什么,让我们尝试手工创建一个简单的Path对象:
var canvas = new fabric.Canvas('c');
var path = new fabric.Path('M 0 0 L 200 100 L 170 200 z');
path.set({ left: 120, top: 120 });
canvas.add(path);
我们是实例化fabric.Path
对象,向它传递一串路径指令。虽然它看起来很神秘,但实际上很容易理解。“M”代表“移动”命令,告诉隐形笔移动到0,0点。“L”代表“线”,使钢笔画一条线到200,100点。然后,另一个“L”创建了到170,200的一行。最后,“z”告诉绘图笔关闭当前路径并最终确定形状。结果,我们得到一个三角形。
因为fabric.Path
就像Fabric中的其他对象一样,我们也可以改变它的一些属性。但是我们可以对它进行更多的修改:
...
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);
出于好奇,让我们看一下稍微复杂一点的路径语法。您将看到为什么手工创建路径可能不是最好的主意。
...
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”1的贝塞尔曲线。它以“29.5,3.22”作为起始控制点,以“24.31,8.41”作为结束控制点。然后,在这整个过程之后,还有十几个其他的三次贝塞尔命令,它们最终创建了一个漂亮的箭头形状。
很有可能,你不会直接与这些“畜牲”打交道。相反,你可能想用Fabric之类的东西。fabric.loadSVGFromString
或fabric.loadSVGFromURL
方法加载整个SVG文件,并让Fabric的SVG解析器遍历所有SVG元素并创建相应的Path对象。
说到整个SVG文档,虽然Fabric的Path通常表示SVG < Path >
元素,但SVG文档中经常出现的路径集合表示为Groups (Fabric)。组实例)。正如你所想象的,Group只是一组路径和其他对象。因为Fabric。Group继承自fabric。对象,它可以像任何其他对象一样添加到画布上,并以相同的方式进行操作。
就像使用路径一样,您可能不会直接使用它们。但是如果您在解析SVG文档之后偶然发现了一个,您就会确切地知道它是什么以及它的用途。
转载自:https://juejin.cn/post/7404749173436694568