我们已经在前几个系列涵盖了很多话题,从基础对象操作到动画,事件,滤镜,组合和子类。但还有几件非常有趣和有用的事情要讨论!
文章目录
自由绘制
如果说<canvas>
有什么闪光点的话,那就是它对自由绘制有极好的支持!由于画布只是一个2D位图——一张可以在上面绘制的纸——所以进行自由绘制是非常自然的。当然,Fabric会帮我们解决这个问题。
只需将Fabric canvas的isDrawingMode
属性设置为true
,即可启用自由绘制模式。这会立即使画布上的任何进一步点击和移动被解释为铅笔/画笔。
当isDrawingMode
为true
时,您可以在画布上任意绘制多次。但只要您执行任何动作,然后是“mouseup”事件,Fabric就会触发“path:created”事件,并实际将刚刚绘制的形状转换为真实的fabric.Path
实例!
如果在任何时候将isDrawingMode
设置回false
,则最终所有创建的路径对象仍存在于画布上。因为它们是fabric.Path
对象,您可以按任何方式修改它们-移动、旋转、缩放等。
还有2个属性可用于自定义自由绘制——freeDrawingBrush.color
和freeDrawingBrush.width
。两者都可以通过freeDrawingBrush
实例在Fabric画布实例上使用。freeDrawingBrush.color
可以是任何常规颜色值,代表画笔的颜色。freeDrawingBrush.width是一个以像素为单位的数字,代表画笔的宽度。
在不久的将来,我们计划为自由绘制添加更多选项——各种版本的画笔(如喷雾状或粉笔状)。还有自定义画笔图案,以及一个可以使用自己的画笔扩展的选项,类似于Fabric图像滤镜。
擦除(Erasing)是另一个很酷的可用功能,可以很好地与自由绘图配合使用。
自定义
Fabric的一个惊人之处在于它的可定制性。您可以在画布或画布对象上调整数十个不同的参数,以使其行为完全符合您的要求。让我们来看看其中的一些。
锁定对象(Locking objects)
画布上的每个对象都可以通过几种方式锁定。“lockMovementX”、“lockMovementY”、“lockRotation”、“ockScalingX”和“lockScalingY”是锁定相应对象动作的属性。所以设置object.lockMovementX
为true
可阻止对象水平移动。但您仍然可以在垂直平面中移动它。类似地,lockRotation
防止旋转,lockScalingX
/lockScalingY
可以阻止水平或者垂直缩放。所有这些都是可叠加的。你可以用任何方式将它们组合在一起。
改变边框和角(Changing borders, corners)
您可以通过“hasControls”和“hasBorders”属性控制对象的边界和角的可见性。只要将它们设置为false
,对象就会立即呈现为“裸露的”。
object.hasBorders = false;
object.hasControls = false;
您还可以通过调整一些自定义属性“cornerDashArray”、“borderDashArry”、“borderColor”、“transparentCorners”、“cornerColor”“cornerStrokeColor”,“cornerStyle”、“selectionBackgroundColor”和“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]
});
禁用选择(Disabling selection)
通过将画布的“selection”属性设置为false
,可以禁用画布上的对象选择。这将阻止对画布上显示的所有内容进行选择。如果只需要使某些对象不可选择,则可以更改对象的“selectable”属性。只要将其设置为false
,对象就会失去交互性。
自定义选择(Customizing selection)
现在,如果您不想禁用选择,而是想更改其外观,该怎么办?没问题。
画布上有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]
模式的外观:
虚线描边(Dashed stroke)
与画布上的“selectionDashArray”类似,所有Fabric对象都有“strokeDashArray”属性,负责在对象上执行的任何笔划的虚线图案。
var rect = new fabric.Rect({
fill: '#06538e',
width: 125,
height: 125,
stroke: 'red',
strokeDashArray: [5, 5]
});
canvas.add(rect);
可点击区域(Clickable area)
如您所知,所有Fabric对象都有边界框,当存在控件/角时,该边界框用于拖动对象或旋转和缩放对象。您可能已经注意到,即使单击对象边界框内的空白区域,也可以拖动对象。
默认情况下,画布上的所有Fabric对象都可以由边界框拖动。但是,如果您想要不同的行为——仅通过对象的实际内容单击/拖动对象,则可以在对象上使用“perPixelTargetFind”属性。只需将其设置为true
即可获得所需的行为。
旋转点(Rotating point)
由于1.0版Fabric默认使用替代UI,因此无法同时缩放和旋转对象。相反,每个对象上都有一个单独的旋转控件。该控件的相应属性为“hasRotatingPoint”。您可以通过“rotatingPointOffset”数字属性自定义其相对于对象的偏移量。
对象变形(Object transformation)
自版本1.0以来,Fabric中还有许多其他与转换相关的属性。其中之一是画布实例上的“uniScaleTransform”。默认情况下为false
,可用于启用对象的非均匀缩放;换句话说,它允许在拖动角时更改对象的比例。
然后是“centeredScaling”和“centered Rotation”属性(在v1.3.4之前是一个属性-“centerTransform”)。它们指定对象的中心是否应用作变换的原点。当两者都设置为true
时,当对象始终从中心缩放/旋转时,它会复制1.0之前的行为。由于1.0变换的原点是动态的,因此可以在缩放对象时进行更精细的控制。
最后一对新属性是“originX”和“originY”。默认情况下,相应地设置为"left" 和"top",它们允许以编程方式更改对象的转换原点。当拖动对象的角时,这些属性在动态变化。
那么我们什么时候手动更改它们呢?例如,在处理文本对象时。当您动态更改文本,并且文本框尺寸增加时,“originX”和“originY”指示框的增长位置。所以,如果需要将文本对象居中,可以将originX设置为"center"。要使其向右移动,您需要将originX设置为“right”。这种行为类似于CSS中的“position:absolute”。
画布的背景和覆盖(Canvas background and overlay)
您可能还记得第一部分,您可以指定一种颜色来填充整个画布背景。只需将任何常规颜色值设置为画布的“backgroundColor”属性即可。
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和optiona完成回调。
canvas.add(new fabric.Circle({ radius: 30, fill: '#f55', top: 100, left: 100 }));
canvas.setBackgroundImage('../assets/pug.jpg', canvas.renderAll.bind(canvas));
需要注意的是,虽然属性名为“backgroundImage”,但它可以承载任何fabric对象类型。你可以设置一个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实现。它依赖于可以在Mac、Linux或Windows上运行的Cairo——一个2D图形库。node-canvas有专用的安装说明,具体取决于您选择的平台。
由于Fabric运行在Node之上,因此它作为NPM包提供。因此,下一步是安装NPM。您可以在其github repo中找到安装说明。
最后一步是使用NPM安装Fabric包。这只需运行npm install fabric
(或npm install-g fabric
来全局安装包)即可完成。
如果我们现在运行node控制台,我们应该可以使用node-canvas和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
)。然后,我们创建了一个Fabric画布。
然后是熟悉的对象创建(new fabric.Text()
)和画布添加 (canvas.add(text)
)。
所有这些只需创建Fabric画布并将文本对象渲染到画布上。现在,如何创建画布上渲染的任何内容的图像?使用画布实例上可用的createPNGStream
方法。createPNGStream
返回Node的流对象,然后可以使用on('data')
将其输出到图像文件中,并写入对应于图像文件的流中(fs.createWriteStream()
)。
fabric.Canvas#createPNGStream
是Node特有的方法之一。其他一切都是一样的——您仍然可以像平常一样创建对象,将它们添加到画布上,修改、渲染等等。
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
并通过“data”查询参数传递给服务器。立即响应,返回“image/png”内容类型,如下所示:
如您所见,在服务器上使用Fabric非常简单。请随意尝试这个片段。也许可以从URL参数中更改画布尺寸,或者在返回图像作为响应之前修改客户端数据。
在Node环境自定义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);
});
使用node customfont.js
运行示例。创建的图像(customfont.png)如下所示:
让我们仔细看看发生了什么。在fabric.nodeCanvas暴露了JSDOM所需的node-canvas库,以将node-canvas与HTMLCanvas Api连接。对于要使用的每个字体文件,我们需要使用fabric.nodeCanvas.registerFont()
注册该文件,方法是传递字体文件路径和指定字体属性的对象。请记住,这必须在创建画布本身之前发生。
现在我们可以通过设置fabric.Text
的fontFamily属性来使用字体。将文本对象设置为字体名称。结合fontWeight和fontStyle属性,我们可以应用我们添加的字体。有关这些属性的更多信息,请参见第2部分(文本)。
请注意,该示例演示了如何在创建新文本对象时使用自定义字体,但这也适用于通过JSON加载的文本对象。
因此,这将使我们结束关于Fabric的4部分系列。我希望你现在拥有足够的知识来创造有趣、酷、有用、有趣、有挑战性、令人兴奋的东西!