Fabric.js 简介。 第 4 部分。
在上一个系列中,我们已经讨论了很多主题; 从基本的对象操作到动画、事件、过滤器、组和子类。 但是仍然有一些非常有趣和有用的事情要讨论!
免费绘图
如果说有什么 <canvas> 真正的亮点,那就是它对免费绘图的出色支持! 由于画布只是一个 2D 位图——一张用于绘画的纸——执行自由绘图是非常自然的。 当然,Fabric 会为我们解决这个问题。
isDrawingMode 只需将Fabric 画布的属性 设置为 ,即可启用自由绘图模式 true 。 这立即使画布上的任何进一步点击和移动都被解释为铅笔/画笔。
您可以根据需要在画布上多次绘制,而 isDrawingMode is true 。 但是,只要您执行任何移动,然后是“mouseup”事件,Fabric 就会触发“path:created”事件并将刚刚绘制的形状转换为真实 fabric.Path 实例!
如果在任何时候,您设置 isDrawingMode 回 false ,您最终会得到所有创建的路径对象仍然存在于画布上。 而且由于它们是很好的旧 fabric.Path 对象,您可以随意修改它们——移动、旋转、缩放等。
还有 2 个属性可用于自定义免费绘图 - freeDrawingBrush.color 和 freeDrawingBrush.width . 两者都可以通过 freeDrawingBrush 实例在 Fabric 画布实例上使用。 freeDrawingBrush.color 可以是任何常规颜色值,代表画笔的颜色。 freeDrawingBrush.width 是以像素为单位的数字,表示画笔厚度。
在不久的将来,我们计划为免费绘图添加更多选项 - 各种版本的画笔(例如喷雾状或粉笔状)。 还有自定义画笔图案,以及使用您自己的扩展选项,类似于 Fabric 图像过滤器。
擦除 是另一个很酷的可用功能,可以很好地与免费绘图配合使用。
定制
Fabric 令人惊奇的事情之一是它的可定制性。 您可以在画布或画布对象上调整数十种不同的参数,以使事物完全按照您想要的方式运行。 让我们来看看其中的一些。
锁定对象画布上的每个对象都可以通过几种方式锁定。 “lockMovementX”、“lockMovementY”、“lockRotation”、“lockScalingX”、“lockScalingY”是锁定相应对象动作的属性。 因此设置 object.lockMovementX 以 true 防止对象水平移动。 你仍然可以在垂直平面上移动它。 同样, lockRotation 防止旋转和 lockScalingX / lockScalingY - 缩放。 所有这些都是累积的。 您可以以任何方式将它们组合在一起。
改变边界,角落您可以通过“hasControls”和“hasBorders”属性控制对象的边框和角的可见性。 只需将它们设置为 false ,对象就会立即呈现“裸体”。
object.hasBorders = false;
object.hasControls = false;
您还可以通过调整一些自定义属性“cornerDashArray”、“borderDashArray”、“borderColor”、“transparentCorners”、“cornerColor”、“cornerStrokeColor”、“cornerStyle”、“selectionBackgroundColor”、“padding”和“cornerSize”来更改它们的外观“ 特性。
object.set({ borderColor:'red', cornerColor:'green', cornerSize:6 });
object.set({ transparentCorners: false, cornerColor:'blue', cornerStrokeColor:'red', borderColor:'red', cornerSize:12, padding:10, cornerStyle:'circle', borderDashArray:[3, 3] }) ;
禁用选择
您可以通过将画布的“ selection ”属性设置为 来禁用画布上的object选择 false 。 这可以防止对画布上显示的所有内容进行选择。 如果您只需要使某些object不可选择,您可以更改对象的“ selectable ”属性。 只需将其设置为 false ,对象就会失去交互性。
自定义选择现在,如果您不想禁用选择,而是想更改其外观怎么办? 没问题。
画布上有 4 个属性可以控制其呈现方式——“selectionColor”、“selectionBorderColor”、“selectionLineWidth”和“selectionDashArray”。 这些应该是不言自明的,所以让我们看一个例子:
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 })); canvas.selectionColor = 'rgba(0,255,0,0.3)'; canvas.selectionBorderColor = 'red'; canvas.selectionLineWidth = 5;
最后一个属性——“selectionDashArray”——并不那么简单。 它允许我们做的是使选择线变成虚线。 定义破折号模式的方法是通过数组指定间隔。 因此,要创建一个长破折号后跟一个短破折号的模式,我们可以使用 [10, 5] “selectionDashArray”之类的东西。 这将绘制一条 10px 长的线,然后跳过 5px,再次绘制 10px 的线,依此类推。 如果我们使用 [2, 4, 6] 数组,图案将通过绘制 2px 线,然后跳过 4px,然后绘制 6px 线,然后跳过 2px,然后绘制 4px 线,然后跳过 6px,等等来创建。 你明白了。 例如,这是 [5, 10] 模式的外观:
虚线描边
与画布上的“selectionDashArray”类似,所有 Fabric 对象都具有“strokeDashArray”属性,负责在对象上执行的任何笔划的虚线图案。
var rect = new fabric.Rect({ fill: '#06538e', width: 125, height: 125, stroke: 'red', strokeDashArray: [5, 5] }); canvas.add(rect);
可点击区域
如您所知,所有 Fabric 对象都有边界框,用于在存在控件/角时拖动对象或旋转和缩放对象。 您可能已经注意到,即使单击对象边界框内没有绘制任何内容的空间,也可以拖动对象。
看看这张图片:
默认情况下,画布上的所有 Fabric 对象都可以被边界框拖动。 但是,如果您想要不同的行为——仅通过对象的实际内容单击/拖动对象,您可以在对象上使用“perPixelTargetFind”属性。 只需将其设置为 true 即可获得所需的行为。
旋转点由于 1.0 版 Fabric 默认使用替代 UI — 对象不能再同时缩放和旋转。 相反,每个对象都有一个单独的旋转控件。 该控件的相应属性是“hasRotatingPoint”。 您可以通过“rotatingPointOffset”数字属性自定义其相对于对象的偏移量。
对象变换
自 1.0 版以来 ,Fabric 中还有许多其他与转换相关的属性 。 其中之一是画布实例上的“uniScaleTransform”。 false 默认情况下,可用于启用对象的非均匀缩放 ; 换句话说,它允许在拖动角落时改变对象的比例。
然后是“centeredScaling”和“centeredRotation”属性(在 v1.3.4 之前是一个属性 - “centerTransform”)。 它们指定是否应将对象的中心用作变换的原点。 当它们都设置为 true 时,当对象总是从中心缩放/旋转时,它会复制 1.0 之前的行为。 由于 1.0 的变换原点是动态的,因此可以在缩放对象时进行更精细的控制。
最后一对新属性是“originX”和“originY”。 默认情况下相应地设置为“left”和“top”,它们允许以编程方式更改对象的转换原点。 当您拖动对象的角时,正是这些属性在引擎盖下动态变化。
那么我们什么时候可以手动更改它们呢? 例如,在处理文本对象时。 当您动态更改文本并且文本框尺寸增加时,“originX”和“originY”指示框的增长位置。 因此,如果您需要将文本对象居中,您可以将 originX 设置为“center”。 要将其固定在右侧,您可以将 originX 设置为“正确”。 等等。 这种行为类似于 CSS 中的“位置:绝对”。
画布背景和覆盖您可能还记得第一部分,您可以指定一种颜色来填充整个画布背景。 只需将任何常规颜色值设置为画布的“背景颜色”属性。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 })); canvas.backgroundColor = 'rgba(0,0,255,0.3)'; canvas.renderAll();
您可以更进一步,将图像指定为背景。 您需要为此使用 setBackgroundImage 方法,传递 url 和一个可选的完成回调。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 })); canvas.setBackgroundImage('assets/pug.jpg', canvas.renderAll.bind(canvas));
需要注意的是,虽然该属性称为“背景图像”,但它可以托管任何结构对象类型。 您可以设置一个 `fabric.Rect` 来表示一个画板,也可以设置一组对象。 下面的`overlayImage`或可以承载任何填充物的`backgroundColor`也是如此,例如渐变或图案。
最后,您还可以设置叠加图像,在这种情况下,它将始终出现在画布上渲染的任何对象的顶部。 只需使用 setOverlayImage ,传递 url 和可选的完成回调。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 })); canvas.setOverlayImage('assets/jail_cell_bars.png', canvas.renderAll.bind(canvas));
Node.js 上的 Fabric
Fabric 的独特之处之一是它不仅可以在客户端、浏览器中工作,还可以在服务器上工作! 当您想从客户端发送数据并在服务器上创建该数据的图像时,这可能很有用。 或者,如果您只是想从控制台使用 Fabric API——出于速度、方便或其他原因。
下面我们来看看如何搭建Node环境并启动Fabric。
首先,您需要安装 Node.js (如果您还没有的话)。 根据平台的不同,安装 Node 的方法很少。 您可以按照 这些说明 或 这些 .
安装 Node 后,我们需要安装 node-canvas 库。 node-canvas 是 NodeJS 的 Canvas 实现。 它依赖于 Cairo — 可以在 Mac、Linux 或 Windows 上运行的 2D 图形库。 node-canvas 有专门的 安装说明 ,具体取决于您选择的平台。
由于 Fabric 在 Node 之上运行,它以 NPM 包的形式出现。 所以下一步就是安装 NPM。 您可以在其github repo 中找到安装说明 。
最后一步是使用 NPM 安装 Fabric 包 。 这只需运行 npm install fabric (或 npm install -g fabric 全局安装包)即可完成。
如果我们现在运行节点控制台,我们应该可以同时使用节点画布和 Fabric:
> node ... > typeof require('canvas'); // "function" > typeof require('fabric'); // "object"
现在一切准备就绪,我们可以尝试一个简单的“hello world”测试。 让我们创建一个 helloworld.js 文件:
var fs = require('fs'), fabric = require('fabric').fabric, out = fs.createWriteStream(__dirname + '/helloworld.png'); var canvas = new fabric.StaticCanvas(null, { width: 200, height: 200 }); var text = new fabric.Text('Hello world', { left: 100, top: 100, fill: '#f55', angle: 15 }); canvas.add(text); canvas.renderAll(); var stream = canvas.createPNGStream(); stream.on('data', function(chunk) { out.write(chunk); });
然后将其作为 node helloworld.js . 打开 helloworld.png 显示:
那么这里发生了什么? 让我们回顾一下这段代码的重要部分。
首先,我们包括 Fabric 本身 ( fabric = require('fabric').fabric )。 然后,我们创建好的旧织物画布。
然后是看起来很熟悉的对象创建 ( new fabric.Text() ) 和画布添加 ( canvas.add(text) )。
所有这些都只是创建了 Fabric 画布并将文本对象渲染到上面。 现在,如何创建在画布上渲染的任何图像? 使用 createPNGStream 画布实例上可用的方法。 createPNGStream 返回 Node 的 流对象 ,然后可以使用 输出到图像文件中 on('data') ,并写入图像文件对应的流中( fs.createWriteStream() )。
fabric.Canvas#createPNGStream 是 Node 特有的方法之一。 其他一切都一样——您仍然可以像往常一样创建对象、将它们添加到画布上、修改、渲染等等。
节点服务器和 Fabric作为示例,让我们创建一个简单的 Node 服务器,它将使用 JSON 格式的 Fabric 数据侦听传入请求,并输出该数据的图像。 整个脚本只有 25 行长!
var fabric = require('fabric').fabric, // or import { fabric } from 'fabric'; http = require('http'), url = require('url'), PORT = 8124; var server = http.createServer(function (request, response) { var params = url.parse(request.url, true); var canvas = new fabric.StaticCanvas(null, { width: 200, height: 200 }); response.writeHead(200, { 'Content-Type': 'image/png' }); canvas.loadFromJSON(params.query.data, function() { canvas.renderAll(); var stream = canvas.createPNGStream(); stream.on('data', function(chunk) { response.write(chunk); }); stream.on('end', function() { response.end(); }); }); }); server.listen(PORT);
这个片段中的大部分代码应该已经很熟悉了。 它的要点在于服务器响应。 我们正在创建 Fabric 画布,将 JSON 数据加载到其上,进行渲染,并将最终结果作为服务器响应流式传输。
为了测试它,让我们取一个绿色的、稍微旋转的矩形的数据:
{"objects":[{"type":"rect","left":103.85,"top":98.85,"width":50,"height":50,"fill":"#9ae759","overlayFill":null,"stroke":null,"strokeWidth":1,"strokeDashArray":null,"scaleX":1.39,"scaleY":1.39,"angle":30,"flipX":false,"flipY":false,"opacity":0.8,"selectable":true,"hasControls":true,"hasBorders":true,"hasRotatingPoint":false,"transparentCorners":true,"perPixelTargetFind":false,"rx":0,"ry":0}],"background":"rgba(0, 0, 0, 0)"}
对其进行 URI 编码:
%7B"objects"%3A%5B%7B"type"%3A"rect"%2C"left"%3A103.85%2C"top"%3A98.85%2C"width"%3A50%2C"height"%3A50%2C"fill"%3A"%239ae759"%2C"overlayFill"%3Anull%2C"stroke"%3Anull%2C"strokeWidth"%3A1%2C"strokeDashArray"%3Anull%2C"scaleX"%3A1.39%2C"scaleY"%3A1.39%2C"angle"%3A30%2C"flipX"%3Afalse%2C"flipY"%3Afalse%2C"opacity"%3A0.8%2C"selectable"%3Atrue%2C"hasControls"%3Atrue%2C"hasBorders"%3Atrue%2C"hasRotatingPoint"%3Afalse%2C"transparentCorners"%3Atrue%2C"perPixelTargetFind"%3Afalse%2C"rx"%3A0%2C"ry"%3A0%7D%5D%2C"background"%3A"rgba(0%2C%200%2C%200%2C%200)"%7D
并通过“数据”查询参数传递给服务器。 立即响应以“image/png”内容类型返回,如下所示:
如您所见,在服务器上使用 Fabric 非常简单直接。 随意尝试这个片段。 可能从 URL 参数中更改画布尺寸,或者在返回图像作为响应之前修改客户端数据。
节点上 Fabric 中的自定义字体在我们可以在 Fabric 中使用自定义字体之前,我们需要先加载它们。 在浏览器(客户端)中,最常见的加载字体的方法是使用 CSS3 @font-face 规则 。 在 Fabric on Node(服务器端)中,我们可以使用 node-canvas Font API ,这使得加载字体变得轻而易举。
下面的示例演示了如何加载和使用自定义字体。 将其保存到 customfont.js 并确保字体文件的路径正确。 在这个例子中,我们使用 Ubuntu 作为我们的自定义字体。
var fs = require('fs'), fabric = require('fabric').fabric; // or import { fabric } from 'fabric'; fabric.nodeCanvas.registerFont(__dirname + '/test/fixtures/Ubuntu-Regular.ttf', { family: 'Ubuntu', weight: 'regular', style: 'normal' }); fabric.nodeCanvas.registerFont(__dirname + '/test/fixtures/Ubuntu-Bold.ttf', { family: 'Ubuntu', weight: 'bold', style: 'normal' }); fabric.nodeCanvas.registerFont(__dirname + '/test/fixtures/Ubuntu-Italic.ttf', { family: 'Ubuntu', weight: 'regular', style: 'italic' }); fabric.nodeCanvas.registerFont(__dirname + '/test/fixtures/Ubuntu-BoldItalic.ttf', { family: 'Ubuntu', weight: 'bold', style: 'italic' }); var canvas = new fabric.StaticCanvas(null, { width: 300, height: 250 }); var text = new fabric.Text('regular', { left: 0, top: 50, fontFamily: 'Ubuntu' }); canvas.add(text); text = new fabric.Text('bold', { left: 0, top: 100, fontFamily: 'Ubuntu', fontWeight: 'bold' }); canvas.add(text); text = new fabric.Text('italic', { left: 0, top: 150, fontFamily: 'Ubuntu', fontStyle: 'italic' }); canvas.add(text); text = new fabric.Text('bold italic', { left: 0, top: 200, fontFamily: 'Ubuntu', fontWeight: 'bold', fontStyle: 'italic' }); canvas.add(text); canvas.renderAll(); var out = fs.createWriteStream(__dirname + '/customfont.png'); var stream = canvas.createPNGStream(); stream.on('data', function(chunk) { out.write(chunk); });
查看更多关于Fabric.js 简介。第 4 部分的详细内容...