I have a background and an unlimited amount of images or animations where I'd like to mask or clip part of the image or animation to the background. I'd like the user to use or move around a polygon to determine where they would like this to happen. See JSFiddle
I've tried a couple things but I seem to have to re-add the background image multiple times to get the effect I'm looking for but even at that, it's not just filling the polygon. I'd the user to be able to move the polygon around and resize the shapes or animations and have the mask or clip effect still work.
Any help would be appreciated!
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;
}, {});
Given the example, I'd like an output like this: