在本系列的第一部分,我们只开始熟悉Fabric.js。我们研究了Fabric在其对象模型和对象层次结构中使用Fabric的各种实例:简单的形状,图像和复杂的路径。我们还学习了如何使用画布上的Fabric对象执行简单的操作。
既然基础知识都差不多了,让我们开始学习一些有趣的东西吧!
动画(animate)
没有听说过哪一个强大的canvas库是没有动画功能的,当然Fabric也不例外。既然有这样一个强大的对象模型和图形功能,那么没有内置动画简直就是一种耻辱。
住改变任何对象的属性是多么容易?我们调用
1 | rect.set('angle', 45); |
那么,动画对象就像一样简单。每个Fabric对象都有
1 2 3 | rect.animate('angle', 45, { onChange: canvas.renderAll.bind(canvas) }); |
第一个参数是动画的属性,第二个参数是动画的最终位置,如果矩形为-15°角,我们传递45,这个矩形将从-15°到45°进行动画。第三个参数是一个可选的对象,指定动画的细节:持续时间,回调,动效等。
1 | rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas) }); |
同样的,逆时针旋转5度,可以像这样完成:
1 | rect.animate('angle', '-=5', { onChange: canvas.renderAll.bind(canvas) }); |
你可能会想知道为什么我们总是在那里指定“onChange”回调。不是说第三个参数是可选的吗?是为了每个动画帧的渲染都能让我们看到实际的动画效果!您可以看到,当我们调用
记住,在画布的下面有整个对象模型。对象有自己的属性和关系,而canvas只负责将它们的存在投射到外部世界。
每次更改之后,
那么我们可以通过哪些其他选项来动画?
- from 允许指定动画属性的起始值(如果我们不希望使用当前值)。
- duration 默认为500ms。可以用来改变动画的持续时间。
- onComplete 动画结束之后的回调。
- easing 动效函数。
除了easing,所有这些选择都应该是不言而喻的。我们来看看吧。
默认情况下,动画使用“easeInSine”动效执行。如果这不是你需要的,那么在
1 2 3 4 5 | rect.animate('left', 500, { onChange: canvas.renderAll.bind(canvas), duration: 1000, easing: fabric.util.ease.easeOutBounce }); |
注意使用了
所以这覆盖了Fabric的动画部分。只是给你一些可能的想法:使对象的angle改变来形成旋转动画,使对象的top/left改变来形成移动的动画,使对象的width/height改变来形成大小缩放的动画,等等。
图像滤镜(filters)
在第一节中我们学习了在Fabric中如何使用图像:
但是,像图像这种有趣的东西,对他们应用图像滤镜更加酷!
Fabric在默认情况下提供了很少的滤镜,对于支持或不支持WEBGL的浏览器都可以使用,也很容易去定义。一些你可能非常熟悉的内置的滤镜:去除白色背景、灰度滤镜、反色或亮度调色。其他的则可能不太受欢迎: colormatrix,sepia,noise。
那么我们怎么在Fabric中使用滤镜呢?
让我们创建一个灰度(Grayscale)图像:
1 2 3 4 5 6 7 8 | fabric.Image.fromURL('pug.jpg', function(img) { // 添加滤镜 img.filters.push(new fabric.Image.filters.Grayscale()); // 图片加载完成之后,应用滤镜效果 img.applyFilters(); // 将处理后的图片添加到canvas画布上 canvas.add(img); }); |
图像的色偏(Sepia)版本怎么样?
1 2 3 4 5 | fabric.Image.fromURL('pug.jpg', function(img) { img.filters.push(new fabric.Image.filters.Sepia()); img.applyFilters(); canvas.add(img); }); |
由于“filters”属性是一个数组,我们可以用数组方法执行任何所需的操作:移除滤镜(pop,splice,shift),添加滤镜(push,unshift,splice),甚至可以组合多个滤镜。当我们调用
1 2 3 4 5 6 7 8 | fabric.Image.fromURL('pug.jpg', function(img) { img.filters.push( new fabric.Image.filters.Sepia(), new fabric.Image.filters.Brightness({ brightness: 100 })); img.applyFilters(); canvas.add(img); }); |
请注意,我们还将
那么现在,您已经熟悉了Fabric滤镜,现在是时候开箱并创建自己的滤镜了!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | fabric.Image.filters.Redify = fabric.util.createClass({ type: 'Redify', /** * 用于编程的片段源码 */ fragmentSource: 'precision highp float;\n' + 'uniform sampler2D uTexture;\n' + 'varying vec2 vTexCoord;\n' + 'void main() {\n' + 'vec4 color = texture2D(uTexture, vTexCoord);\n' + 'color.g = 0;\n' + 'color.b = 0;\n' + 'gl_FragColor = color;\n' + '}', applyTo2d: function(options) { var imageData = options.imageData, data = imageData.data, i, len = data.length; for (i = 0; i < len; i += 4) { data[i + 1] = 0; data[i + 2] = 0; } } }); fabric.Image.filters.Brightness.fromObject = fabric.Image.filters.BaseFilter.fromObject; |
深入研究这段代码,主要的操作是在循环中进行。我们把绿色(data[i+1])和蓝色(data[i+2])替换为0,本质上是移除它们。标准rgb三元色的红色组件保持不变,基本上使整个图像变成红色。如您所见,
颜色(Colors)
无论您是否使用十六进制,RGB或RGBA颜色,Fabric都能提供一个完整的颜色基础,以帮助您最自然地表达自己。以下是在Fabric中定义颜色的一些方法:
1 2 3 4 5 | new fabric.Color('#f55'); new fabric.Color('#123123'); new fabric.Color('356735'); new fabric.Color('rgb(100,0,100)'); new fabric.Color('rgba(10, 20, 30, 0.5)'); |
转换也很简单。
1 2 3 | new fabric.Color('#f55').toRgb(); // "rgb(255,85,85)" new fabric.Color('rgb(100,100,100)').toHex(); // "646464" new fabric.Color('fff').toHex(); // "FFFFFF" |
转换不仅仅可以使单一的颜色。您也可以用另一种颜色叠加,或将其转换为灰度版本。
1 2 3 4 5 | var redish = new fabric.Color('#f55'); var greenish = new fabric.Color('#5f5'); redish.overlayWith(greenish).toHex(); // "AAAA55" redish.toGrayscale().toHex(); // "A1A1A1" |
渐变(Gradients)
更加表现出色的方式是通过渐变。渐变让我们将一种颜色混合到另一种颜色中,创造出一些令人惊叹的图形效果。
Fabric通过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var circle = new fabric.Circle({ left: 100, top: 100, radius: 50 }); circle.setGradient('fill', { x1: 0, y1: 0, x2: 0, y2: circle.height, colorStops: { 0: '#000', 1: '#fff' } }); |
在上面的例子中,我们在100,100的位置创建一个圆圈,半径为50px。然后,我们将其填充设置为跨越整个圆形高度的渐变,从黑色到白色。
传递给一个方法的参数是一个options对象,它需要2个坐标对(x1,y1和x2,y2)以及“colorStops”对象。坐标指出了渐变开始和结束的位置,
坐标相对于物体左上角,因此圆的最高点为0,最低点为
这是一个从左到右的红蓝渐变:
1 2 3 4 5 6 7 8 9 10 | circle.setGradient('fill', { x1: 0, y1: 0, x2: circle.width, y2: 0, colorStops: { 0: "red", 1: "blue" } }); |
这是一个彩虹??渐变,颜色跨度20%:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | circle.setGradient('fill', { x1: 0, y1: 0, x2: circle.width, y2: 0, colorStops: { 0: "red", 0.2: "orange", 0.4: "yellow", 0.6: "green", 0.8: "blue", 1: "purple" } }); |
你还能想到哪些炫酷的版本?
文本(Text)
如果你想不仅在画布上显示图像和矢量形状,还要显示文本,该怎么办?Fabric也为您准备好了!认识
我们提供文本的原因有两个,首先是允许以面向对象的方式处理文本。原生canvas方法,只允许在非常低的级别上填充或描边文本。通过实例化
第二个原因是提供比canvas给我们更丰富的功能,包括:
- 支持多行Multiline support 不幸的是,原生文本方法忽略了新建一行。
- 文本对齐Text alignment 左,中,右。使用多行文本时很有用。
- 文本背景Text background 背景也支持文本对齐。
- 文字装饰Text decoration 下划线,上划线,贯穿线。
- 行高Line Height 在使用多行文本时有用。
- 字符间距Char spacing 使文本更紧凑或更间隔。
- 子范围Subranges 将颜色和属性应用到文本对象的子对象中。
- 多字节Multibyte 支持表情符号。
- 交互式画布编辑On canvas editing 可以直接在画布上键入文本。
做一个hello world的例子:
1 2 | var text = new fabric.Text('hello world', { left: 100, top: 100 }); canvas.add(text); |
那就对了!在画布上显示文本就像在所需位置添加
当然,文本对象也有自己独有的与文本相关的属性。我们来看看其中的一些:
字体(fontFamily)
默认设置为“Times New Roman”,此属性允许我们更改用于呈现文本对象的字体系列。更改它将立即使文本以新字体呈现。
1 2 3 | var comicSansText = new fabric.Text("I'm in Comic Sans", { fontFamily: 'Comic Sans' }); |
字体大小(fontSize)
字体大小控制渲染文本的大小。请注意,与Fabric中的其他对象不同,您不能直接更改文本的width / height属性。相反,您需要更改“fontSize”值才能使文本对象更大或更小。或者,您可以随时使用scaleX / scaleY属性。
1 2 3 4 5 6 | var text40 = new fabric.Text("I'm at fontSize 40", { fontSize: 40 }); var text20 = new fabric.Text("I'm at fontSize 20", { fontSize: 20 }); |
粗体(fontWeight)
1 2 3 4 5 6 | var normalText = new fabric.Text("I'm a normal text", { fontWeight: 'normal' }); var boldText = new fabric.Text("I'm at bold text", { fontWeight: 'bold' }); |
文本修饰(textDecoration)
文本修饰允许添加下划线,上划线或贯穿线到文本。这与CSS类似,但是Fabric进一步,并允许使用上述的任何组合。所以你可以有一个文本,既有下划线又有上划线,还有贯穿线。等等。
1 2 3 4 5 6 7 8 9 | var underlineText = new fabric.Text("I'm an underlined text", { underline; true }); var strokeThroughText = new fabric.Text("I'm a stroke-through text", { linethrough: true }); var overlineText = new fabric.Text("I'm an overline text", { overline: true }); |
阴影
此属性在版本1.3.0之前被称为“textShadow”
文本阴影由4个组件组成:颜色,水平偏移,垂直偏移和模糊大小。如果您在CSS中使用阴影,这可能看起来很熟悉。通过更改这些值可以实现许多组合。
1 2 3 4 5 6 7 8 9 | var shadowText1 = new fabric.Text("I'm a text with shadow", { shadow: 'rgba(0,0,0,0.3) 5px 5px 5px' }); var shadowText2 = new fabric.Text("And another shadow", { shadow: 'rgba(0,0,0,0.2) 0 0 5px' }); var shadowText3 = new fabric.Text("Lorem ipsum dolor sit", { shadow: 'green -5px -5px 3px' }); |
字体风格(fontStyle)
字体样式可以是2个值之一:normal(正常)或italic(斜体)。这类似于同名的CSS属性。
1 2 3 4 5 6 7 8 | var italicText = new fabric.Text("A very fancy italic text", { fontStyle: 'italic', fontFamily: 'Delicious' }); var anotherItalicText = new fabric.Text("another italic text", { fontStyle: 'italic', fontFamily: 'Hoefler Text' }); |
描边的颜色和宽度(stroke and strokeWidth)
通过结合stroke和strokeWidth,你可以在你的文字上获得一些有趣的效果。这里有几个例子:
1 2 3 4 5 6 7 8 9 | var textWithStroke = new fabric.Text("Text with a stroke", { stroke: '#ff1318', strokeWidth: 1 }); var loremIpsumDolor = new fabric.Text("Lorem ipsum dolor", { fontFamily: 'Impact', stroke: '#c3bfbf', strokeWidth: 3 }); |
文本对齐(textAlign)
使用多行文本时,文本对齐很有用。使用单行文本,边界框的宽度总是与该行的宽度完全匹配,因此没有任何对齐方式。
允许的值为“left”,“center”和“right”。
1 2 3 4 | var text = 'this is\na multiline\ntext\naligned right!'; var alignedRightText = new fabric.Text(text, { textAlign: 'right' }); |
行高(lineHeight)
CSS中可能熟悉的另一个属性是lineHeight。它允许我们改变多行文本中文本行之间的垂直间距,只是值的规格和css中的不太一样,在以下示例中,第一个文本块的lineHeight为3,第二个为1。
1 2 3 4 5 6 | var lineHeight3 = new fabric.Text('Lorem ipsum ...', { lineHeight: 3 }); var lineHeight1 = new fabric.Text('Lorem ipsum ...', { lineHeight: 1 }); |
文本背景颜色(textBackgroundColor)
最后,textBackgroundColor允许给文本一个背景。请注意,背景仅填充文本字符占用的空间,而不是整个边框。这意味着文本对齐改变了文本背景渲染的方式。一行的高度也是如此,因为背景是由lineHeight创建的线之间的垂直空间。
1 2 3 4 | var text = 'this is\na multiline\ntext\nwith\ncustom lineheight\n&background'; var textWithBackground = new fabric.Text(text, { textBackgroundColor: 'rgb(0,200,0)' }); |
事件(Events)
事件驱动架构是框架内一些惊人的功能和灵活性的基础。Fabric也不例外,并提供广泛的事件系统,从低级“鼠标”事件开始到高级对象。
这些事件可以让我们在画布上发生的事件的同时,做一些事情。想知道什么时候鼠标被按下?只需要监听“mouse:down”事件,想知道什么时候对象被添加到canvas? 监听“object:add”事件,如果想知道画布什么时候渲染完成呢?只需监听“after:render”。
事件API非常简单,类似于jQuery,Underscore.js或其他流行的JS库。有一个
让我们来看个例子:
1 2 3 4 | var canvas = new fabric.Canvas('...'); canvas.on('mouse:down', function(options) { console.log(options.e.clientX, options.e.clientY); }); |
我们将事件“mouse:down”事件侦听器添加到画布上,并给它一个事件处理程序,用于记录事件起始位置的坐标。换句话说,它会记录鼠标按下的位置。事件处理程序接收一个选项对象,它有两个属性
1 2 3 4 5 | canvas.on('mouse:down', function(options) { if (options.target) { console.log('有对象被点击咯! ', options.target.type); } }); |
上面的例子将记录“有对象被点击咯!”如果您单击一个对象。它还将显示点击的对象的类型。
那么Fabric中有哪些其他的事件呢?那么,从鼠标级别可以看到“mouse:down”,“mouse:move”和“mouse:up”。从通用方面看,有“after:render”。然后有选择相关的事件:“before:selection:cleared”, “selection:created”, “selection:cleared”,最后,对象的事件:“object:modified”,“object:selected”,“object:moving”,“object:scaling”,“object:rotate”,“object:added”和“object:removed”。
请注意,每当一个对象被移动(或缩放)甚至一个像素时,诸如“object:moving”(或“object:scaling”)的事件被连续地触发。另一方面,诸如“object:modified”或“selection:created”之类的事件仅在操作结束时被触发(对象修改或选择创建)。
注意我们如何将事件附加到画布上(
为方便起见,Fabric将进一步提升事件系统,并允许您将侦听器直接附加到canvas画布中的对象上。让我们来看看:
1 2 3 4 5 6 7 8 9 | var rect = new fabric.Rect({ width: 100, height: 50, fill: 'green' }); rect.on('selected', function() { console.log('selected a rectangle'); }); var circle = new fabric.Circle({ radius: 75, fill: 'blue' }); circle.on('selected', function() { console.log('selected a circle'); }); |
我们将事件侦听器直接附加到矩形和圆形实例。使用"selected"来代替"object:selected"。同样的,使用“modified”代替“object:modified”,使用“rotating”来代替“object:rotating”。等等。。
查看此事件演示,以更广泛地探索Fabric的事件系统