返回介绍

Part 1:Objects 对象、Canvas 画布、Images 图像、Paths 形状

发布于 2024-11-03 22:57:33 字数 22602 浏览 0 评论 0 收藏 0

今天,我想向您介绍 Fabric.js —一个功能强大的 JavaScript 库,使使用 HTML5 canvas 的工作变得轻而易举。 Fabric 提供了画布缺少的对象模型,以及 SVG 解析器,交互性层和一整套其他必不可少的工具。这是一个完全开放源代码的项目,已获得麻省理工学院 (MIT)的许可,多年来有许多贡献。

在发现使用原生 canvas API 的烦恼之后,Fabric 于 2010 年左右开始。最初的作者正在为 printio.ru 创建一个交互式设计编辑器-他的创业公司允许用户设计自己的 服装。他们想要的那种交互性仅在那时存在于 Flash 应用程序中。即使是现在,也很少有人能接近 Fabric 所能提供的功能。

让我们仔细看看!

Why fabric? 什么是 Fabric?

如今,Canvas 允许我们在网络上创建一些绝对惊人的图形。但是它提供的 API 是极令人失望地低级。如果我们只是想在画布上绘制一些基本形状而忘记它们,那是一回事。但是,只要需要进行任何形式的交互,随时更改图片或绘制更复杂的形状,情况就会发生巨大变化。

Fabric 旨在解决这个问题。

原生 canvas 方法仅允许我们触发简单的图形命令,盲目地修改整个画布位图。想画一个矩形吗?使用 fillRect(left,top, width,height)。要画一条线吗?使用 moveTo(left,top)和 lineTo(x,y)的命令组合。就像我们正在用刷子画画布一样, 在很少的控制下将越来越多的油层涂在上面。

Fabric 没有在如此低的级别上进行操作,而是在原生 canvas 方法之上提供了简单但功能强大的对象模型。它负责画布的状态和渲染,并让我们直接使用“对象”。

让我们看一个简单的例子来说明这种差异。假设我们要在画布上的某个地方绘制一个红色矩形。这是我们如何使用原生 canvas API 进行的操作。

<template>
  <div class="app">
    <canvas id="canvas" width="300" height="200"></canvas>
  </div>
</template>

<script>
export default {
  mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    ctx.fillRect(100,100,20,20);
  },
};
</script>
<style>
</style>

现在,让我们看一下使用 Fabric 进行相同的操作:

<template>
  <div class="app">
    <canvas id="canvas" width="300" height="200"></canvas>
  </div>
</template>

<script>
import {fabric} from "fabric"
export default {
  mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
    });
    canvas.add(rect);
  }
}
</script>

<style>

</style>

屏幕快照 2021-03-19 上午 11.36.35

此时,大小几乎没有区别-这两个示例非常相似。但是,您已经可以看到使用画布的方法有多么不同。使用原生方法,我们可以在上下文上操作——一个表示 整个画布位图的对象。在 Fabric 中,我们对对象进行操作——实例化它们,更改其属性并将其添加到画布。您会看到这些对象是 Fabric 土地上的一等公 民。

但是渲染纯红色矩形实在太无聊了。我们至少可以从中获得一些乐趣!也许,稍微旋转一下?

让我们尝试 45 度。首先,使用原生 canvas 方法:

mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    // ctx.fillRect(100,100,20,20);

    //偏移角度
    ctx.translate(100,100);
    ctx.rotate(Math.PI/180*45);
    ctx.fillRect(-10,-10,20,20);
  },

image-20210319135900911

现在使用 Fabric:

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
      angle:45 //偏移角度
    });
    canvas.add(rect);
  }

image-20210319135813749

(注意这里两个是不一样的,下面作者说使用 canvas 时为了让为了矩形保持在原位需要偏移(-10,-10)但其实不需要这样做。最后直接 ctx.fillRect(0,0,20,20) 才是一样的效果。)

这里发生了什么?

我们在 Fabric 中要做的就是将对象的“角度”值更改为 45。但是,使用原生 canvas 方法,事情会变得更加“有趣”。请记住,我们不能对对象 进行操作。相反,我们调整整个画布位图的位置和角度(ctx.translate,ctx.rotate)以适合我们的需求。然后,我们再次绘制矩形,但 要记住适当地偏移位图(-10,-10),以便它仍在(100,100)点位处渲染。作为一项额外的练习,在旋转画布位图时,我们必须将度数转换为弧度。

我相信您已经开始确切地了解为什么存在 Fabric 以及它隐藏了多少个低级样板。

但是,让我们看看另一个示例——跟踪画布状态。

如果在某个时候,我们想将现在熟悉的红色矩形移动到画布上稍有不同的位置,该怎么办?在无法对对象进行操作的情况下,我们将如何做到这一点?我们仅在画布位图上调用另一个 fillRect 吗?

不完全是。调用另一个 fillRect 命令实际上会在画布上已绘制的内容的上方绘制矩形。还记得我之前提到过用画笔绘画吗?为了“移动”它,我们需要首先擦除先前绘制的内容,然后在新位置绘制矩形。

mounted() {
    var canvasEl = document.getElementById("canvas");
    canvasEl.style.background = 'grey'
    var ctx = canvasEl.getContext("2d");
    ctx.fillStyle = 'red';
    // ctx.fillRect(100,100,20,20);

    //位移
    ctx.strokeRect(100,100,20,20);
    ctx.clearRect(0,0,canvasEl.width,canvasEl.height);
    ctx.fillRect(20,50,20,20);
  },

那我们将如何使用 Fabric 做到这一点?

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      fill:"red",
      width:20,
      height:20,
    });
    canvas.add(rect);
        // 位移
    rect.set({left:20,top:50});
    canvas.renderAll()
  }

image-20210319143139135

注意一个非常重要的区别。使用 Fabric,在尝试修改任何内容前,我们不再需要删除内容。我们仍然使用对象,只需更改其属性,然后重新渲染画布即可获得“新图片”。

Objects 对象

我们已经看到了如何通过实例化 fabric.Rect 构造函数来使用矩形。但是,Fabric 当然也涵盖了所有其他基本形状——圆形,三角形,椭圆 形等。它们全部都在 fabric“命名空间”下暴露为 fabric.Circle,circle.Triangle,fabric.Ellipse 等。

Fabric 提供的 7 中基本形状:

想画一个圆吗?只需创建一个圆形对象,然后将其添加到画布即可。与其他任何基本形状相同:

 mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    // 画圆
    var circle = new fabric.Circle({
      radius:20,
      fill:"green",
      left:100,
      top:100,
    })
    // 画三角形
    var triangle = new fabric.Triangle({
      width:20,
      height:30,
      fill:'blue',
      left:50,
      top:50
    })

    canvas.add(circle,triangle)
  }

image-20210319151214103

我们有一个在(100,100) 位置绘制的绿色圆圈和在(50,50) 位置绘制的蓝色三角形。

Manipulating objects 操作对象

创建图形对象(矩形,圆形或其他形状)当然只是开始。在某个时候,我们可能会想要修改那些对象。也许某些动作将需要触发状态改变,或者播放某种形式的动画。或者,我们可能想更改某些鼠标交互时的对象属性(颜色,不透明度,大小,位置)。

Fabric 负责我们的画布渲染和状态管理。我们只需要修改对象本身即可。

前面的示例演示了 set 方法以及如何调用 set({left:20,top:50}) 从先前位置“移动”对象。以类似的方式,我们可以更改对象的任何其他属性。但是这些属性是什么?

好了,正如您所期望的,这些都与定位有关——left、top;尺寸——width、height;渲染——fill、opacity、 stroke、strokeWidth;缩放和旋转——scaleX、scaleY、angle;甚至与翻转相关的内容——flipX、flipY 和倾斜 的 skewX、skewY

是的,在 Fabric 中创建翻转对象就像将 flip *属性设置为 true 一样容易。

您可以通过 get 方法读取任何这些属性,并通过 set 进行设置。让我们尝试更改红色矩形的一些属性:

mounted(){
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var rect = new fabric.Rect({
      left:100,
      top:100,
      width:20,
      height:20,
    });
    canvas.add(rect);

    rect.set("fill","red");
    rect.set({strokeWidth:5,stroke:"rgba(100,200,200,0.5)"});
    rect.set("angle",60).set("flipY",true);
}

image-20210319154452512

首先,我们将“填充”值设置为“红色”,实质上使对象变成红色。下一条语句同时设置“ strokeWidth”和“ stroke”值,为矩形提供 5px 浅绿色的边框。最后,我们要更改“ angle”和“ flipY”属性。请注意,这 3 条语句中的每条语句如何使用,略有不同的语法。

这证明了 set 是一种通用方法。您可能会经常使用它,因此它应该尽可能地方便。

我们已经介绍了 setter,那么 getter 呢?很明显,既有通用的 get 方法,也有许多特定的 get * 方法。要读取对象的“宽度”值,可以使用 get('width')getWidth() 。要获取“ scaleX”值,请使用 get('scaleX')getScaleX() ,依此类推。对于每个“公共”对象属性(例如“ stroke”,“ strokeWidth”,“ angle”等),都有一种类似于 getWidth 或 getScaleX 的方法。

您可能会注意到,在先前的示例中,对象是使用与我们刚在 set 方法中使用过的配置相同的配置哈希值创建的。那是因为它是完全一样的。您可以在创建时“配置”对象,或在之后使用 set 方法:

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect({
      width: 10,
      height: 20,
      fill: "#f55",
      opacity: 0.7,
    });

    // or functionally identical

    //var rect = new fabric.Rect();
    //rect.set({ width: 10, height: 20, fill: "#f55", opacity: 0.7 });

    canvas.add(rect);
 }

image-20210319160446768

Default options 默认参数

此时,您可能会问——当我们创建一个对象而不传递任何“配置”对象时会发生什么。它仍然具有那些属性吗?

是的当然。 Fabric 中的对象始终具有一组默认属性。在创建过程中被忽略时,将“赋予”对象的此默认属性集。我们可以尝试自己看看:

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect()
    console.log(rect.get("width"));//0
    console.log(rect.get('height'));//0
    console.log(rect.get("left"));//0
    console.log(rect.get("top"));//0
    console.log(rect.get("fill"));//rgb(0,0,0)
    console.log(rect.get("stroke"));//null
    console.log(rect.get("opacity"));//1

    canvas.add(rect);
}

image-20210319161246603

我们的矩形具有一组默认属性。它的位置是(0,0),是黑色,完全不透明,没有笔触,没有尺寸(宽度和高度均为 0)。由于没有尺寸,因此我们无法在画布上看到它。但是给它任何宽度/高度的正值肯定会在画布的左/上角显示一个黑色的矩形。

mounted() {
    var canvas = new fabric.Canvas("canvas", { backgroundColor: "grey" });

    var rect = new fabric.Rect({width:20,height:20})
    console.log(rect.get("width"));//20
    console.log(rect.get('height'));//20
    console.log(rect.get("left"));//0
    console.log(rect.get("top"));//0
    console.log(rect.get("fill"));//rgb(0,0,0)
    console.log(rect.get("stroke"));//null
    console.log(rect.get("opacity"));//1

    canvas.add(rect);
}

image-20210319161433016

Hierarchy and Inheritance 层次结构和继承

构造对象不仅彼此独立存在。它们形成了非常精确的层次结构。

大多数对象都从根 fabric.Object 继承。 fabric.Object 几乎代表一个二维形状,位于二维画布平面中。它是一个具有 left / top 和 width / height 属性以及一系列其他图形特征的实体。我们在对象上看到的那些属性(fill、stroke、angle、opacity、flip *等)对于所有从 fabric.Object 继承的 Fabric 对象都是共有的。 这种继承使我们可以在 fabric.Object 上定义方法,并在所有子“类”之间共享它们。例如,如果要在所有对象上都具有 getAngleInRadians 方法,则只需在 fabric.Object.prototype 上创建它:

mounted() {
    fabric.Object.prototype.getAngleInRadians = function(){
      return this.get("angle")/180*Math.PI;
    };

    var rect =new fabric.Rect({angle:45});
    console.log(rect.getAngleInRadians());//0.7853981633974483

    var circle = new fabric.Circle({angle:30,radius:10});
    console.log(circle.getAngleInRadians());//0.5235987755982988

    console.log(circle instanceof fabric.Circle);//true
    console.log(circle instanceof fabric.Object);//true
    }

如您所见,方法立即在所有实例上可用。

虽然子“类”继承自 fabric.Object ,但它们通常还定义了自己的方法和属性。例如, fabric.Circle 需要具有“半径”属性。稍后我们将要讨论的 fabric.Image 需要具有 getElement / setElement 方法,以访问/设置图像实例所源自的 HTML <img> 元素。

对于高级项目,使用原型来获取自定义渲染和行为是很常见的。

Canvas 画布

现在我们已经详细介绍了对象,让我们回到画布上。

如果创建画布对象,则可以在所有 Fabric 示例中看到的第一件事—— new fabric.Canvas('...') 。 fabric.Canvas 用作 <canvas> 元素的包装,并负责管理该特定画布上的所有 Fabric 对象。它接受元素的 ID,并返回 fabric.Canvas 的实例。

我们可以在其上添加对象,从中引用或删除它们:

mounted(){
        var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect();

//在 canvas 上添加一个对象
canvas.add(rect); 

//canvas 上添加的第一个对象
canvas.item(0); 

//获取 canvas 上的所有对象
//这里需要注意,使用 canvas.item 或者 canvas.item() 获取所有对象是没有用的
canvas.getObjects(); 

//删除指定的对象
canvas.remove(rect); 
}

虽然管理对象是 fabric.Canvas 的主要目的,但它也可以用作配置画布本身。是否需要为整个画布设置背景颜色或图像?将所有内容都剪切到某个区域?设置不同的宽度/高度?指定画布是否是交互式的?所有这些选项(以及其他选项)都可以在创建时或之后在 fabric.Canvas 上设置:

mounted(){
        var canvas = new fabric.Canvas("canvas",{
      backgroundColor:'rgb(100,100,200)',
      selectionColor:'blue',
      selectionLineWidth:2
    })

    var rect = new fabric.Rect()
    canvas.add(rect);
}

image-20210319171222435

或者

mounted(){
        var canvas = new fabric.Canvas("canvas");
        canvas.setBackgroundColor("rgb(100,200,200)");
    canvas.onFpsUpdate = function(){
      console.log('success!');
    }

    var rect = new fabric.Rect();
    canvas.add(rect);
}

image-20210319174149116

Interactivity 互动性

当我们讨论 canvas 元素时,让我们谈谈交互性。内置的 Fabric 的独特功能之一是,在我们刚刚查看的所有便捷对象模型之上,都具有一层交互性。

存在对象模型以允许以编程方式访问和操纵画布上的对象。但是在外部,在用户级别上,有一种方法可以通过鼠标(或触摸设备上的触摸)来操纵这些对象。通过 new fabric.Canvas('...') 初始化画布后,就可以选择对象,对其进行拖动,缩放或旋转它们,甚至将它们组合在一起以在一个块中进行操作!

mounted(){
        var canvas = new fabric.Canvas("canvas");
        canvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20});
    canvas.add(rect);
}

原始画布如下:

可以通过鼠标操作对象,如下:

image-20210319180035162

如果我们希望用户允许在画布上拖动某些东西(例如图像),我们要做的就是初始化画布并在其上添加一个对象。无需其他配置或设置。

为了控制这种交互性,我们可以在画布上结合使用 Fabric 的“ selection”布尔属性和单个对象的“ selectable”布尔属性。

mounted(){
        var canvas = new fabric.Canvas("canvas");
    canvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20,left:100,top:50});
    var circle = new fabric.Circle({radius:30,fill:"rgb(100,100,200)"})

    //设置不能群体选择
    canvas.selection = false;
    //设置 rect 对象无法被选中
    rect.set("selectable",false);

    canvas.add(rect,circle);
}

这时候只有圆才能被选中操作。

image-20210319180137348

但是,如果您根本不想要这样的交互层,该怎么办?如果是这种情况,您始终可以用 fabric.StaticCanvas 替换 fabric.Canvas 。初始化的语法完全相同。您只能使用 StaticCanvas 而不是 Canvas

mounted(){
        var staticCanvas = new fabric.StaticCanvas("canvas");

    staticCanvas.setBackgroundColor("rgb(100,200,200)");

    var rect = new fabric.Rect({width:20,height:20,left:100,top:50});
    var circle = new fabric.Circle({radius:30,fill:"rgb(100,100,200)"})

    staticCanvas.add(rect,circle);
}

image-20210319180927556

这将创建一个“较轻”版本的画布,而没有任何事件处理逻辑。请注意,您仍然可以使用整个对象模型-添加对象,删除或修改它们,以及更改任何画布配置-所有这些仍然有效。只是事件处理不见了。

稍后,当我们遍历自定义构建选项时,您会看到,如果只需要 StaticCanvas ,您甚至可以创建一个轻量级的 Fabric。如果您需要非交互式图表或应用程序中带有过滤器的非交互式图像,这可能是一个不错的选择。

Images 图像

说起图片… 在画布上添加矩形和圆形很有趣,但是为什么我们不玩一些图像呢?就像您现在想像的那样,Fabric 使这变得容易。让我们实例化 fabric.Image 对象并将其添加到画布:

<template>
  <div class="app">
    <canvas id="canvas" width="400" height="300" ></canvas>
    <img src="../src/assets/logo.png" alt="picture" id="img" width="100" height="auto" >
  </div>
</template>

<script>
import { fabric } from "fabric";
export default {
  mounted() {
       var canvas = new fabric.Canvas("canvas");
    var imgElement = document.getElementById("img");

    var imgInstance = new fabric.Image(imgElement,{
      left:100,
      top:10,
      angle:30,
      opacity:0.85
    });
    canvas.add(imgInstance);
  },
};
</script>

<style>
</style>

注意我们如何将图像元素传递给 fabric.Image 构造函数。这将创建一个 fabric.Image 实例,其外观与文档中的图像相似。此外,我们立即将 left/top 设置为 100/10,角度设置为 30,不透明度设置为 0.85。一旦添加到画布上,图像将在 100,10 位置,30 度角处渲染,并且稍微透明!不错。

image-20210319184929008

现在,如果我们在文档中没有真正的图像,而只是图像的 URL,该怎么办?没问题让我们看看如何使用 fabric.Image.fromURL :

<template>
  <div class="imageFilter">
      <canvas id="canvas" width="300" height="300"></canvas>
  </div>
</template>

<script>
import {fabric} from "fabric"
export default {
    mounted(){
        var canvas = new fabric.Canvas("canvas",{backgroundColor:'#ddd'})

        fabric.Image.fromURL("https://ss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/zhidao/wh=450,600/sign=093b8529572c11dfde84b72756174ee6/aa18972bd40735fa8d2f0c739e510fb30e240818.jpg",(img)=>{
            canvas.add(img);
        })
    }

}
</script>

<style>

</style>

image-20210320155638670

看起来很简单,不是吗?只需使用图像的 URL 调用 fabric.Image.fromURL ,然后给它一个回调即可在加载和创建图像后调用。回调函数接收已创建的 fabric.Image 对象作为第一个参数。此时,您可以将其添加到画布,或者可能先更改,然后再添加到画布:

mounted(){
  var canvas = new fabric.Canvas("canvas",{backgroundColor:'#ddd'})

  fabric.Image.fromURL("5fa8d2f0c739e510fb30e240818.jpg",(img)=>{
  img.scale(0.5).set("flipX",true)
    canvas.add(img);
  })
}

image-20210320155726595

Paths

我们研究了简单的形状,然后是图像。但是更复杂,更丰富的形状和内容呢?

结识实力雄厚的一对——路径和群组。

Fabric 中的路径代表可以用其他方式填充,描边和修改的形状轮廓。路径由一系列命令组成,这些命令实际上模仿了笔从一个点到另一个点的过程。借 助“移动”,“线”,“曲线”或“弧”等命令,路径可以形成难以置信的复杂形状。在路径组(PathGroup's)的帮助下,可能性更大。

Fabric 中的路径非常类似于 SVG <path> 元素。它们使用相同的命令集,可以从 <path> 元素创建它们,并将其序列化。稍后我们将更仔细地研究序列化和 SVG 解析,但是现在值得一提的是,您可能很少会手动创建 Path 实例。相反,您将使用 Fabric 的内置 SVG 解析器。但是要了解什么是 Path 对象,让我们尝试手动创建一个简单的对象:

<template>
  <div class="app">
    <canvas id="canvas" width="400" height="400" ></canvas>
  </div>
</template>

<script>
import { fabric } from "fabric";
export default {
  mounted() {
    var canvas = new fabric.Canvas("canvas",{backgroundColor:"grey"});
    var path = new fabric.Path("M 0 0 L 200 100 L 170 200 z");
    path.set({left:120,top:120});
    canvas.add(path);
  },
};
</script>

<style>
</style>

image-20210320100814266

我们正在实例化 fabric.Path 对 象,并向其传递一串路径指令。虽然看起来很神秘,但实际上很容易理解。 “ M”表示“移动”命令,并指示隐形笔移动到(0、0) 点。 “ L”代表“线”,使笔画一条线到(200、100) 点。然后,另一个“ L”创建一条到(170、200) 的线。最后,“ z”告诉强制绘图笔关闭电流路径并最终确定形状。结果,我们得到一个三角形。

由于 fabric.Path 就像 Fabric 中的任何其他对象一样,我们也能够更改其某些属性。但是我们可以对其进行更多修改:

mounted() {
  var canvas = new fabric.Canvas("canvas",{backgroundColor:"#ddd"});
  var path = new fabric.Path("M 0 0 L 300 100 L 200 300 z");
  path.set({ fill: "red", stroke: "green", opacity: 0.5 });
  canvas.add(path);
}

image-20210320101536800

出于好奇,让我们看一下稍微复杂一些的路径语法。您将看到为什么手动创建路径可能不是最好的主意。

mounted(){
var canvas = new fabric.Canvas("canvas",{backgroundColor:"#ddd"});

var path = new fabric.Path('M121.32,0L44.58,0C36.67,0,29.5,3.22,24.31,8.41\
c-5.19,5.19-8.41,12.37-8.41,20.28c0,15.82,12.87,28.69,28.69,28.69c0,0,4.4,\
0,7.48,0C36.66,72.78,8.4,101.04,8.4,101.04C2.98,106.45,0,113.66,0,121.32\
c0,7.66,2.98,14.87,8.4,20.29l0,0c5.42,5.42,12.62,8.4,20.28,8.4c7.66,0,14.87\
-2.98,20.29-8.4c0,0,28.26-28.25,43.66-43.66c0,3.08,0,7.48,0,7.48c0,15.82,\
12.87,28.69,28.69,28.69c7.66,0,14.87-2.99,20.29-8.4c5.42-5.42,8.4-12.62,8.4\
-20.28l0-76.74c0-7.66-2.98-14.87-8.4-20.29C136.19,2.98,128.98,0,121.32,0z');

canvas.add(path.set({ left: 100, top: 200 }));
}

哦,孩子,这是怎么回事?

嗯,“ M”仍然代表“移动”命令,因此笔在“ 121.32,0”点开始绘制过程。然后是“ L”命令,将其设置为“ 44.58,0”。到目前为止,一切都很好。下一步是什么? “ C”命令,代表“三次贝塞尔曲线”。它使笔绘制贝塞尔曲线从当前点到“ 36.67,0”之一。它以“ 29.5,3.22”作为控制点在行的开头,并以“ 24.31,8.41”作为控制点在行的末尾。然后,接下来是许多其他三次贝塞尔曲线命令,这些命令最终创建了一个漂亮的箭头形状。

很有可能,您不会直接使用此类“野兽”。相反,您可能想使用 fabric.loadSVGFromStringfabric.loadSVGFromURL 方法之类的方法来加载整个 SVG 文件,并让 Fabric 的 SVG 解析器完成遍历所有 SVG 元素并创建相应的 Path 对象的工作。

说到整个 SVG 文档,虽然 Fabric 的 Path 通常代表 SVG <path> 元素,但是 SVG 文档中经常出现的一组路径被表示为 Groups( fabric.Group 实例)。可以想象,Group 只是一组 Paths 和其他对象。而且由于 fabric.Group 继承自 fabric.Object ,因此可以像其他任何对象一样将其添加到画布,并以相同的方式进行操作。

就像使用 Paths 一样,您可能不会直接使用它们。但是,如果您在解析 SVG 文档后偶然发现一个文档,您将确切知道它是什么以及它的作用。

Afterword 后记

我们仅介绍了 Fabric 可能实现的功能。现在,您可以轻松创建任何简单的形状,复杂的形状,图像;将它们添加到画布上,然后以所需的任何方式进行 修改(positions、dimensions、angles、colors、strokes、opacity),即可为其命名。

在本系列的下一部分中,我们将研究与 groups 的合作。动画片、文本、SVG 解析、渲染、序列化、事件、图像滤镜。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文