网站地图    收藏   

主页 > canvas引擎 > Fabricjs >

Fabric.js 简介。第2部分。

来源:未知    时间:2022-07-04 00:16 作者:小飞侠 阅读:

[导读] Fabric.js 简介。 第2部分。 在 本系列的第一部分 ,我们才刚刚开始熟悉 Fabric.js。 我们研究了使用 Fabric 的原因,它的对象模型和对象层次结构,以及 Fabric 中可用的不同类型的实体——...

Fabric.js 简介。第2部分。

本系列的第一部分,我们才刚刚开始熟悉 Fabric.js。我们研究了使用 Fabric 的原因,它的对象模型和对象层次结构,以及 Fabric 中可用的不同类型的实体——简单的形状、图像和复杂的路径。我们还学习了如何在画布上使用 Fabric 对象执行简单的操作。

既然大部分基础知识都已经过去了,让我们开始做一些有趣的事情吧!

动画

没有一个受人尊敬的画布库没有动画设施。Fabric 也不例外。由于有如此强大的对象模型和图形功能,如果没有内置动画助手将是一种耻辱。

还记得改变任何对象的属性是多么容易吗?我们只是调用了set方法,传递了相应的值:

rect.set('angle', 45);

好吧,动画对象同样容易。每个 Fabric 对象都有animate方法……为该对象设置动画。

rect.animate('angle', 45, {
  onChange: canvas.renderAll.bind(canvas)
});

第一个参数是动画的属性。第二个参数是动画的结束值。如果矩形有 -15° 角,并且我们传递 45,它将从 -15° 动画到 45°。第三个参数是一个可选对象,指定动画的更精细细节——持续时间、回调、缓动等。

一个方便的特性animate是它还支持相对值。例如,如果你想将对象的 left 属性设置为 100px,你可以这样做:

rect.animate('left', '+=100', { onChange: canvas.renderAll.bind(canvas) });

类似地,将物体逆时针旋转 5 度,可以这样完成:

rect.animate('angle', '-=5', { onChange: canvas.renderAll.bind(canvas) });

你可能想知道为什么我们总是在那里指定“onChange”回调。第三个参数不是可选的吗?是的,但是调用canvas.renderAll每个动画帧是让我们看到实际动画的原因!你看,当我们调用animate方法时,它只会随着时间的推移动画属性值,遵循特定的算法(即缓动)。所以rect.animate('angle', 45)会改变对象的角度,但不会在每次改变角度后重新渲染画布屏幕。我们显然需要重新渲染才能看到实际的动画。

请记住,在该画布表面下方有整个对象模型。对象有自己的属性和关系,canvas只负责将它们的存在投射到外界。

animate每次更改后不会自动重新渲染画布的原因是由于性能。毕竟,我们在画布上可以有成百上千的动画对象,如果每个人都尝试重新渲染屏幕,那就不好了。在许多对象的情况下,您可以使用类似requestAnimationFrame(或其他基于计时器的)循环来自行连续渲染画布,而无需调用renderAll每个对象。但大多数时候,您可能需要明确指定canvas.renderAll为“onChange”回调。

那么我们可以传递哪些其他选项来制作动画呢?

  • from:允许指定动画属性的起始值(如果我们不希望使用当前值)。

  • 持续时间:默认为 500(毫秒)。可用于更改动画的持续时间。

  • onComplete:在动画结束时调用的回调。

  • easing:缓动函数。

所有这些选项都应该是不言自明的,除了可能放宽一个。让我们仔细看看。

默认情况下,animate动画使用“easeInSine”函数。如果这不是您所需要的,那么在fabric.util.ease例如,如果我们想以有弹性的方式将对象向右移动:

rect.animate('left', 500, { 
  onChange: canvas.renderAll.bind(canvas), 
  duration: 1000, 
  easing: fabric.util.ease.easeOutBounce });

注意fabric.util.ease.easeOutBounce作为缓动选项。其他值得注意的包括easeInCubiceaseOutCubiceaseInElasticeaseOutElasticeaseInBounceeaseOutExpo

所以这几乎涵盖了Fabric的动画部分。只是为了给您一些可能的想法-您可以为对象的角度设置动画以使其旋转;为左/上属性设置动画以使其移动;为宽度/高度设置动画以使其缩小/增长;动画不透明度以使其淡入/淡出;等等。


fabric.runningAnimations


如果您需要访问当前由 fabric 运行的动画,请使用fabric.runningAnimations它是一个对象数组,每个对象都是一个动画上下文对象。

方便的方法:

  • fabric.runningAnimations.findAnimation(signature)- 返回动画上下文匹配signature,它是返回的中止函数fabric.util.animate

  • fabric.runningAnimations.findAnimationIndex(signature)- 与 相同findAnimation,返回索引。

  • fabric.runningAnimations.findAnimationsByTarget(target)- 返回所有target属性等于 target 的动画。

  • fabric.runningAnimations.cancelAll()- 取消所有正在运行的动画。

  • fabric.runningAnimations.cancelByTarget(target)- 取消target属性等于目标的动画。

  • object.dispose()- 对象 ( ) 创建的所有动画object.animate(...)都被取消。如果您想使用 fabric.util.animate而不是添加动画,object.animate(...)可以通过传递 target属性将它们附加到对象。这样,一旦对象被释放,动画就会取消。


  let cancel = fabric.util.animate({...});
  let i = fabric.runningAnimations.findAnimationIndex(cancel);
  let context = fabric.runningAnimations.findAnimation(cancel);
  let cancelled = fabric.runningAnimations.cancelAll();
  
  //  the following statements are true
  cancelled[i] === context;
  cancelled[i].cancel === cancel;
  fabric.runningAnimations.length === 0;

图像过滤器

在本系列的第一部分,我们学习了如何在 Fabric 中处理图像。fabric.Image构造函数,它接受图像元素。还有fabric.Image.fromURL一种方法,可以创建 URL 字符串的图像实例。并且任何这些图像都可以像任何其他对象一样在画布上抛出和渲染。

但是,尽管处理图像很有趣,但对它们应用图像过滤器会更酷!

默认情况下,Fabric 提供了很少的过滤器,无论是针对启用 WEBGL 的浏览器还是不支持的浏览器。它还可以轻松定义自己的。一些您可能非常熟悉的内置过滤器 - 过滤器以去除白色背景、灰度过滤器、反转或亮度过滤器。其他的可能不太受欢迎——彩色矩阵、棕褐色或噪声。

那么我们如何对 Fabric 中的图像应用过滤器呢?好吧,每个实例fabric.Image都有“过滤器”属性,这是一个简单的过滤器数组。该数组中的每个过滤器都是 Fabric 过滤器之一的实例。或者您自己的自定义过滤器的一个实例。

所以让我们创建一个灰度图像。

fabric.Image.fromURL('pug.jpg', function(img) { 
  // 添加滤镜
  img.filters.push(new fabric.Image.filters.Grayscale()); 
  // 应用滤镜并在完成后重新渲染画布
  img.applyFilters(); 
  // 将图像添加到画布上(它也会重新渲染画布)
  canvas.add(img); });

image.png

棕褐色版本的图像怎么样?

fabric.Image.fromURL('pug.jpg', function(img) { 
  img.filters.push(new fabric.Image.filters.Sepia()); 
  img.applyFilters(); 
  // 将图像添加到画布上(它也重新渲染画布) 
  canvas.add(img); });

由于“filters”属性是一个简单的数组,我们可以用它以通常的方式执行任何所需的操作——删除过滤器(通过pop、、spliceshift),添加过滤器(通过、、、pushspliceunshift甚至组合多个过滤器。当我们调用applyFilters因此,让我们尝试创建一个棕褐色和明亮的图像。

fabric.Image.fromURL('pug.jpg', function(img) { 
  img.filters.push( 
    new fabric.Image.filters.Sepia(), 
    new fabric.Image.filters.Brightness({亮度: 100 })) ; 
  img.applyFilters(); 
  canvas.add(img); });

请注意,我们还将{ brightness: 100 }对象传递给亮度过滤器。这是因为有些过滤器可以在没有任何额外配置的情况下应用(例如灰度、反转、棕褐色),而其他过滤器可以更好地控制它们的行为。对于亮度滤镜,它是实际的亮度级别(-1 全黑到 1 全白)。对于噪声滤波器,它是噪声值(0-1000)。对于“移除颜色”过滤器,它是阈值和距离值。等等。

现在您已经熟悉了 Fabric 过滤器,是时候打破常规,创建自己的过滤器了!

创建过滤器的模板非常简单。我们需要创建一个“类”,然后定义applyTo方法。可选地,我们可以提供过滤toJSON方法(支持 JSON 序列化)和/或initialize方法(支持可选参数)。

fabric.Image.filters.Redify = fabric.util.createClass(fabric.Image.filters.BaseFilter, {

  type: 'Redify',

  /**
   * Fragment source for the redify program
   */
  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.0;\n' +
      'color.b = 0.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.Redify.fromObject = fabric.Image.filters.BaseFilter.fromObject;

无需过多研究此代码,主要操作就在循环中发生。我们用 0 替换每个像素的绿色 (data[i+1]) 和蓝色 (data[i+2]) 分量,基本上将它们删除。标准 rgb 三元组的红色分量保持不变,基本上使整个图像涂成红色。如您所见,applyTo方法正在传递一个选项对象,该对象包含在过滤管道的该阶段图像的 imageData。从那里,我们可以迭代它的像素(getImageData().data)以我们想要的任何方式修改它们。如果浏览器支持 WEBGL,过滤器可以在 GPU 上运行。为此,您必须提供一个片段着色器来描述对像素执行的操作。在 fabric 中定义了许多过滤器,您可以在其中看到如何编写片段或顶点着色器的示例

颜色

无论您更喜欢使用 hex、RGB 还是 RGBA 颜色,Fabric 都能提供纯色基础,帮助您最自然地表达自己。以下是在 Fabric 中定义颜色的一些方法:

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)');

转换也很简单。toHex()将颜色实例转换为十六进制表示。toRgb()— 到 RGB 一,以及toRgba()— 到 RGB 与 alpha 通道。

new fabric.Color('#f55').toRgb(); // "rgb(255,85,85)"
new fabric.Color('rgb(100,100,100)').toHex(); // "646464"
new fabric.Color('fff').toHex(); // "FFFFFF"

转换并不是您可以对颜色进行的唯一操作。您还可以将一种颜色与另一种颜色叠加,或将其转换为灰度版本。

var redish = new fabric.Color('#f55');
var greenish = new fabric.Color('#5f5');

redish.overlayWith(greenish).toHex(); // "AAAA55"
redish.toGrayscale().toHex(); // "A1A1A1"

渐变

使用颜色的一种更具表现力的方式是通过渐变。渐变让我们可以将一种颜色混合到另一种颜色中,创造出一些令人惊叹的图形效果。

Fabric 支持渐变设置所有对象的填充或描边属性。为了给对象设置渐变,首先创建渐变,然后将其分配给填充或描边。

var circle = new fabric.Circle({
  left: 100,
  top: 100,
  radius: 50
});

var gradient = new fabric.Gradient({
  type: 'linear',
  gradientUnits: 'pixels', // or 'percentage'
  coords: { x1: 0, y1: 0, x2: 0, y2: circle.height },
  colorStops:[
    { offset: 0, color: '#000' },
    { offset: 1, color: '#fff'}
  ]
})

circle.set('fill', gradient);

在上面的示例中,我们在 100,100 位置创建了一个圆,半径为 50px。然后,我们将其填充设置为跨越该圆圈整个高度的渐变,从黑色到白色。

渐变选项对象有 2 个主要属性,坐标和颜色停止。coords预计至少有 2 个坐标对(x1, y1 和 x2, y2)将定义渐变如何在对象上扩展,同时colorStops是一个定义渐变颜色的数组。数组中的每种颜色都由offset表示其在渐变中的位置的 a 定义,acolor定义颜色本身并最终定义为一个opacity属性。您可以根据需要定义任意数量的色标,只要它们的偏移范围从“0”到“1”。`0` 表示渐变的开始,`1` 表示结束。坐标相对于对象的左上角,所以圆的最高点是0,最低点是circle.height
您可以指定类型linearradial获得 2 种不同的渐变,并且 gradientUnits默认为像素,但可以指定为“百分比”。`percentage` 将允许以对象大小的百分比指定渐变大小,其中 `1` 是对象大小的 100%。此设置对于根据文本内容更改宽度或高度
的对象很有用。fabric.Text

这是一个从左到右的红蓝渐变示例:

  var gradient = new fabric.Gradient({
    type: 'linear',
    gradientUnits: 'pixels', // or 'percentage'
    coords: { x1: 0, y1: 0, x2: circle.width, y2: 0 },
    colorStops:[
      { offset: 0, color: 'red' },
      { offset: 1, color: 'blue'}
    ]
  })
 // or in percentage
  var gradient = new fabric.Gradient({
    type: 'linear',
    gradientUnits: 'percentage',
    coords: { x1: 0, y1: 0, x2: 1, y2: 0 },
    colorStops:[
      { offset: 0, color: 'red' },
      { offset: 1, color: 'blue'}
    ]
  })

这是一个 5 档的彩虹渐变,颜色以 20% 的间隔跨越:

var gradient = new fabric.Gradient({
  type: 'linear',
  gradientUnits: 'pixels', // or 'percentage'
  coords: { x1: 0, y1: 0, x2: circle.width, y2: 0 },
  colorStops:[
    { offset: 0, color: 'red' },
    { offset: 0.2, color: 'orange' },
    { offset: 0.4, color: 'yellow' },
    { offset: 0.6, color: 'green' },
    { offset: 0.8, color: 'blue' },
    { offset: 1, color: 'purple' }
  ]
});

文本

如果您不仅想在画布上显示图像和矢量形状,还想显示文本怎么办?面料有你!遇见fabric.Text对象。

我们在 Fabric 中提供文本抽象有两个原因。首先,是允许以面向对象的方式处理文本。像往常一样,本机画布方法只允许在非常低的级别上填充或描边文本。通过实例化fabric.Text实例,我们可以像处理任何其他 Fabric 对象一样处理文本——移动它、缩放它、更改它的属性等等。

第二个原因是提供比 canvas 给我们的功能更丰富的功能。一些织物添加包括:

  • 多行支持不幸的是,原生文本方法只是忽略了新行。

  • 文本对齐左、中、右。在处理多行文本时很有用。

  • 文本背景背景也尊重文本对齐方式。

  • 文字装饰下划线、上划线、删除线。

  • 行高在处理多行文本时很有用。

  • 字符间距使文本更紧凑或更间距

  • 范围将颜色和属性应用于文本对象的子范围

  • 多字节支持表情!

  • 在使用交互式类进行画布编辑时,您可以直接在画布上键入文本

你好世界的例子怎么样?

var text = new fabric.Text('hello world', { left: 100, top: 100 }); canvas.add(文本);

这是正确的!fabric.Text在画布上显示文本就像在所需位置添加实例一样简单。如您所见,唯一需要的第一个参数是实际的文本字符串。第二个参数是常用的选项对象,它可以具有任何常用的 left、top、fill、opacity 等属性。

当然,文本对象也有自己独特的与文本相关的属性。让我们看看其中的一些:

字体系列

默认设置为“Times New Roman”,此属性允许我们更改用于渲染文本对象的字体系列。更改它会立即使文本以新字体呈现。

var comicSansText = new fabric.Text("我在 Comic Sans", { 
  fontFamily: 'Comic Sans' });

字体大小

字体大小控制渲染文本的大小。请注意,与 Fabric 中的其他对象不同,您不能直接更改文本的宽度/高度属性。相反,您需要更改“fontSize”值以使文本对象更大或更小。要么,要么你总是可以使用 scaleX/scaleY 属性。

var text40 = new fabric.Text("我在 fontSize 40", { 
  fontSize: 40 }); var text20 = new fabric.Text("我在 fontSize 20", { 
  fontSize: 20 });

字体重量

字体粗细允许使文本看起来更厚或更薄。就像在 CSS 中一样,您可以使用关键字(“normal”、“bold”)或数字(100、200、400、600、800)。请注意,您是否可以使用某些粗细取决于所选字体的该粗细的可用性。如果您使用远程字体,则需要确保提供正常和粗体(以及任何其他所需的粗细)字体定义。

var normalText = new fabric.Text("我是普通文本", { 
  fontWeight: 'normal' }); var boldText = new fabric.Text("我是粗体", { 
  fontWeight: 'bold' });

文字装饰

文本装饰允许在文本中添加下划线、上划线或删除线。这与 CSS 类似,但 Fabric 更进一步,允许将上述任意组合一起使用。所以你可以有一个既是下划线又是上划线的文本,或者是下划线和删除线,等等。

var underlineText = new fabric.Text("我是下划线文本", { 
  underline; true }); var strokeThroughText = new fabric.Text("我是描边文本", { 
  linethrough: true }); var overlineText = new fabric.Text("我是一个上划线文本", { 
  overline: true });

阴影

此属性在 1.3.0 版之前称为“textShadow”

文本阴影由 4 个组件组成:颜色、水平偏移、垂直偏移和模糊大小。如果您在 CSS 中使用过阴影,这可能看起来很熟悉。通过更改这些值可以实现许多组合。

var shadowText1 = new fabric.Text("我是带阴影的文本", { 
  shadow: 'rgba(0,0,0,0.3) 5px 5px 5px' }); var shadowText2 = new fabric.Text("还有另一个阴影", { 
  shadow: 'rgba(0,0,0,0.2) 0 0 5px' }); var shadowText3 = new fabric.Text("Lorem ipsum dolor sit", { 
  shadow: 'green -5px -5px 3px' });

字体样式

字体样式可以是 2 个值之一:正常或斜体。这类似于同名 CSS 属性。

var italicText = new fabric.Text("一个非常漂亮的斜体文本", { 
  fontStyle: 'italic', 
  fontFamily: 'Delicious' }); var anotherItalicText = new fabric.Text("另一个斜体文本", { 
  fontStyle: 'italic', 
  fontFamily: 'Hoefler Text' });

行程和行程宽度

通过组合 stroke(笔触的颜色)和 strokeWidth(它的宽度),你可以在你的文本上实现一些有趣的效果。这里有几个例子:

var textWithStroke = new fabric.Text("带有笔画的文本", { 
  stroke: '#ff1318', 
  strokeWidth: 1 }); var loremIpsumDolor = new fabric.Text("Lorem ipsum dolor", { 
  fontFamily: 'Impact', 
  stroke: '#c3bfbf', 
  strokeWidth: 3 });

文本对齐

处理多行文本时,文本对齐非常有用。对于单行文本,边界框的宽度始终与该行的宽度完全匹配,因此无需对齐。

允许的值为“left”、“center”和“right”。

var text = '这是\n多行\n文本\n右对齐!'; var alignedRightText = new fabric.Text(text, { 
  textAlign: 'right' });

线高

CSS 领域可能熟悉的另一个属性是 lineHeight。它允许我们更改多行文本中文本行之间的垂直间距。在以下示例中,第一个文本块的 lineHeight 为 3,第二个文本块的 lineHeight 为 1。

var lineHeight3 = new fabric.Text('Lorem ipsum ...', { 
  lineHeight: 3 }); var lineHeight1 = new fabric.Text('Lorem ipsum ...', { 
  lineHeight: 1 });

文字背景颜色

最后, textBackgroundColor 允许为文本提供背景。请注意,背景仅填充文本字符占用的空间,而不是整个边界框。这意味着文本对齐会改变文本背景的呈现方式。行高也是如此,因为背景尊重行之间的垂直空间,由 lineHeight 创建。

var text = '这是\n 多行\ntext\nwith\ncustom lineheight\n&background'; var textWithBackground = new fabric.Text(text, { 
  textBackgroundColor: 'rgb(0,200,0)' });

活动

事件驱动架构是框架内一些惊人的功能和灵活性的基础。Fabric 也不例外,它提供了一个广泛的事件系统,从低级“鼠标”事件到高级对象事件。

这些事件使我们能够利用画布上发生的各种动作的不同时刻。想知道鼠标何时被按下?只需观察“鼠标:按下”事件。什么时候将对象添加到画布上?“对象:添加”是给你的。那么当整个画布重新渲染时呢?只需使用“之后:渲染”。

事件 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); });

我们将事件“mouse:down”事件侦听器添加到画布上,并为其提供一个事件处理程序,该处理程序将记录事件起源的坐标。换句话说,它将记录鼠标在画布上的确切位置。事件处理程序接收一个选项对象,该对象具有 2 个属性:e— 原始事件,以及target— 画布上的单击对象(如果有)。该事件始终存在,但target仅在您确实单击画布上的某个对象时才存在。target也只传递给有意义的事件处理程序例如,对于“mouse:down”而不是“after:render”(这表示整个画布被重新绘制)。

canvas.on('mouse:down', function(options) { 
  if (options.target) { 
    console.log('an object was clicked!', options.target.type); 
  } });

上面的例子将记录“一个对象被点击了!” 如果您单击一个对象。它还将显示单击的对象的类型。

那么 Fabric 中还有哪些其他事件可用?好吧,从鼠标级别来看,有“ mouse:down ”、“ mouse:move ”和“ mouse:up ”。从通用的,有“ after:render ”。然后是与选择相关的事件:“ before:selection:cleared ”、“ selection:created ”、“ selection:cleared ”。最后,对象:“对象:修改”,“对象:选定”,“对象:移动”,“对象:缩放”,“对象:旋转”,“对象:添加”,

请注意,每次移动(或缩放)对象甚至移动一个像素时,都会连续触发诸如“object:moving”(或“object:scaling”)之类的事件。另一方面,像“object:modified”或“selection:created”这样的事件仅在动作结束时触发(对象修改或选择创建)。

请注意我们是如何将事件直接附加到画布 ( canvas.on('mouse:down', ...)) 上的。正如您可以想象的那样,这意味着事件的范围都是画布实例。如果页面上有多个画布,则可以将不同的事件侦听器附加到每个画布。他们都是独立的,只尊重分配给他们的事件。

为方便起见,Fabric 进一步改进了事件系统,并允许您将侦听器直接附加到画布对象。让我们来看看:

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”。类似地,我们可以使用“修改”事件(附加到画布时为“object:modified”)、“旋转”事件(附加到画布时为“object:rotating”)等等。

查看此事件演示以更广泛地探索 Fabric 的事件系统。

阅读第 3 部分


自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论