首页 > 其他分享 >Fabric.js

Fabric.js

时间:2023-05-03 17:12:12浏览次数:42  
标签:Canvas fabric 对象 canvas js Fabric

Fabric.js是什么
Fabric.js 是一个简化HTML5 Canvas开发的Javascript库,Fabric.js提供了HTML5 Canvas本身缺失的对象模型、交互层、SVG解析器以及其他一整套工具。它是一个完全开源的项目,在MIT下获得授权,多年来一直在维护,近期要发布4.0版本,支持自定义controls。

Fabric.js能做什么
在Canvas画布上创建、填充图形(包括图片、文字、规则图形和复杂路径组成图形)。
给图形填充渐变颜色。
组合图形(包括组合图形、图形文字、图片等)。
设置图形动画及用户交互,生成的对象自带拖拉拽功能。
JSON, SVG数据的序列化和反序列化。

为什么使用Fabric.js
HTML5 Canvas提供了完整的画布,可以轻松的在画布上绘制简单的图形、制作简单的动画,但是HTML5 Canvas提供的API过于低级,且操作基于上下文,因此在绘制复杂图形或者需要实现用户交互时,就显得不是那么方便了。Fabric.js在HTML5 Canvas原生API之上,提供完整的对象模型,由Fabric.js来管理HTML5 Canvas画布的状态和渲染,用户基于具体的对象,来编写代码,完成所需功能的开发(类似于面向过程和面向对象)。“Talk is cheap. Show me the code”,下面通过代码来看看用HTML5 Canvas原生方法和用Fabric.js分别实现在画布上画一个矩形这一功能。

用HTML5 Canvas的原生方法实现

// 获取canvas元素
var canvasEl = document.getElementById('c');
// 获取上下文
var ctx = canvasEl.getContext('2d');
// 设置填充颜色
ctx.fillStyle = 'red';
// 在100,100点创建20x20的矩形
ctx.fillRect(100, 100, 20, 20);
在线演示地址:https://codepen.io/liblack/pen/LYpaMwa

用Fabric.js实现相同的功能:

// 创建原生canvas元素的包装类(‘c’canvas元素的id)
var canvas = new fabric.Canvas('c');

// 创建一个矩形对象
var rect = new fabric.Rect({
left: 100,
top: 100,
fill: 'red',
width: 20,
height: 20
});

// 添加矩形到画布上
canvas.add(rect);
在线演示地址:https://codepen.io/liblack/pen/PoPLVwZ

由上面例子,可以看到使用HTML5 Canvas原生方法是对context(代表整个画布位图的对象)进行操作,而使用Fabric.js,我们是对对象操作,只需要去实例化对象,设置对象的属性,然后将它们添加到canvas中。到这里,可能还感受不到Fabric对比原生Canvas方法的优势,下面可以完成这样的功能,即在刚才绘制的矩形的基础上,将矩形位置移动到(10,10)点上。
这样的功能,在Canvas原生方法上实现,需要先把原来的矩形从画布上清除,然后在(10,10)位置重新绘制一个矩形,以此来实现矩形移动这样的功能。

// 擦除之前的矩形(这里是擦除了整个canvas区域)
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// 重新绘制矩形
ctx.fillRect(20, 50, 20, 20);
听起来是不是很蹩脚?是的,使用Canvas原生方法时,就像是个带橡皮檫的笔刷,在画布上绘制图形时,是笔刷在画布上移动,绘制出的图形就是笔刷移动的整个痕迹,而想把绘制好的图形移动到其他地方,只好用橡皮檫擦掉,然后在新位置重新绘制。
相应的,使用Fabric.js实现该功能时,只需要改变矩形对象的属性,然后重新刷新(渲染)画布即可。

rect.set({ left: 20, top: 50 });
canvas.renderAll();

Canvas对象
fabric.Canvas作为元素的包装器,创建fabric.Canvas对象如下:

var canvas = new fabric.Canvas('...')

它接收一个元素的id,并返回一个fabric.Canvas 的实例。fabric.Canvas对象,并不是DOM里的元素,如果需要直接控制DOM,或者对应的context,需要通过相应API去获取。

var canvasElement = document.getElementById(canvasEle);
var ctx = canvasElement.getContext("2d");
fabric.Canvas对象负责管理画布上绘制的所有对象,可以将对象添加到fabric.Canvas对象,也可以从fabric.Canvas获取或删除对象。

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

canvas.add(rect); // 添加对象

canvas.item(0); ///获取之前添加的fabric.Rect(第一个对象)
canvas.getObjects(); ///获取画布上的所有对象(rect将是第一个也是唯一一个)
canvas.remove(rect); ///删除之前添加的对象删除
fabric.Canvas对象可以对画布进行配置。比如需要为整个画布设置背景颜色或图像?需要将所有内容剪裁到特定区域?设置不同的宽度/高度?指定画布是否是交互式的?所有这些选项(及其他)都可以在fabric.Canvas对象上进行设置,可以在创建时或之后进行设置。

var canvas = new fabric.Canvas('c', {
backgroundColor: 'rgb(100,100,100,200)',
selectionColor:'blue',
selectLineWidth: 2
// ...
});

// 或

var canvas = new fabric.Canvas('c');
canvas.setBackgroundImage('http://...');
canvas.onFpsUpdate = function(){ /* ...... */ };
// ...
注意这种创建对象的形式,Fabric.js里基本上都是类似的,类名表示要创建的对象类型,第一个参数是必要的数据,第二个参数是各种选项。

所有对canvas的修改,包括在其中添加删除对象,以及对象参数的修改,都需要调用渲染方法才能显示出来:

canvas.renderAll();
Objects基本图形
Fabric.js提供了7种基本图形,下面是图形对应的类,

fabric.Circle
fabric.Ellipse
fabric.Line
fabric.Polygon
fabric.Polyline
fabric.Rect
fabric.Triangle
所有基本形状,都可以通过类实例的属性和方法来控制它们的位置、颜色、大小等样式。所有类都继承自fabric.Object类,有一些公共的属性和方法。

创建
下面是画线的例子(给出两个顶点坐标):

var line = new fabric.Line([x1, y1, x2, y2], {
strokeWidth: 2, //线宽
stroke: rgba(255,0,0,0.8), //线的颜色
selectable: false
});
canvas.add(line);
画圆的例子(顶点和半径是在选项里的),这里left和top其实就是(x,y),应该是借用了css里的命名。

var circle = new fabric.Circle({
radius: 2,
left: left,
top: top,
originX: 'center',
originY: 'center',
fill: rgba(0,200,0,0.8),
strokeWidth: 1,
stroke: rgba(255,0,0,0.8),
selectable: false
});
canvas.add(circle);
从这里可以看出,和创建canvas类似,第一个参数是这个类专用的(比如画直线的时候传的起止点坐标),第二个参数是通用选项,如果没有专用参数,那么第一个参数就直接是通用选项。所有创建完的形状,只有通过canvas.add方法加入,才能显示出来。

控制
left和top是每种Object都有的属性,至于它到底指图形中哪一个点的坐标,由originX和originY这组参数决定,它们相当于文本编辑软件里的对齐方式,originX有三种可选值:left,center, right;originY也有三种可选值:top, center, bottom。

它们的示意图如下:
http://fabricjs.com/test/misc/origin.html

如果希望对象的默认原点在中心,可以这样设置:

fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center'
width和height也是可以直接存取的属性,顾名思义,表示长和宽(所有形状都是有外接矩形的,所以可以用长和宽来控制大小)。除了上面那几个可以直接存取的属性,大部分属性需要使用get/set方法读写,比如:

line.left = pointer.x;
line.top = pointer.y;
line.set('stroke', startColor);
line.set('height', 20);
Image对象
Image跟其他形状类似,都是fabric.Object的子类,最大的区别在于,图像文件的加载是异步的,所以对Image的后续操作,都要在回调中完成。

fabric.Image.fromURL('my_image.png', function(oImg) {
// scale image down, and flip it, before adding it onto canvas
oImg.scale(0.5).set('flipX, true);
canvas.add(oImg);
});
Path对象
在Fabric.js中的Path代表了一个形状的轮廓,它可以被填充、描边和修改。Path由一系列的命令组成,本质上是模仿一支笔从一个点到另一个点。在“move”, “line”, “curve”, or “arc”等命令的帮助下,Path可以形成非常复杂的形状。而在Paths(PathGroup’s)组的帮助下,Path的能实现更多的复杂图形。
Fabric.js中的Path与SVG的 元素非常相似。它们使用相同的命令集,可以从 元素中创建,并将其序列化。我们稍后会更深入地研究序列化和SVG解析,但现在值得一提的是,实践中很少会手动创建Path实例。相反,更多的是使用Fabric.js的内置SVG解析器。
手工创建一个简单的Path对象。

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);
实践中,更多是直接使用fabric.loadSVGFromString 或 fabric.loadSVGFromURL 方法来加载整个 SVG 文件,然后让 Fabric 的 SVG 解析器来完成所有 SVG 元素的解析工作,并创建相应的 Path 对象。

Events事件
事件驱动的架构是一个框架灵活性和可扩展性的基础。Fabric.js提供了一个广泛的事件系统,覆盖了低级的 “鼠标”事件到高级的对象事件。
这些事件使我们能够监控到到画布上发生的各种动作的不同时刻。比如想知道鼠标被按下的时间,只需监听”mouse:down”事件就可以了。想知道对象何时被添加到画布上,只需要监听”object:added “事件就在那里。

事件API非常简单,类似于jQuery、Underscore.js或其他流行的JS库。有on方法来初始化事件监听器,有off方法来移除事件监听器。

下面一个实际的例子。

var canvas = new fabric.Canvas('...');
canvas.on('mouse:down', function(options) {
console.log(options.e.clientX, options.e.clientY);
});
上面例子演示了在canvas上添加”mouse:down”事件监听器,并给它设置事件处理程序,它将记录事件发生的坐标。事件处理程序会接收一个选项对象,它有两个属性:e—原始事件,和target—画布上的点击对象(如果有的话)。该事件在任何时候都是存在的,但目标只有在实际点击了画布上的某个对象后才会存在。target也只有在有意义的情况下才会传递给事件的处理程序,例如,对于 “mouse:down”事件,但对于 “after:render”事件(表示整个画布被重新绘制),target不会传递给事件处理程序。

canvas.on('mouse:down', function(options) {
if (options.target) {
console.log('一个对象被点击了!', options.target.type);
}
});
如果你点击了一个对象,上面的例子会输出 “一个对象被点击了!”。还会显示被点击的对象类型。

那么,在Fabric.js中还有鼠标级的事件:“mouse:down”, “mouse:move”, 和”mouse:up”。通用的事件: “after:render”。选择相关的事件: “before:selection:cleared”,”selection:create”,”selection:cleared”。对象相关的事件: “object:moded”、”object:selected”、”object:moving”、”object:scaling”、”object:rotating”、”object:additional “和 “object:detter”。
注意,像”object:moving”(或”object:scaling”)这样的事件在每次对象移动(或缩放)时,即使是一个像素点的移动,也会连续地被触发。另一方面,像 “object:modified” 或 “selection:create”这样的事件只在操作(对象修改或选区创建)结束时才会被触发。如果将事件直接附加到画布上的(canvas.on(‘mouse:down’, …)),这意味着事件被覆盖到了canvas实例上。如果一个页面上有多个画布,可以给每个画布附加不同的事件监听器。它们都是独立互不影响的。
为了方便,Fabric.js将事件系统做得更进一步,允许直接将监听器附加到具体对象上。如下例子。

var rect = new fabric.Rect({ width: 100, height: 50, fill: 'green' });
rect.on('elected', function() {
console.log('选择了一个矩形');
});

var circle = new fabric.Circle({ radius: 75, fill: 'blue' });
circle.on('selected', function() {
console.log('选择了一个圆圈');
上述例子直接给矩形和圆的实例附加事件监听器,使用的是 “selected “事件,而不是 “object:selected”。同样的,可以在对象上使用 “modified”事件(当附加到canvas上时使用 “object:modified”)、”rotating”事件(当附加到canvas时使用 “object:rotating”)等等。

Groups组
fabric.Group是Fabric.js提供的强大的功能之一。使用Groups可以将任何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);
组成组以后,依旧可以对每个对象操作,改变对象的属性和状态。

group.item(0).set('fill', 'red');
group.item(1).set({
text: 'trololo',
fill: 'white'
});
Serialization序列化
为构建某种有状态的应用程序,允许用户将canvas内容的结果保存在服务器上,或者将内容流媒体化到不同的客户端。Fabric.js提供了canvas序列化/解序列化支持。

toObject, toJSON
Fabric中序列化的主要方法是 fabric.Canvas#toObject()和 fabric.Canvas#toJSON()方法。让我们来看一个简单的例子,首先对一个空画布进行序列化。

var canvas = new fabric.Canvas('c');
JSON.stringify(canvas); // '{"objects":[], "background": "rgba(0,0,0,0,0)"}'
使用的是ES5 JSON.stringify()方法,如果传入的对象存在toJSON方法,那么这个方法就会隐含地调用toJSON方法。由于Fabric中的canvas实例有toJSON方法,所以调用JSON.stringify(canvas)方法和调用JSON.stringify(canvas.toJSON())一样。

注意,返回的字符串表示空的canvas。它是JSON格式的,本质上由 “objects”和”background”属性组成。”objects”目前是空的,因为canvas上没有任何东西,而 background 有一个默认的透明值(“rgba(0,0,0,0,0)”)。
当在canvas上添加了具体对象后:

canvas.add(new fabric.Circle({
left: 100,
top: 100,
radius: 50,
fill: 'red'
}));
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},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red",
"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,"radius":50}],"background":"rgba(0, 0, 0, 0)"}'
toSvg
Fabric.js支持将canvas画布序列化为SVG格式的文本。

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>'
Deserialization反序列化, SVG Parser SVG解析器
与序列化类似,反序列化是从字符串中加载canvas,与序列化时相对应的,也有两种方法:从JSON文本反序列和从SVG文本反序列化。当使用JSON表示时,有 fabric.Canvas#loadFromJSON和 fabric.Canvas#loadFromDatalessJSON方法。当使用SVG时,

有 fabric.loadSVGFromURL和 fabric.loadSVGFromString两个方法。

var canvas = new fabric.Canvas();

canvas.loadFromJSON('{"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},{"type":"circle","left":100,"top":100,"width":100,"height":100,"fill":"red","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,"radius":50}],"background":"rgba(0, 0, 0, 0)"}');
下移:canvas.sendBackwards(obj)
上移:canvas.bringForward(obj)
置顶:canvas.bringToFront(obj)
置底:canvas.sendToBack(obj)
更多资源
fabric.js官网:http://fabricjs.com/
fabric.js源码:https://github.com/fabricjs/fabric.js
fabric.js应用案例:https://printio.ru/tees/new_v2
HTML5 Canvas资料:https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API/Tutorial

标签:Canvas,fabric,对象,canvas,js,Fabric
From: https://www.cnblogs.com/ones/p/17369318.html

相关文章

  • AngularJS快速上手,从安装到运行
    0、先决条件    在开始之前,请确保你的开发环境已经包含了 Node.js和npm包管理器。Node.jsAngular需要 Node.js 的8.x或10.x版本。要想检查你的版本,请在控制台窗口中运行 node-v 命令。C:\Users\Administrator>node-vv8.12.0要想安装 Node.js,请访问 nodejs......
  • python学习笔记8(json数据格式、pycharts简单折线图)
    1.jsonjson是一种轻量级的数据交互格式,可以以json指定的格式去组织和封装数据;json本质上是一个带有特定格式的字符串;json负责不同编程语言中的数据传递和交互;1.1python数据与json数据相互转化引入json模块importjson1.1.1python数据转json数据importjson#python列表......
  • three.js 入门学习(一)
    webGl和three.jshttp://webgl3d.cn/pages/aac9ab/图形学算法Web3DWebGPU下载yarnaddthree@types/three使用import*asTHREEfrom'three';onstscene=newTHREE.Scene();仅导入你所需要的部分import{Scene}from'three';一个初始化的demo场景、相机......
  • 第139篇:JS数组常用方法(map(),reduce(),foreach())
    好家伙,本篇为MDN文档数组方法的学习笔记Array.prototype.reduce()-JavaScript|MDN(mozilla.org)数组方法这块的知识缺了,补一下 1.map()方法map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成。constarray1=[1,4,9,......
  • 数据交换格式:XML、JSON
    XMLXML是什么可扩展标记语言(XML)是存储和交换数据的重要方法。它文档的形式类似于HTML,不过比HTML低级,都是标签里放内容。XML只包含少量的预定义标签,其他都由程序员来定义,只要数据的读者和编写者都知道标签的含义,标签就可以包含任何设计者希望的有用信息。XML的标签有单双,双标签包......
  • python 合并json
    importjson#foriinrange(800):f1=open('.\\'+str(0)+'.txt',"r")data1=f1.read()dic_str2=json.loads(str(data1).replace("'","\""))foriinrange(1,50):f=open('.\\'+......
  • 简单聊聊,使用Vue.js编写命令行界面,前端开发CLI的利器
    Temir介绍Temir,一个用Vue组件来编写命令行界面应用的工具.开发者只需要使用Vue就可以编写命令行应用,不需要任何额外的学习成本.<scriptlang="ts"setup>import{ref}from'@vue/runtime-core'import{TBox,TText}from'@temir/core'constcounter=ref(0)setIn......
  • JS闭包理解
    概念在一个函数中嵌套另一个函数,嵌套(内部)函数对其容器(外部)函数是私有的。闭包是一个可以自己拥有独立的环境与变量的表达式(通常是函数,因为ES6有块级作用域的概念)闭包是指有权访问另一个函数作用域中变量的函数。闭包作用:可以在函数外部访问到函数内部的局部变量;让这些变量始......
  • 01_JS技巧
    1.判断对象数据类型示例代码如下constisType=(type)=>(target)=>`[object${type}]`===Object.prototype.toString.call(target)constisArray=isType('Array')constisObject=isType('Object')constisBoolean=isType('......
  • unity发布到4399的webgl模式问题:FRAMEWORK.JS中的WEBREQUEST_SEND括号内的函数(不能有
    在发布4399的时候,之前遇到过这个问题,解决方法当然就是删除这个函数啦。步骤也很简单,但是刚开始摸不着头脑搞了好久,最后发现发布的时候有个加密选项,选择不加密,后面build的文件里面就可以进行打开修改,按照要求修改函数即可。......