一,fabric对象的理解
原生的canvas画图操作太过繁琐,fabric.js在原生canvas方法之上封装了一层,提供更为简单但功能强大的对象模型。它负责画布的状态和渲染,并让我们直接使用“对象”。
使用abric需要先理解它对象的概念。画布是一个对象,上面的各个图案也是对象。而原生的canvas则没有这么多对象。可以看下文对比,理解一下。
1,使用原生canvas和fabric创建图形对比
当我们使用canvas画图的操作:
<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>
看起来和原生js差不多,繁琐低效。再看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>
使用原生方法,我们可以在上下文ctx(一个表示整个画布位图的对象)上操作。在Fabric中,我们对对象进行操作——实例化它们,更改其属性并将其添加到画布。这些对象是Fabric土地上的一等公民。
2,使用原生canvas和fabric修改图形对比
按照1中的说法,原生canvas中我们只能获取到表示整个画布位图的ctx对象。那如果要移动画布中的图案呢?
原生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.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()//修改完之后重新渲染
}
画布上创建的矩形rect本身是一个对象,我们只要修改它的位置属性,就可以实现位置的移动了。理解了fabric的对象的概念,就可以开始使用了。
二,安装
npm i fabric --save
三,画布对象
<canvas width="400" height="400" id="canvas" style="border: 1px solid #ccc;"></canvas>
import { fabric } from 'fabric'
const canvas=new fabric.Canvas("canvas", {
backgroundColor: "pink",
selectionColor: "blue",
selectionLineWidth: 2,
// ...
}); // 这里传入的是canvas的id:c,第二个参数是对象,里面可以设置画布的一些属性
//获取canvas上的所有对象
canvas.getObjects();
//在画布上添加指定的对象
canvas.add(rect);
//删除指定的对象
canvas.remove(rect);
}
参数的设置可以查看官网对应的api:JSDoc: Class: Canvas (fabricjs.com)
四,创建基础对象
1,Fabric.js
基础对象
在 Fabric.js
中有以下七种预定义的基础对象。
fabric.Rect 矩形
fabric.Circle 圆形
fabric.Triangle 三角形
fabric.Ellipse 椭圆
fabric.Line 线型
fabric.Polyline 折线
fabric.Polygon 多边形
为了方便起见,我把这七种都写出来了:
//新建画布对象
const canvas = new fabric.Canvas("canvas", {
backgroundColor: "pink",
selectionColor: "blue",
selectionLineWidth: 2,
});
//矩形
const rect = new fabric.Rect({
left: 10,
top: 10,
width: 30,
height: 30,
fill: "yellow",
});
//圆形
const circle = new fabric.Circle({
left: 50,
top: 10,
radius: 15,
fill: "green",
});
//三角形
const triangle = new fabric.Triangle({
left: 90,
top: 10,
width: 30,
height: 30,
fill: "blue",
});
//椭圆
const ellipse = new fabric.Ellipse({
left: 130,
top: 10,
rx: 10, //短轴
ry: 15, //长轴
fill: "red", //填充颜色
stroke: "rgba(0, 0, 0, 1)", //边框颜色
});
//直线
const line = new fabric.Line([170, 25, 200, 25], {
//该数组四个值【起点x,起点y,终点x,终点y】
fill: "#5E2300", //填充颜色
stroke: "#5E2300", //笔触颜色
});
//折线
const polyline = new fabric.Polyline(
[
{
x: 200,
y: 10,
},
{
x: 250,
y: 50,
},
{
x: 250,
y: 180,
},
],
{
fill: "transparent",
stroke: "red", //笔触颜色
strokeWidth: 2,
}
);
// 多边形
const polygon = new fabric.Polygon(
[
{
x: 200,
y: 10,
},
{
x: 250,
y: 50,
},
{
x: 250,
y: 180,
},
],
{
left: 100,
top: 50,
fill: "red",
strokeWidth: 4,
stroke: "green",
}
);
canvas.add(rect, circle, triangle, ellipse, line, polyline, polygon); //它可以添加一个,也可以一次性添加多个
实现的效果:
2,基础对象的常见属性
上文创建了其中基础对象,他们有一些属性参数,如下所示:
fill: "#5E2300", //填充颜色
stroke: "#5E2300", //笔触颜色
scaleX: 4,//x轴放大倍数
scaleY: 4,//y轴放大倍速
strokeWidth: 4, //笔触宽度
selectable: true, //是否可被选中
transparentCorners: false, //选中后的控制手柄的是否透明
cornerColor: "blue", //选中后的控制手柄颜色
hasControls: true, //选中时是否可以放大缩小
hasRotatingPoint: true, //选中时是否可以旋转
hasBorders: true, //选中时是否有边框
lockMovementX: true, //X轴是否可被移动(true为不可,因为前缀是lock)
lockMovementY: true, //Y轴是否可被移动(true为不可,因为前缀是lock)
更多的属性,可以查看对应文档:JSDoc: Home (fabricjs.com)
五,操作对象
上文我们只是创建了对象,在某个时候,我们可能会想要修改那些对象。
也许某些动作将需要触发状态改变,或者播放某种形式的动画。或者,我们可能想更改某些鼠标交互时的对象属性(颜色,不透明度,大小,位置)。
Fabric和vue一样,数据驱动视图,我们只要变更对应对象的属性值,就可以驱动视图发生相应的变化。
而这些可供修改的属性,都与定位有关——left、top;尺寸——width、height;渲染——fill、opacity、stroke、strokeWidth;缩放和旋转——scaleX、scaleY、angle;甚至与翻转相关的内容——flipX、flipY和倾斜的skewX、skewY。
1,读取对象属性
obj.get('属性名')
例如,获取距离左侧的宽度:
//新建画布对象
const canvas = new fabric.Canvas("canvas", {
backgroundColor: "pink",
selectionColor: "blue",
selectionLineWidth: 2,
});
// 多边形
const 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);
console.log("获取距离左侧距离", polygon.get("left"));
2,设置对象属性
obj.set("属性名",新的属性值)
这个set还支持链式调用,并且,在canvas.add(polygon);之前或者之后调用,都是可以的:
polygon.set("fill", "blue").set("stroke", "red");//将上文的多边形填充和边框颜色变更
或者obj.set传入属性对象也是可以的
obj.set({属性名:属性值})
3,自定义原型对象属性方法
在fabric中的大多数对象都从根fabric.Object
继承。 fabric.Object
几乎代表一个二维形状,位于二维画布平面中。它是一个具有left / top和width / height属性以及一系列其他图形特征的实体。我们在对象上看到的那些属性(fill、stroke、angle、opacity、flip *等)对于所有从fabric.Object
继承的Fabric对象都是共有的。 这种继承使我们可以在fabric.Object
上定义方法,并在所有子“类”之间共享它们。例如,如果要在所有对象上都具有getWidth
方法,则只需在fabric.Object.prototype
上创建它:
fabric.Object.prototype.getWidth = function () {
return this.get("width");
};
//新建画布对象
const canvas = new fabric.Canvas("canvas", {
backgroundColor: "pink",
selectionColor: "blue",
selectionLineWidth: 2,
});
// 多边形
const 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);
console.log("获取宽度", polygon.getWidth());
六,Image对象
图像对象和基础对象又有所不同,因为它需要开发者额外导入图像资源。
这里需要区分两种情况。
1,document上有已经渲染的图像
这种写法需要等图像已经渲染出来了,才能创建fabric对象。所以我把创建canvas放在image的load方法内部了。
<template>
<div id="app">
<!-- 1、创建 canvas 元素 -->
<canvas
width="400"
height="400"
id="canvas"
style="border: 1px solid #ccc"
></canvas>
<img
src="./hq-wmyd.png"
alt="picture"
id="img"
width="100"
height="auto"
@load="imageLoad"
/>
</div>
</template>
<script>
//引入fabric
import { fabric } from "fabric";
export default {
name: "App",
components: {},
mounted() {},
data() {
return {};
},
methods: {
imageLoad() {
const canvas = new fabric.Canvas("canvas");
canvas.setBackgroundColor("rgb(100,200,200)");
const imgElement = document.getElementById("img");
const imgInstance = new fabric.Image(imgElement, {
left: 100,
top: 100,
angle: 45,
opacity: 0.6,
});
console.log(imgInstance);
canvas.add(imgInstance);
},
},
};
</script>
<style scoped lang="scss"></style>
2,采用图片地址的方式
const canvas = new fabric.Canvas("canvas");
canvas.setBackgroundColor("rgb(100,200,200)");
fabric.Image.fromURL(
"https://i.niupic.com/images/2022/10/15/a9pJ.png",
(oImg) => {
canvas.add(oImg);
}
);
实现的效果均是如下:
七,路径对象path
上文我们已经能够绘制基本的图案了。
如果想要画出更复杂的图案,则需要引入path。
Fabric中的路径非常类似于SVG <path>
元素。它们使用相同的命令集。Fabric中的路径代表可以用其他方式填充,描边和修改的形状轮廓。路径由一系列命令组成,这些命令实际上模仿了笔从一个点到另一个点的过程。借助“移动”,“线”,“曲线”或“弧”等命令,路径可以形成难以置信的复杂形状。
const canvas = new fabric.Canvas("canvas");
canvas.setBackgroundColor("rgb(100,200,200)");
const path = new fabric.Path(
`M8870 5431 c-20 -11 -46 -38 -60 -63 l-25 -43 -5 -1985 c-5 -1969 -5
-1985 15 -2029 36 -80 41 -81 412 -81 l323 0 29 57 c98 194 293 384 499 487
81 41 217 83 326 102 147 25 406 16 538 -19 301 -79 541 -276 674 -553 l45
-94 421 0 421 0 63 31 c88 43 186 135 221 207 l28 57 3 728 3 728 -35 104
c-45 134 -88 218 -199 390 -191 296 -1019 1641 -1061 1723 -76 150 -232 238
-466 262 -64 6 -479 10 -1119 10 -970 -1 -1018 -2 -1051 -19z m1991 -291 c171
-17 279 -58 309 -117 8 -15 62 -107 119 -203 281 -475 476 -823 537 -961 26
-58 39 -103 42 -146 4 -58 2 -64 -25 -92 -16 -16 -47 -34 -69 -40 -55 -15
-2238 -15 -2299 0 -52 13 -119 67 -144 118 -40 79 -43 122 -39 731 3 577 3
586 25 632 13 29 35 55 55 68 32 19 48 20 709 20 393 0 720 -4 780 -10z m1605
-3111 c49 -45 54 -72 54 -294 0 -236 -7 -266 -64 -290 -71 -30 -157 -13 -206
39 -24 26 -25 34 -28 173 -7 312 33 409 166 400 38 -3 59 -10 78 -28z`
);
let commonObj = {
left: 10,
top: 10,
fill: "#5E2300",
scaleX: 0.01,
scaleY: 0.01,
};
canvas.add(path.set(commonObj));
创建路径的流程是:实例化fabric.Path对
象,并向其传递一串路径指令。虽然看起来很神秘,但实际上很容易理解。 “ M”表示“移动”命令,并指示隐形笔移动到第一点。 “ L”代表“线”,使笔画一条线到下一点。然后,另一个“ c”创建一条到另一点的贝塞尔曲线。最后,“ z”告诉强制绘图笔关闭路径并最终确定形状。
由于fabric.Path
就像Fabric中的任何其他对象一样,我们也能够更改其某些属性。
可以看到,路径是很多的点数据,所以通常我们很少会手动创建Path实例,而是使用Fabric的内置SVG解析器来获取路径。
上面代码实现的效果:
八,文本对象
fabric提供的文本对象支持多行输入、文字对齐方式选择、文字背景设置、文本修饰、文本行高、设置字间距、修改样式、富文本(支持标签)、在 canvas
进行输入编辑。 常用的创建文本的方法如下,第一个参数是文本内容,第二个则是文本的属性对象:
const canvas = new fabric.Canvas("canvas");
canvas.setBackgroundColor("rgb(100,200,200)");
const txt = new fabric.Text('test', {
left: 140, // 位置
top: 140,
shadow: 'rgba(0,0,0,0.3) 15px 15px 15px', // 阴影
fontFamily: 'Hoefler Text', // 字体
stroke: '#ff1318', // 画笔颜色
strokeWidth: 2, // 画笔粗度
fill: '#0f0', //填充颜色
fontSize: 60, // 字体大小
});
canvas.add(txt);
实现的效果:
九,组的概念
上文讲的都是单个元素的创建和操作,除此之外,fabric也提供了将创建的对象进行组合的方法 group
,将几个对象进行组合成为一个新的对象之后进行操作:
const canvas = new fabric.Canvas("canvas");
canvas.setBackgroundColor("rgb(100,200,200)");
const rect = new fabric.Rect({
//自己对象的属性可以单独设置
width: 30,
height: 30,
fill: "yellow",
});
const ellipse = new fabric.Ellipse({
rx: 10, //短轴
ry: 15, //长轴
fill: "red", //填充颜色
stroke: "rgba(0, 0, 0, 1)", //边框颜色
});
const group = new fabric.Group([ rect, ellipse ], {
//公用的属性可以设置到这里来
left: 0,
top: 0,
});
canvas.add(group);
实现的效果如下图:
而如果需要设置对象在组合中的位置,可以在创建对象的时候设置位置:
const rect = new fabric.Rect({
+ left: 15,
+ top: 10,//这里设置位置,是以groups的左上角为基准的相对坐标值
width: 30,
height: 30,
fill: "yellow",
});