4

聊聊 fabric.js Group 对象

 1 year ago
source link: https://jelly.jd.com/article/62bd77e952d2600188a9fdac
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
聊聊 fabric.js Group 对象
上传日期:2022.07.01
Fabric.js 续集:在之前的文章《图形编程 Fabric.js 复合图形实现记录》中已经介绍过 `fabric.js` 了,这篇就来讲讲 Group 的使用场景,并且解决一些实际问题。

在之前的文章 《图形编程 Fabric.js 复合图形实现记录》 中已经介绍过 fabric.js 了,这里就不再赘述了。感兴趣或者不清楚的可以翻看之前的文章。

Group 实际使用

在用 Group 之前先看下 Group 常用的方法:

1. getObjects()         // 获取所有子对象
2. item(idx)            // 获取某个子对象
3. addWithUpdate()      // 将对象添加到组;然后重新计算组的尺寸、位置。
4. removeWithUpdate()   // 将对象从组里删除;然后重新计算组的尺寸、位置。
5. toActiveSelection()  // 将组转换为选区对象
6. setObjectsCoords()   // 设置组内所有对象的坐标
7. toObject()           // 将对象导出为 json
8. fromObject()         // 从 json 恢复为对象

了解了 Group 的常用方法我们能用它做什么呢?

说白了就是可以将 Fabric 多个对象当成一个对象来处理。比如:整体移动、缩放、旋转这个组合,甚至改变颜色、透明度、边界等特性。

正如上一篇文章提到的复合对象就是利用了分组的特性将“基础图形+文本”组成一个分组,方便移动、缩放等等。

那既然上一篇已经说过了这篇说啥呢? 还是记录开发过程中遇到的问题。

Group 内子对象绝对位置计算

对于 Group 来说有个隐藏的特点需要注意,这也是我开发过程中被坑了的地方:

  • 不论 Group原点设置在左上角还是中心点,Group 内部子对象的原点始终都是相对于 Group 中心点计算的,也就是说 Group 内部的对象获取到的位置其实是相对于 Group 中心点的相对数值。而且还无法更改这个设定。

接下来展开讲讲我遇到的问题:

  • 问:对象相对于画布的位置可以从它的 lefttop 属性中获取,但如果对象在 Group 中,内部对象的原点则是相对于 Group 中心点的。那怎么计算它们相对于整个画布的实际位置呢?

首先我们来画个图分析一下 Group 和内部对象的关系:

主要根据 Group 原点的不同有以下三种关系:

下面的图只分析了内部对象相对于画布的 left 计算方式,top 计算方式同理,后面会附计算代码。 此分析还有个前提:Group 内部的对象原点都是左上角。即:originX: leftoriginY: top

  1. Group 原点在左上角 当 Group 原点在左上角时,Group 相对于画布的位置就是从左上角计算的即下图蓝色 group.leftobj.left 获取到的是子对象到 Group 中心的距离,再加上 Group 宽度的一半就是子对象距离整个画布左侧的距离。
    11d36b920f7b79f2.png
  2. Group 原点在中心 当 Group 原点在中心时,Group 相对于画布的位置就是从中心点计算的即下图蓝色 group.leftobj.left 获取到的还是子对象到 Group 中心的距离,两者相加便是子对象距离还不左侧的距离。
    11d36b920f7b79f2.png
  3. Group 原点在右下角 当 Group 原点在右下角是,Group 相对于画布的位置就是从右侧开始计算了还是下图蓝色 group.leftobj.left 获取到的还是子对象到 Group 中心的距离,这次因为是从 Group 右侧开始算的,明显多了半个 Group 的宽度,所以需要再减掉 Group 一半的宽度。
    11d36b920f7b79f2.png

左侧的这么算,那距离顶部的也是同理。但是可能大家会发现一个问题,图里所有的子对象都是在 Group 中心点的右侧,所以计算公式都是加法,如果子对象在 Group 中心点的左侧呢?该怎么计算呢?

其实结果还是一样的,当子对象位于 Group 中心点左侧的时候,obj.left 取到的就是负数,所以说结果其实是一样的。

计算代码如下:

// 获取 Group 内子对象的实际坐标位置
// 此时 this 指向的是内部子对象
let absoluteLeft = obj.left;
switch (obj.group.originX) {
  case "left":
    absoluteLeft = obj.left + obj.group.left + obj.group.width / 2;
    break;
  case "center":
    absoluteLeft = obj.left + obj.group.left;
    break;
  case "right":
    absoluteLeft = obj.left + obj.group.left - obj.group.width / 2;
    break;
}

let absoluteTop = obj.top;
switch (obj.group.originY) {
  case "top":
    absoluteTop = obj.top + obj.group.top + obj.group.height / 2;
    break;
  case "center":
    absoluteTop = obj.top + obj.group.top;
    break;
  case "bottom":
    absoluteTop = obj.top + obj.group.top - obj.group.height / 2;
    break;
}

const groupCenter = obj.group.getPointByOrigin("center", "center");

在后来我发现还有一种计算方法也可以很方便的计算结果,回到上面的几张图可以看到子对象一直都是相对于 Group 的中心点来计算的,那我们直接求出中心点的位置岂不是可以直接计算子对象的绝对位置。

解决了开发遇到的问题,下面就看看 Group 的源码吧。

Group 源码逻辑

下图是根据源码画的图,主要体现了源码执行的几个重要步骤

11d36b920f7b79f2.png
  1. 初始化主要根据传入的参数计算分组内有多少对象,并计算整个分组的边界;
  2. 循环所有子对象并设置子对象的坐标
  3. 设置分组本身的坐标

Group 初始化很简单,重要的是 Group 被操作之后的相应的处理,比如重新计算边界框设置坐标等等,以及 Group 移动、缩放、旋转之后如何处理等等的逻辑。这些复杂的逻辑咱们留到下篇继续讲解。

本文主要整理了 Group 的使用,以及记录了开发过程中遇到的问题,以及分析解决办法;并查看了一遍源码,源码这里只是粗略带过,因为 Group 本身没有太多的方法,大多数核心方法都是继承自 Fabric ObjectFabric Object才是整个库的核心,留待下次分享。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK