理解 fabricJS 中的 matrix

414 阅读3分钟

本文采用的版本为 fabric@6.0.0-rc2

  1. Matrix 是一个 3 * 2 的矩阵,表示目标的缩放和平移。

 [0]: 水平方向的缩放因子 (a)。
 [1]: 水平方向的倾斜变换 (c),也可以被理解为在Y轴方向上倾斜对象。
 [2]: 垂直方向的倾斜变换 (b),也可以被理解为在X轴方向上倾斜对象。
 [3]: 垂直方向的缩放因子 (d)。
 [4]: 水平方向的平移量 (e)。
 [5]: 垂直方向的平移量 (f)。
  1. Canvas.viewportTransform

表示画布的缩放和平移(即视口的变换)。

// 1. 在 800 * 600 的画布上创建一个矩形
var rect = new fabric.Rect({
    width: 600,
    height: 600,
    left: 0,
    top: 0,
    fill: 'black',
    strokeWidth: 0,
  });

// 2. 修改 viewportTransform, 使得画布的中心点放到矩形的中心
const center = rect.getCenterPoint(); // {x: 300, y: 300}
const { viewportTransform, width, height } = canvas;
viewportTransform[4] = width / 2 - center.x * viewportTransform[0];
viewportTransform[5] = height / 2 - center.y * viewportTransform[3];
canvas.setViewportTransform(viewportTransform); // [1, 0, 0, 100, 0], 即水平方向移动了100px
console.log(viewportTransform);

发生 viewportTransform 变换后,相对于视口位置和相对于画布位置就不相等了。

// 3. 打印鼠标位置
canvas.on('mouse:down', (opt) => {
    const event = opt.e;
    console.log('scenePoint', canvas.getScenePoint(event));
    console.log('viewPoint', canvas.getViewportPoint(event));
});

// 此时点击左上角
// scenePoint {x: -100, 0}
// viewPoint {x: 0, 0}

即 getScenePoint 获得的是当前相对于视口的位置,getViewportPoint 获得的是当前相对于画布的位置。

// 4. 在鼠标位置创建一个红色的方块,此时需采用相对于视口的位置
canvas.on('mouse:down', (opt) => {
    const event = opt.e;

    console.log('scenePoint', canvas.getScenePoint(event));
    console.log('viewPoint', canvas.getViewportPoint(event));

    const scenePoint = canvas.getScenePoint(event);

    const redRect = new fabric.Rect({
      width: 40,
      height: 40,
      fill: 'red',
      strokeWidth: 0,
      left: scenePoint.x - 40 / 2,
      top: scenePoint.y - 40 / 2,
    });
    canvas.add(redRect);
    canvas.renderAll();
  });

  1. Object matrix

  • calcTransformMatrix: 返回当前对象相对于画布的矩阵变换,如果对象在一个Group中,这个方法返回的结果包括 Group 的矩阵变换。
  • calcOwnMatrix: 返回当前对象的矩阵变换,不考虑其他的信息。
// 5. 创建一个Group 和 Rect
const rect2 = new fabric.Rect({
    left: 0,
    top: 0,
    width: 100,
    height: 50,
    fill: 'green',
    strokeWidth: 0,
  });

  const group = new fabric.Group([rect2], {
    left: 100,
    top: 100,
    width: 100,
    height: 100,
    strokeWidth: 0,
  });
  canvas.add(group);
  console.log('Group calcTransformMatrix', group.calcTransformMatrix());
  console.log('calcTransformMatrix', rect2.calcTransformMatrix());
  console.log('calcOwnMatrix', rect2.calcOwnMatrix());
  
  // Group calcTransformMatrix [1, 0, 0, 1, 150, 125]
  1. invertTransform

fabric.util.invertTransform 用于计算给定变换矩阵的逆矩阵。

假定在画布上存在两个矩形,希望一个矩形 Rect1 移动时,另一个矩形 Rect2 跟着进行移动(相对位置不变)。

Rect1 * Matrix = Rect2
Matrix 就是这个相对位置的变换矩阵

Rect1-Invert * Rect1 * Matrix = Rect1-Invert * Rect2
Rect1-Invert * Rect1 相消:
Matrix = Rect1-Invert * Rect2

相对位置的变换矩阵 = Rect1-Invert * Rect2

已知相对位置,无论 Rect1 怎么平移,都可以根据相对位置的变换矩阵得到 Rect2 的位置。


  const rect1 = new fabric.Rect({
    left: 0,
    top: 0,
    width: 100,
    height: 50,
    fill: 'red',
  });

  const rect2 = new fabric.Rect({
    left: 100,
    top: 300,
    width: 100,
    height: 50,
    fill: 'green',
  });
  
  // 相对位置的变化矩阵
  const desiredTransform = fabric.util.multiplyTransformMatrices(
    fabric.util.invertTransform(rect1.calcTransformMatrix()),
    rect2.calcTransformMatrix(),
  );
  
  rect1.on('moving', () => {
    // 根据 rect1 的新地址和相对变换矩阵,计算得到 rect2 的新地址
    const matrix = rect1.calcTransformMatrix();
    const newMatrix = fabric.util.multiplyTransformMatrices(matrix, desiredTransform);
    // 进行变换
    fabric.util.applyTransformToObject(rect2, newMatrix);
    canvas.renderAll();
  });