跳转到内容

简介:4.绘制、定制化、node应用

在上一个系列中,我们讨论了很多主题。从基本的对象操作到动画,事件,过滤器,组和子类。但是仍然有一些非常有趣和有用的事情要讨论!

自由绘制

如果说有什么功能能让<canvas>在眼前一亮,那一定是它能够很好的支持自由绘图!由于画布只是一个2D位图, Fabric为我们提供了一张纸:可以自由绘画,而且非常自然。

只需将Fabric画布的isDrawingMode属性设置为true即可启用免费绘图模式。这立即使在画布上的任何进一步的单击和移动都被解释为铅笔/画笔。 当isDrawingMode为true时,您可以根据需要在画布上绘制多次。但是,一旦执行任何移动,然后执行“ mouseup”事件,Fabric就会触发“ path:created”事件,并实际上将刚刚绘制的形状转换为真实的fabric.Path实例!

如果随时将isDrawingMode设置为false,最终将在画布上仍然存在所有创建的路径对象。而且,由于它们是很好的旧fabric.Path对象,因此您可以根据需要进行任意修改(移动,旋转,缩放等)。

还有两个可用于自定义自由绘制的属性-freeDrawingBrush.colorfreeDrawingBrush.width。两者都可以通过freeDrawingBrush实例在Fabric画布实例上使用。 freeDrawingBrush.color可以是任何常规颜色值,并表示画笔的颜色。 freeDrawingBrush.width是一个以像素为单位的数字,代表画笔的厚度。

在不久的将来,我们计划为免费绘画添加更多选项-各种版本的画笔(例如,类似喷雾或粉笔的刷子)。还有自定义画笔图案,以及可以自己扩展的选项,类似于Fabric图像滤镜。

Erasing 擦除效果在这里也是其中很酷的效果之一.

定制化

Fabric的令人惊奇的事情之一是它的可定制性。您可以调整canvas或canvas对象上的数十个各种参数,以使事物的行为完全符合您想要的方式。让我们来看看其中的一些。

锁定对象

画布上的每个对象都可以通过几种方式锁定。 “ 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]
});

禁用选择

您可以通过将canvas的“ selection”属性设置为false来禁用画布上对象的选择。这样可以防止在画布上显示的所有内容上进行选择。如果只需要使某些对象不可选择,则可以更改对象的“ selectable”属性。只需将其设置为false,对象就会失去交互性。

自定义选择行为

现在,如果您不想禁用选择,而是想更改其外观怎么办?没问题。 画布上有4个控制其显示的属性-“ selectionColor”,“ selectionBorderColor”,“ selectionLikeWidth”和“ 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,来创建图案。你明白了。例如,这是[2,4,6]模式的外观:

作为一个例子, 下面是 [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中还有许多其他与转换相关的属性。其中之一是canvas实例上的“ uniScaleTransform”。默认情况下为false,可用于启用对象的非均匀缩放;换句话说,它允许在拖动角落时更改对象的比例。

然后有“ centeredScaling”和“ centeredRotation”属性(在v1.3.4之前是一个属性,即“ centerTransform”)。它们指定是否应将对象的中心用作变换的原点。当它们都设置为true时,当对象总是从中心缩放/旋转时,它将复制1.0之前的行为。由于1.0的转换原点是动态的,因此在缩放对象时可以进行更精细的控制。

最后一对新属性是“originX”和“originY”。默认情况下,将其相应地设置为“left”和“top”,它们允许以编程方式更改对象的变换原点。当您拖动对象的角时,正是这些属性在引擎盖下动态变化。

那么我们什么时候手动更改它们?例如,使用文本对象时。当您动态更改文本,并且文本框的尺寸增加时,“ originX”和“ originY”将指出框的增长位置。因此,如果需要将文本对象居中,则可以将originX设置为“ center”。要将其粘贴在右侧,可以将originX设置为“ right”。等等。此行为类似于CSS中的“ position:absolute”。

画布背景和填充

你可能在第1部分中已经看到过,可以分配一种颜色来填充整个画布背景,只需将任何常规颜色值设置为画布的“ 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.Rect”来代表一个画板,或者可以设置一组对象。下面的“ overlayImage”或可以托管任何填充物(例如渐变或图案)的“ backgroundColor”也是如此。

Finally, you can also set overlay image, in which case it will always appear on top of any objects rendered on canvas. Just use setOverlayImage, 最后,您还可以设置叠加图像,在这种情况下,叠加图像将始终显示在画布上渲染的所有对象的顶部。只需使用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-canvas库。 node-canvas是NodeJS的Canvas实现。它依靠Cairo —可以在Mac,Linux或Windows上运行的2D图形库。根据您选择的平台,node-canvas具有专用的安装说明。 由于Fabric在Node之上运行,因此它是NPM软件包。因此,下一步是安装NPM。您可以在其github存储库中找到安装说明。

最后一步是使用NPM安装Fabric软件包。只需运行npm install fabric(或npm install -g fabric以全局安装软件包)即可完成此操作。

如果现在运行节点控制台,则应该同时使用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);
});

然后将其作为节点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环境下定制字体

Before we can use custom fonts in Fabric we need to load them first. In the browser (client-side) the most common way to load fonts is by using the 在Fabric中使用自定义字体之前,我们需要先加载它们。在浏览器(客户端)中,最常见的加载字体的方法是使用CSS3 @ font-face规则。在节点上的Fabric(服务器端)中,我们可以利用node-canvas字体API,该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.FabricText('regular', {
left: 0,
top: 50,
fontFamily: 'Ubuntu'
});
canvas.add(text);
text = new fabric.FabricText('bold', {
left: 0,
top: 100,
fontFamily: 'Ubuntu',
fontWeight: 'bold'
});
canvas.add(text);
text = new fabric.FabricText('italic', {
left: 0,
top: 150,
fontFamily: 'Ubuntu',
fontStyle: 'italic'
});
canvas.add(text);
text = new fabric.FabricText('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);
});

使用节点customfont.js运行示例将创建一个如下图所示的图像(customfont.png):

Custom fonts

让我们仔细看看发生了什么。

在fabric.nodeCanvas上,公开了JSDOM所需的节点画布库,以将node-canvas library与HTMLCanvas Api接口连接起来。对于我们要使用的每个字体文件,我们需要通过传递字体文件路径和指定字体属性的对象来向fabric.nodeCanvas.registerFont()注册该文件。请记住,这必须在创建Canvas本身之前发生。

现在,我们可以通过将fabric.FabricText对象的fontFamily属性设置为字体名称来使用我们的字体。结合fontWeight和fontStyle属性,我们可以应用添加的字体。有关这些属性的更多信息,请参见第2部分(文本)。 请注意,该示例显示了在创建新的文本对象时如何使用自定义字体,但这也适用于通过JSON加载的文本对象。

因此,这使我们结束了有关Fabric的4部分系列的结尾。

希望您现在已经掌握了足够的知识,可以创建有趣,酷,有用,有趣,具有挑战性,令人兴奋的东西!