在 FabricJS 中遮罩或剪辑给定的多边形

问题描述 投票:0回答:1

我有一个背景和无限数量的图像或动画,我想将部分图像或动画遮罩或剪辑到背景。我希望用户使用或移动多边形来确定他们希望发生这种情况的位置。请参阅JSFiddle

我已经尝试了一些方法,但我似乎必须多次重新添加背景图像才能获得我正在寻找的效果,但即使如此,它也不仅仅是填充多边形。我希望用户能够移动多边形并调整形状或动画的大小,并且蒙版或剪辑效果仍然有效。

如有任何帮助,我们将不胜感激!

var canvas = new fabric.Canvas('c');

var points = [{
    x: 3,
    y: 4,
  },
  {
    x: 16,
    y: 3,
  },
  {
    x: 30,
    y: 5,
  },
  {
    x: 25,
    y: 55,
  },
];

// define a function that can locate the controls.
// this function will be used both for drawing and for interaction.
function polygonPositionHandler(dim, finalMatrix, fabricObject) {
  var x =
    fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
    y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
  return fabric.util.transformPoint({
      x: x,
      y: y
    },
    fabric.util.multiplyTransformMatrices(
      fabricObject.canvas.viewportTransform,
      fabricObject.calcTransformMatrix()
    )
  );
}

function getObjectSizeWithStroke(object) {
  var stroke = new fabric.Point(
    object.strokeUniform ? 1 / object.scaleX : 1,
    object.strokeUniform ? 1 / object.scaleY : 1
  ).multiply(object.strokeWidth);
  return new fabric.Point(
    object.width + stroke.x,
    object.height + stroke.y
  );
}

// define a function that will define what the control does
// this function will be called on every mouse move after a control has been
// clicked and is being dragged.
// The function receive as argument the mouse event, the current trasnform object
// and the current position in canvas coordinate
// transform.target is a reference to the current object being transformed,
function actionHandler(eventData, transform, x, y) {
  var polygon = transform.target,
    currentControl = polygon.controls[polygon.__corner],
    mouseLocalPosition = polygon.toLocalPoint(
      new fabric.Point(x, y),
      "center",
      "center"
    ),
    polygonBaseSize = getObjectSizeWithStroke(polygon),
    size = polygon._getTransformedDimensions(0, 0),
    finalPointPosition = {
      x: (mouseLocalPosition.x * polygonBaseSize.x) / size.x +
        polygon.pathOffset.x,
      y: (mouseLocalPosition.y * polygonBaseSize.y) / size.y +
        polygon.pathOffset.y,
    };
  polygon.points[currentControl.pointIndex] = finalPointPosition;
  return true;
}

// define a function that can keep the polygon in the same position when we change its
// width/height/top/left.
function anchorWrapper(anchorIndex, fn) {
  return function(eventData, transform, x, y) {
    var fabricObject = transform.target,
      absolutePoint = fabric.util.transformPoint({
          x: fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
          y: fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y,
        },
        fabricObject.calcTransformMatrix()
      ),
      actionPerformed = fn(eventData, transform, x, y),
      newDim = fabricObject._setPositionDimensions({}),
      polygonBaseSize = getObjectSizeWithStroke(fabricObject),
      newX =
      (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) /
      polygonBaseSize.x,
      newY =
      (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) /
      polygonBaseSize.y;
    fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
    return actionPerformed;
  };
}

var polygon = new fabric.Polygon(points, {
  left: 150,
  top: 100,
  fill: "transparent",
  strokeWidth: 2,
  stroke: "green",
  scaleX: 2,
  scaleY: 2,
  objectCaching: false,
  transparentCorners: false,
  cornerColor: "blue",
  absolutePositioned: true,
});

fabric.Image.fromURL("https://s3.amazonaws.com/static-mywoodhome/wp-content/uploads/sites/4/2014/11/Screen-Shot-2016-10-18-at-4.39.30-PM.png", (img) => {
  canvas.add(img);
  img.sendToBack();
});

fabric.Image.fromURL("https://s3.amazonaws.com/static-mywoodhome/wp-content/uploads/sites/4/2014/11/Screen-Shot-2016-10-18-at-4.39.30-PM.png", (house) => {
  fabric.Image.fromURL("https://www.onlygfx.com/wp-content/uploads/2017/06/man-silhouette-1-79x300.png", (person) => {
    person.set("left", 100);
    canvas.add(person, polygon);
    house.set({
      left: 10,
      top: 10,
      clipPath: polygon,
    });
  });
});

var poly = polygon;

var lastControl = poly.points.length - 1;
poly.cornerStyle = "circle";
poly.cornerColor = "rgba(0,0,255,0.5)";
poly.controls = poly.points.reduce(function(acc, point, index) {
  acc["p" + index] = new fabric.Control({
    positionHandler: polygonPositionHandler,
    actionHandler: anchorWrapper(
      index > 0 ? index - 1 : lastControl,
      actionHandler
    ),
    actionName: "modifyPolygon",
    pointIndex: index,
  });
  return acc;
}, {});

给出的示例,我想要这样的输出:

enter image description here

javascript fabricjs
1个回答
0
投票

悲伤的事实,但我们必须接受

我用圆形、三角形、静态 html、js 做了一个简单的例子。通过(圆形,三角形)的并集来掩盖背景。只需将并集部分更改为交集部分即可

是的,下面的代码有一些问题

  • 三角形的移动方向与用户移动圆形时的矢量方向相反(与边界框、中心相关)
  • 因此,您可以通过选择圆形(就像选择一组文件)并移动它(仅在左上角)来重叠圆形和三角形

那么我为什么要中途发布这个答案呢?

  • 答案并不是真正的代码,而是 FabricJs 中不提供两个对象的交集,因此从技术上讲你无法做到这一点。
  • 我使用了fabric.Group,圆形和三角形来制作联合蒙版
  • 但是对于相交掩模,我们需要获取相交的结构对象,有一个方法
    new fabric.Intersection()
    可以接收结构点,为了让事情变得简单,它有
    intersectLineLine   intersectLinePolygon   intersectPolygonPolygon   intersectPolygonRectangle

但是您无法获取轮廓图像对象和库中的多边形交集(您可以检查它们是否相交,但无法从中制作遮罩) 至于你的问题,由于你无法获得对象的交集,你可以将其用作掩模,除非你脱离织物的 object-svg 框架进入光栅图形。或者使用其他支持两个对象交集的库(对象上的布尔运算)

查看 Christoph 在 如何填充 Fabric.js 中两个对象的交集?

中的回答

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Fabric.js Example</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.5.0/fabric.min.js"></script>

  <style>
    canvas {
      border: 1px solid #ccc;
    }
  </style>
</head>

<body>
  <canvas id="canvas" width="800" height="600"></canvas>
  <script>
    // Create a Fabric.js canvas
    const canvas = new fabric.Canvas('canvas');
    canvas.backgroundColor = '#45eeee';
    canvas.renderAll()// Create a circle and a triangle
      const circle = new fabric.Circle({
        radius: 100,
        top: 0,
        left: 0,
        originX: 'center',
        originY: 'center'
      });

      const triangle = new fabric.Triangle({
        width: 150,
        height: 150,
        top: 50,
        left: 0,
        originX: 'center',
        originY: 'center'
      }); canvas.add(circle); canvas.add(triangle);

    // Load an image onto the canvas
    fabric.Image.fromURL('https://picsum.photos/seed/picsum/1600/1200', function (img) {
      img.originX = 'center';
      img.originY = 'center';
      // Resize the image if necessary
      img.scaleToWidth(1600);
      img.scaleToHeight(1200);
      img.selectable = false;
      img.hasControls = false;

      

      // Group the circle and triangle
      function update() {
        maskGroup = new fabric.Group([circle,
        triangle], {
          originX: 'center',
          originY: 'center',
        });

        // Apply the mask as a clipPath to the image
        img.set({
          clipPath: maskGroup
        });

        // Add the image to the canvas
        canvas.add(img);
        canvas.renderAll()
      }
      update();
      circle.on('moving', update);
      circle.on('modified', update);
      triangle.on('moving', update);
      triangle.on('modified', update);
    });

  </script>
</body>

</html>

© www.soinside.com 2019 - 2024. All rights reserved.