Fabric.js深入学习指南
在Fabric.js中,几乎所有的2d图形直接或间接继承自 Object 类,那么如果我们不用其自带的2d图形,而是自建图形,要怎么应用 Fabric.js 中的方法呢?Fabric.js 提供了方法解决这个问题// 创建一个自定义子类// 自定义属性},// 将自定义属性添加到序列化对象中});},// 自定义渲染逻辑},});一个简单的自定义类主要要修改3个地方,分别是:initialize:
前言
近期在学习 Fabric.js,Fabric.js 的官网文档可谓是一言难尽。没有中文翻译只能算最小的问题,代码举例少的可怜,文档结构十分不友好,搜索功能也不尽人意,很难让人有看下去的耐心。
网络上倒是有挺多不错的学习文章,但是大部分都没有深入介绍,比如在 Fabric.js 第一章的 controls 对象,在我看的文章中都没有见到对其的介绍。
综上所述,在此记录一些个人学习见解,以及通过一些典型案例介绍学习 Fabric.js 的方法。
案例代码有不懂的可以见git:https://github.com/pengzhijian/fabric-learing-demo (强烈推荐,里面还有文章没列出来的部分测试用例)
一、安装
yarn add fabric -S
#or
npm i fabric -S
或者下载js到本地使用官网下载链接
注意:用npm下载的一定要下 v5.0版本的,v6.0版本bug很多,而且更新的很多内容和官方文档完全对不上,关键是还没!有!教!程!
建议下载到本地方便查看修改源码。
二、使用
先用基础代码看看效果:
<canvas id="canvas" width="500" height="500"></canvas>
// 创建一个fabric实例
const canvas = new fabric.Canvas("canvas");
// 创建一个矩形对象
const rect = new fabric.Rect({
left: 50,
top: 50,
width: 50,
height: 50,
fill: "red"
});
const triangle = new fabric.Triangle({
left: 100,
top: 100,
width: 80,
height: 40,
fill: "blue",
});
// 添加矩形和三角形到画布
canvas.add(rect, triangle);
从上图直观可见,Fabric.js由3部分组成,分别是 canvas (画布载体),Object(所画的图形),Controls(选中时的控制器)。而这3项也是 Fabric.js 的几大核心类,下文将着重讲解这几个类的一些应用。
一. Object 类
Fabric.js 提供了很多基础图形:
- Circle 圆
- Ellipse 椭圆
- Line 线
- Polygon 多边形
- Polyline 折线
- Rect 矩形
- Triangle 三角形
- 文字
- 图片
所有基础图形都是 Object类的子类
Object 类提供了绝大多数基础图形的属性和方法,比如 set、get方法,left、top (位置)、width、height (宽高)、fill (填充颜色)、stroke (描边)、angle (角度)、opacity (不透明度)、flip (翻转属性)等。具体有哪些属性方法可以参考官网。
注意:官网列举了绝大多数方法和属性,但是并未列全,比如最常用的 set 方法就没有列出来,对于此类情况可以参考我上篇文章通过控制台学习修改Fabric.js源码。
可以在我的git仓库项目中查看修改属性值:
这些属性和方法的使用很简单,其他文章都有讲解,此处只举例最简单的用法。
rect.set('fill', 'yellow'); // 修改方块填充颜色
traingle.set('angle', 45); // 修改三角形角度
fabric.Object.prototype.opacity = '0.5'; // 修改所有基础对象透明度
canvas.renderAll(); // 重新渲染
当我们要修改通用的属性方法时,我们可以通过修改 Object 父类实现,如上面的修改透明度。
除此之外我们还可以添加通用属性或方法给 Object,达到修改所有子类的目的,此处举个简单实用的例子讲解。
1. 添加层级方法 getLevel
在 Fabric.js 中是有层级的概念的,在询问过gpt后,得知 Fabric.js 有很多操作层级的方法,但是没有直接的层级属性。
-
Canvas对象层级操作方法:
canvas.bringToFront(object)
: 将指定对象移到最前面。canvas.sendToBack(object)
: 将指定对象移到最后面。canvas.bringForward(object)
: 将指定对象向前移动一个层级。canvas.sendBackwards(object)
: 将指定对象向后移动一个层级。canvas.moveTo(object, index)
: 将指定对象移动到指定的层级索引。
-
Object对象层级操作方法:
object.bringToFront()
: 将当前对象移到最前面。object.sendToBack()
: 将当前对象移到最后面。object.bringForward(intersecting)
: 将当前对象向前移动一个层级,若intersecting
为true则会跳过所有交叉的对象。object.sendBackwards(intersecting)
: 将当前对象向后移动一个层级,若intersecting
为true则会跳过所有交叉的对象。object.moveTo(index)
: 将当前对象移动到指定的层级索引。
想要获取具体图形的层级一般使用 canvas.getObjects().indexOf(xxx) 。
显然,这个有点麻烦,我们自己加一个 level 方法让其直接显示对象的层级。
// 新增 level 方法
fabric.Object.prototype.getLevel = function() {
return this.canvas.getObjects().indexOf(this);
}
// 添加到画布
canvas.add(rect, circle, triangle);
// 调用level方法
console.log(rect.getLevel()); // 0
console.log(triangle.getLevel()); // 1
如果觉得这个还是太麻烦了,甚至可以添加一个level属性来显示层级(会稍微复杂一点)。
2. 添加层级属性 level
此处需要修改源码,具体可参考我上篇文章 通过控制台学习修改Fabric.js源码。
核心思路:
- 先在 Object 类添加level属性。
- 写一个方法 setAllLevel 去设置canvas下的所有对象的 level 属性。
- 重写 canvas 的 renderAll 方法,在方法的最后加上2中的 setAllLevel方法。
添加 level 属性
// 新增 level 属性
fabric.Object.prototype.level = 0
// 添加对象到画布
canvas.add(rect, circle, triangle);
console.log(rect.level); // 0
console.log(triangle.level); // 0
新增 setAllLevel 方法:
// Canvas 类新增 setAllLevel 方法
canvas.__proto__.__proto__.setAllLevel = function() {
this.getObjects().forEach((obj, index) => {
obj.level = index;
})
}
重写覆盖源码中 canvas 的 renderAll 方法:
// Canvas 类 重写renderAll方法
canvas.__proto__.renderAll = function () {
console.log('renderAll')
!this.contextTopDirty ||
this._groupSelector ||
this.isDrawingMode ||
(this.clearContext(this.contextTop), (this.contextTopDirty = !1)),
this.hasLostContext && this.renderTopLayer(this.contextTop);
var t = this.contextContainer;
// 偷个懒,直接用 setTimeout 0 延迟执行
setTimeout(() => {
this.setAllLevel();
})
return this.renderCanvas(t, this._chooseObjectsToRender()), this;
}
注意:拖动是不会改变物体层级的,只有用上面列出的层级修改方法才会改变层级。
注意:直接在 canvas.add(rect, circle, triangle); 后console level都显示0,因为我偷懒延迟了,在渲染所有图形后再设置level,稍微延迟一点即可得到正确的level属性。
在控制台看结果:
Object 类还有很多其他功能,比如通用事件监听,动画功能等,官网讲的很详细,论坛里也有很多写的很好的文章,此处就不再做介绍了。
3. 添加自定义子类
在Fabric.js中,几乎所有的2d图形直接或间接继承自 Object 类,那么如果我们不用其自带的2d图形,而是自建图形,要怎么应用 Fabric.js 中的方法呢?
Fabric.js 提供了 fabric.util.createClass 方法解决这个问题
先看看一个自定义子类的结构:
// 创建一个自定义子类
const customClass = fabric.util.createClass(fabric.Object, {
type: "customClass",
initialize: function (options) {
options || (options = {});
this.callSuper("initialize", options);
// 自定义属性
},
toObject: function () {
return fabric.util.object.extend(this.callSuper("toObject"), {
// 将自定义属性添加到序列化对象中
});
},
_render: function (ctx) {
this.callSuper("_render", ctx);
// 自定义渲染逻辑
},
});
一个简单的自定义类主要要修改3个地方,分别是:
- initialize : 添加的自定义属性方法放这
- toObject: 将自定义属性添加到序列化对象中,方便canvas记录
- _render: 处理自定义渲染逻辑
此处举一个简单的例子,写一个自定义图形类 Map 网格图:
新增绘制网格图的方法 initMap:
/*
绘制网格横竖线map
*/
function initMap(options, ctx) {
const { gridNumX, gridNumY, width, height, fill, left, top } = options;
ctx.save();
ctx.translate(-width / 2, -height / 2)
// 开始路径并绘制线条
ctx.beginPath();
// 设置线条样式
ctx.lineWidth = 1;
ctx.strokeStyle = fill;
// 开始绘制横线
for (let i = 0; i < gridNumY + 1; i++) {
// 注意要算线的宽度,也就是后面那个+i
ctx.moveTo(0, height / gridNumY * i);
ctx.lineTo(width, height / gridNumY * i);
ctx.stroke();
}
// 开始绘制竖线
for (let i = 0; i < gridNumX + 1; i++) {
ctx.moveTo(width / gridNumX * i, 0);
ctx.lineTo(width / gridNumX * i, height);
ctx.stroke();
}
ctx.restore();
}
创建 Map 子类:
// 创建一个自定义子类
const Map = fabric.util.createClass(fabric.Object, {
type: "Map",
initialize: function (options) {
options || (options = {});
this.callSuper("initialize", options);
this.set("gridNumX", options.gridNumX || "");
this.set("gridNumY", options.gridNumY || "");
},
toObject: function () {
return fabric.util.object.extend(this.callSuper("toObject"), {
gridNumX: this.get("gridNumX"),
gridNumY: this.get("gridNumY"),
});
},
_render: function (ctx) {
this.callSuper("_render", ctx);
initMap({
...this
}, ctx)
},
});
新建 map 实例并添加到canvas:
const map = new Map({
left: 100,
top: 100,
label: "test",
fill: "#faa",
width: 100,
height: 100,
gridNumX: 4,
gridNumY: 3
});
const map2 = new Map({
left: 300,
top: 100,
label: "test",
fill: "green",
width: 200,
height: 300,
gridNumX: 2,
gridNumY: 5
});
// 将所有图形添加到 canvas 中
canvas.add(map, map2);
如图所示,成功创建了可复用的自定义图形,而且能够使用 Object 类的功能。
二、Controls 类
当 Fabric.js 画布中的对象被选中时,对象的四周会出现一些小方框,我们可以选中方块进行缩放、旋转操作。
这些被选中后出现的小方框被称为控制器 Controls。fabric.Object 类中提供了可以修改的 controls 对象,我们可以通过修改该对象达到修改控制器样式、修改控制器功能、亦或者添加新控制器的目的。
1. 修改 controls
修改之前我们先在控制台看看 Object 的 controls 属性有什么:
从图中可得知,controls 默认有9个属性,对应着对象被选中的9个小方框,其中的 t 是 top 的缩写,l 是 left 的缩写, m 是 middle 的缩写, r 是 right 的缩写 b 是 bottom 的缩写。我们可以通过名字快速得知该控制器的位置。
1.1 删除 control
先删除一个控制器试试水。删除右下角的控制器:
// 创建一个矩形对象
let rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 100,
height: 100,
});
// 创建一个矩形对象
let circle = new fabric.Circle({
left: 200,
top: 200,
fill: 'blue',
radius: 50,
});
delete rect.controls.br; // 删除方块右下角的控制点
// 添加矩形到画布
canvas.add(rect, circle);
修改后发现方块的右下角控制器没了,但是圆还有合并后的图形右下角控制器都没了。在 Fabric.js 中,当修改对象的 controls
时,默认情况下所有相同类型的对象会共享相同的 controls
。要确保只修改特定对象的 controls
而不影响其他对象,需要克隆默认的 controls
并在克隆的基础上进行修改。
// 克隆默认的controls
rect.controls = fabric.util.object.clone(fabric.Rect.prototype.controls);
// 删除矩形右下角的控制点
delete rect.controls.br;
1.2 修改 control 样式
Fabric.js 官方提供了一些修改 controls 样式的属性:
// 创建一个矩形对象
let rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 100,
height: 100,
cornerStyle: 'circle', // 设置控制点样式为圆形
cornerColor: 'blue', // 设置控制点颜色
cornerSize: 10, // 设置控制点大小
hasBorders: true, // 设置是否显示边框
borderColor: 'purple', // 设置控制线的颜色
borderDashArray: [5, 5], // 设置控制线的虚线样式
borderScaleFactor: 0, // 设置控制线的宽度
borderOpacityWhenMoving: 0.9, // 设置控制线的透明度
});
显而易见的是,官方提供的样式修改依旧难看,而且可供修改的属性很少。
要想自定义修改控制器样式,先需要了解 Control 类。所有控制器都继承自 Control 对象,在 Control 对象中的 render 函数负责渲染控制器图形。
要想修改样式,直接修改 render 方法即可:
将左上角修改为小圆点
// 克隆默认的controls
circle.controls = fabric.util.object.clone(fabric.Rect.prototype.controls);
// 为了不所有对象全部修改,同样需要对其tl控制点进行克隆
circle.controls.tl = fabric.util.object.clone(fabric.Object.prototype.controls.tl);
// 将左上角控制点样式修改为一个实心小圆点
circle.controls.tl.render = function(ctx, left, top, styleOverride, fabricObject) {
ctx.save();
ctx.fillStyle = 'rgb(78, 172, 189)';
ctx.beginPath();
ctx.arc(left, top, 8, 0, Math.PI * 2, true);
ctx.fill();
ctx.restore();
}
1.3 修改 control 功能
Control 类中提供了鼠标拖动和鼠标按下抬起的监听事件,我们可以通过修改这些事件达到修改功能的目的。
先在控制台中看看这些事件方法:
上图可以看到 ml 控制器只用了 actionHandler 方法,且方法的参数有4个,接下来尝试修改 actionHandler 方法:
circle.controls.tl.actionHandler = function(eventData, transform, x, y) {
console.log('自定义控制点的行为', eventData, transform, x, y);
// 自定义控制点的行为
// 这里可以做一些自定义的操作,比如修改圆的颜色
transform.target.set('fill', `rgb(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255})`);
canvas.renderAll();
}
代码修改后拖动左上角不再是缩放,而是改变成随机颜色。
2. 新增自定义 controls
上面的例子都是修改原来就有的 control ,一般而言我们会去修改他们的样式,但是对于功能更倾向于新增控制点,而不是修改原来的 control。接下来我们尝试新增一个 control。
control 类的属性方法很少,文档理所当然的没有讲解,可以自行在控制台查看,以下列举一些常改的属性:
// 创建一个控件
const customControl = new fabric.Control({
// 相对于中心点的偏移量,1指偏移一整个width
x: 1,
y: 0,
cursorStyle: 'pointer',
// 鼠标按下事件
mouseDownHandler: (eventData, transform, x, y) => {
},
// 鼠标滑动事件
actionHandler: (eventData, transform, x, y) => {
},
// 控件名字
actionName: 'test',
// 自定义渲染
render: function(ctx, left, top, styleOverride, fabricObject) {
}
});
举一个简单的删除按钮例子讲解:
2.1 自定义删除控件
编写思路:1. 位置在右上角 2. 图形是一个红色小x 3. 点击弹窗提示是否删除,是的话就删掉。
只要对Control属性做简单修改即可:
// 创建一个控件
const deleteControl = new fabric.Control({
// 相对于中心点的偏移量,1指偏移一整个width
x: 0.7,
y: -0.7,
sizeX: 30, // 控件的宽度
sizeY: 30,
cursorStyle: 'pointer',
// 鼠标按下事件
mouseDownHandler: (eventData, transform, x, y) => {
if (confirm('确定删除?')) {
canvas.remove(transform.target);
}
canvas.renderAll();
},
// 控件名字
actionName: 'delete',
// 自定义渲染
render: function(ctx, left, top, styleOverride, fabricObject) {
ctx.save();
ctx.translate(left, top);
ctx.fillStyle = 'red';
ctx.font = '20px Arial';
ctx.fillText('X', -10, 10); // 确保文字在控件的中心
ctx.restore();
}
});
// 将控件添加到圆的controls对象中
circle.controls.delete = deleteControl
如图所示,成功添加了删除按钮:
三、Canvas 类
Canvas类作为图形的底座有很多功能,最常用的有 renderAll (渲染所有图形),add(添加图形对象),remove(删除图形对象)等方法。大部分的用法都比较简单,有不懂用什么方法的直接问gpt即可。
此处仅介绍一些关于Canvas类典型案例。
1. 平移和缩放
平移和缩放相信熟悉 canvas 原生属性的小伙伴都不陌生,这俩功能都不难实现。Fabric.js 提供了专门的方法,让平移和缩放功能变得更加简单。
1.1 缩放
Canvas类缩放相关方法有三个:
-
setZoom(zoom: number) :
- 设置整个画布的缩放比例。
- 例如:
canvas.setZoom(2);
将画布的缩放比例设置为 2 倍。
-
zoomToPoint(point: fabric.Point, value: number) :
- 以特定点为中心缩放画布。
- 例如:
canvas.zoomToPoint(new fabric.Point(100, 100), 2);
将画布以 (100, 100) 点为中心缩放到 2 倍。
-
getZoom() :
- 获取当前画布的缩放比例。
- 例如:
let currentZoom = canvas.getZoom();
将当前缩放比例赋值给currentZoom
。
举个简单的例子,滚轮滚动时光标中心缩放:
canvas.on("mouse:wheel", function (option) {
// 判断是放大还是缩小
const delta = option.e.deltaY;
let zoom = canvas.getZoom();
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
// 在鼠标位置缩放
canvas.zoomToPoint({ x: option.e.offsetX, y: option.e.offsetY }, zoom);
option.e.preventDefault();
option.e.stopPropagation();
});
1.2 平移
Canvas类平移相关的方法有:
-
absolutePan(point: fabric.Point) :
- 绝对移动画布视口到指定的点。
- 例如:
canvas.absolutePan(new fabric.Point(100, 100));
将画布的视口移动到 (100, 100)。
-
relativePan(point: fabric.Point) :
- 相对当前视口的位置移动画布的视口。
- 例如:
canvas.relativePan(new fabric.Point(50, 50));
将画布的视口相对当前视口位置向右下方移动 50 像素。
-
viewportTransform:
- 一个 6 元素数组,表示画布的当前视口变换矩阵。
- 可以直接操作此数组来进行高级平移和变换。
- 例如:
canvas.viewportTransform[4] = 100; canvas.viewportTransform[5] = 100;
将画布的视口移动到 (100, 100)。
-
setViewportTransform(transform: number[]) :
- 设置画布的视口变换矩阵。
- 参数含义:[scaleX, skewX, skewY, scaleY, translateX, translateY]
- 例如:
canvas.setViewportTransform([1, 0, 0, 1, 100, 100]);
将画布的视口变换设置为平移到 (100, 100)。
举个简单的例子,按下alt键拖动画布:
// 当鼠标按下时
canvas.on('mouse:down', function(option) {
const evt = option.e;
console.log(evt)
if (evt.altKey === true) { // 检查是否按下alt键
this.isDragging = true;
this.selection = false;
this.lastPosX = evt.clientX;
this.lastPosY = evt.clientY;
}
});
// 当鼠标移动时
canvas.on('mouse:move', function(option) {
if (this.isDragging) {
const e = option.e;
const vpt = this.viewportTransform;
vpt[4] += e.clientX - this.lastPosX;
vpt[5] += e.clientY - this.lastPosY;
this.requestRenderAll();
this.lastPosX = e.clientX;
this.lastPosY = e.clientY;
}
});
// 当鼠标松开时
canvas.on('mouse:up', function(option) {
// 在鼠标松开时重新计算所有对象的交互
this.setViewportTransform(this.viewportTransform);
this.isDragging = false;
this.selection = true;
});
2. 获取真实转换坐标
在图像处理的过程中,我们经常会用到坐标点信息,以便于进行一些交互操作。
此处举一个简单的例子,当鼠标点击时,在鼠标的位置创建一个方块对象:
// 当鼠标按下时
canvas.on('mouse:down', function(option) {
const evt = option.e;
// 创建一个小方块
this.add(new fabric.Rect({
left: evt.offsetX,
top: evt.offsetY,
width: 50,
height: 50,
fill: 'yellow'
}))
this.renderAll();
});
从上图可见,当canvas未平移或缩放时,可以很简单的获取相应点位置,但是一但平移或者缩放后,鼠标点的位置就全乱了。Fabric.js 提供了 transformPoint 方法解决这一问题。
-
fabric.util.transformPoint(Point, transform):
- 将Canvas坐标点转换为视口坐标点
- 例如:fabric.util.transformPoint(new fabric.Point(100, 100), canvas.viewportTransform),将视口的(100,100)坐标点转化为平移缩放后的坐标点。
-
Canvas.getPointer(event):
- 用于获取事件(如鼠标或触摸事件)发生时相对于画布的坐标。它考虑了当前视口的变换(包括平移和缩放),因此可以正确地将鼠标或触摸事件的屏幕坐标转换为画布坐标。
修改代码:
// 当鼠标按下时
canvas.on('mouse:down', function(option) {
const evt = option.e;
// 用transformPoint创建一个小方块
// 注意 transformPoint 作用是将一个坐标从一个坐标系转换到另一个坐标系
// 由于这里的将按下的视口坐标转换成 canvas画布坐标系,所以需要用 invertTransform 反转变换
this.add(new fabric.Rect({
left: fabric.util.transformPoint({ x: evt.offsetX, y: evt.offsetY }, fabric.util.invertTransform(canvas.viewportTransform)).x,
top: fabric.util.transformPoint({ x: evt.offsetX, y: evt.offsetY }, fabric.util.invertTransform(canvas.viewportTransform)).y,
width: 50,
height: 50,
fill: 'red'
}))
// 用getPointer创建一个小方块
const pointer = canvas.getPointer(evt);
console.log('potint, ', pointer)
this.add(new fabric.Rect({
left: pointer.x,
top: pointer.y,
width: 50,
height: 50,
fill: 'blue'
}))
this.renderAll();
});
注意 transformPoint 作用是将一个坐标从一个坐标系转换到另一个坐标系,由于这里的将按下的视口坐标转换成 canvas画布坐标系,所以需要用 invertTransform 反转变换。
如图所示,两种方法都成功在正确位置创建了方块。
3. 保存和导入导出
3.1 保存为图片
Fabric.js 提供了canvas.toDataURL 方法导出保存为图片:
function exportImage() {
const dataURL = canvas.toDataURL({
format: 'png', // 图片格式
quality: 1, // 图片质量
multiplier: 2, // 图片放大倍数
left: 0, // 裁剪区域左上角x坐标
top: 0, // 裁剪区域左上角y坐标
width: canvas.width, // 裁剪区域宽度
height: canvas.height, // 裁剪区域高度
cropX: 0, // 裁剪导出图片的起始 X 坐标
cropY: 0, // 裁剪导出图片的起始 Y 坐标
cropWidth: canvas.width, // 裁剪导出图片的裁剪宽度
cropHeight: canvas.height // 裁剪导出图片的裁剪高度
})
// 创建下载链接
let downloadLink = document.createElement('a');
downloadLink.href = dataURL;
downloadLink.download = 'canvas-image.jpg';
downloadLink.click();
}
保存图片通常很实用,因为大部分人使用画图的目的都是为了保存图片,但是对于开发者和绘图者来说,图片的资源占用太大了,而且一但存储为图片就意味着整个画布会被像素化,没有办法很好的进行二次开发。对于此种情况,Fabric.js提供了序列化字符串和svg的导入导出方法。
3.2 导出序列化字符串
Fabric 中序列化相关方法主要是 fabric.Canvas.toObject() 和 fabric.Canvas.toJSON(),两个方法的用法一样,只是 toJSON 进行了字符串压缩,toObject 没有。
由于在 ES5 中使用 JSON.stringify() 方法会隐式调用传递对象上的 toJSON 方法(如果该方法存在),Fabric 中的 canvas 实例具有 toJSON 方法,因此我们可以直接对其进行 stringify 调用:
const canvas = new fabric.Canvas('canvas');
JSON.stringify(canvas); // '{"objects":[],"background":"rgba(0, 0, 0, 0)"}'
在 Fabric 中的每次操作都会记录下来,比如新建一个方块:
// 创建一个矩形对象
let rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 100,
height: 100,
});
// 添加矩形到画布
canvas.add(rect);
console.log(JSON.stringify(canvas));
输出:
'{"version":"4.5.0","objects":[{"type":"rect","version":"4.5.0","originX":"left","originY":"top","left":100,"top":100,"width":100,"height":100,"fill":"red","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0}]}'
反序列化导入使用 Canvas.loadFromJSON 或 loadFromDatalessJSON 方法:
canvas.loadFromJSON('{"version":"4.5.0","objects":[{"type":"rect","version":"4.5.0","originX":"left","originY":"top","left":100,"top":100,"width":100,"height":100,"fill":"red","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0}]}')
3.3 导出svg格式
使用 toSVG 导出,导出使用 fabric.loadSVGFromURL 或 fabric.loadSVGFromString 方法
代码演示:
导出:
canvas.add(new fabric.Rect({
left: 50,
top: 50,
height: 20,
width: 20,
fill: 'green'
}));
console.log(canvas.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="20" height="20" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: green; opacity: 1;" transform="translate(50 50)" /></svg>'
导入:
fabric.loadSVGFromString('<?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="20" height="20" style="stroke: none; stroke-width: 1; stroke-dasharray: ; fill: green; opacity: 1;" transform="translate(50 50)" /></svg>', function (objects, options) {
// 创建一个Group对象,将加载的SVG对象组装到一起
let svgGroup = fabric.util.groupSVGElements(objects, options);
// 设置SVG对象的位置和缩放
svgGroup.set({
left: 100,
top: 100,
scaleX: 1,
scaleY: 1,
});
// 将SVG对象添加到画布
canvas.add(svgGroup);
// 渲染画布
canvas.renderAll();
});
总结
此文算是我几个星期学习Fabric.js的深入总结,Fabric.js的功能很强大,依旧有很多内容值得去探索,在学习期间发现了很多做的很好的Fabric.js二开项目。但是在深入了解和学习Fabric.js后,发现可能并不是所有项目都适合 Fabric.js(别人的二开项目做的太好了,我就不凑热闹了),所以我接下来的项目会直接用原生 Canvas 制作,此处学习算是告一段落了,希望我总结的教程对您有用,点赞多的话后续还会出新的教程(▽)
前置相关文章:
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)