【fabric.js】 理解与变换矩阵相关的方法

394 阅读2分钟

与转换矩阵相关的方法

与变换矩阵相关的方法

// @return 该特定时刻的对象变换的矩阵(受top, left, scale...的影响)
fabric.Object.prototype.calcTransformMatrix() => matrix
// @retrun 矩阵 A 乘以矩阵 B 进行嵌套变换后的矩阵
fabric.util.multiplyTransformMatrices(matrix, matrix) => matrix
// @return 反向矩阵,用来进行反向计算
fabric.util.invertTransform(matrix) => matrix
// @return 解码后的矩阵,返回一个包含(angle, scale, skew, translate)的选项对象
fabric.util.qrDecompose(matrix) => options

fabric.util.qrDecompose(matrix) 返回的选项

{
  angle: number, // in degree
  scaleX: number,
  scaleY: number,
  skewX: number, // in degree
  skewY: 0, // always 0! in degree.
  translateX: number,
  translateY: number,
}

使用示例

场景

girl牵着cat出门,cat可以随意走动,但是一旦girl走动,cat就会跟着girl走。

fabric 实现的逻辑分析

cat跟着girl走的行为,需要以girl为主,记录下girl走动的变换矩阵,再将变换矩阵同步到cat的变换上。以下是步骤的逐步分析:

  1. 鼠标按下记录girlcat的初始变换矩阵
  2. cat的初始变换矩阵减去girl的初始变换矩阵,目的是只同步之后的变换矩阵,并不同步girl的初始变换
  3. 鼠标移动,记录girl的变换矩阵,同时将girl的变换矩阵同步到cat

代码实现

/**
 * @param { bossUrl } 女孩svg图片地址
 * @param { bossUrl } 猫咪svg图片地址
 * @param { canvasDom } canvas id
 */
function Minions(bossUrl, minionUrl, canvasDom) {
  let boss, minion, canvas; // girl cat object, canvas
  let startMatrix;
  function init() {
    canvas = new fabric.Canvas(canvasDom, {
      width: 500,
      height: 500,
      backgroundColor: "#fafafa",
    });
    fabric.loadSVGFromURL(bossUrl, function (objects, options) {
      boss = fabric.util.groupSVGElements(objects, {
        ...options,
        top: 100,
        left: 50,
      });
      canvas.add(boss);
    });
    fabric.loadSVGFromURL(minionUrl, function (objects, options) {
      minion = fabric.util.groupSVGElements(objects, {
        ...options,
        top: 240,
        left: 250,
      });
      minion.scale(0.3);
      canvas.add(minion);
      // 鼠标点击画布时,初始化转换矩阵
      canvas.on("mouse:down", initMatrix);
      // 女孩移动时,猫咪跟随女孩移动
      boss.on("moving", updateMinions);
      boss.on("rotating", updateMinions);
      boss.on("scaling", updateMinions);
    });
  }
  function initMatrix() {
    // cat 当前的变换矩阵
    const minionMatrix = minion.calcTransformMatrix();
    // girl 当前的变换矩阵
    const bossMatrix = boss.calcTransformMatrix();
    // girl 当前的反转变换矩阵, 也就是girl经过什么样的转换矩阵能变成初始状态
    const invertBossMatrix = fabric.util.invertTransform(bossMatrix);
    // cat 的初始变换矩阵需要减去girl的初始变换值,避免后续girl移动时, cat的变换不同步
    startMatrix = fabric.util.multiplyTransformMatrices(
      invertBossMatrix,
      minionMatrix
    );
  }
  function updateMinions() {
    // 每次移动时都需要获取girl当前的变换矩阵
    const bossMatrix = boss.calcTransformMatrix();

    // cat同步girl的变换矩阵
    const multiplyMatrix = fabric.util.multiplyTransformMatrices(
      bossMatrix,
      startMatrix
    );
    const opt = fabric.util.qrDecompose(multiplyMatrix);
    minion.set({
      flipX: false,
      flipY: false,
    });
    minion.setPositionByOrigin(
      { x: opt.translateX, y: opt.translateY },
      "center",
      "center"
    );
    minion.set(opt);
    minion.setCoords();
  }
  return init();
}