Created
June 4, 2023 09:43
-
-
Save vasiliishvakin/da20f7745ff0afce36a5bc5dc47dcc6c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(() => { | |
// Create the area object | |
const areaObject = new fabric.Rect({ | |
width: 200, | |
height: 200, | |
fill: "transparent", | |
stroke: "red", | |
strokeWidth: 2, | |
selectable: false, | |
original: { isBase: true }, | |
}); | |
// Create a regular object (rectangle) | |
const regularObject = new fabric.Rect({ | |
width: 100, | |
height: 100, | |
fill: "blue", | |
left: 50, | |
top: 50, | |
}); | |
// Initialize the canvas | |
const canvas = new fabric.Canvas("canvas-container", { | |
width: 400, | |
height: 400, | |
}); | |
// Add the area object to the canvas | |
canvas.add(areaObject); | |
// Add the regular object to the canvas | |
canvas.add(regularObject); | |
// Utility function to check intersection between two objects | |
function checkIntersection(obj1, obj2) { | |
const rect1 = obj1.getBoundingRect(); | |
const rect2 = obj2.getBoundingRect(); | |
return ( | |
rect1.left < rect2.left + rect2.width && | |
rect1.left + rect1.width > rect2.left && | |
rect1.top < rect2.top + rect2.height && | |
rect1.top + rect1.height > rect2.top | |
); | |
} | |
// Utility function to calculate the intersection area between two objects | |
function calculateIntersectionArea(obj1, obj2) { | |
const rect1 = obj1.getBoundingRect(); | |
const rect2 = obj2.getBoundingRect(); | |
const intersectionLeft = Math.max(rect1.left, rect2.left); | |
const intersectionTop = Math.max(rect1.top, rect2.top); | |
const intersectionWidth = | |
Math.min(rect1.left + rect1.width, rect2.left + rect2.width) - | |
intersectionLeft; | |
const intersectionHeight = | |
Math.min(rect1.top + rect1.height, rect2.top + rect2.height) - | |
intersectionTop; | |
if (intersectionWidth > 0 && intersectionHeight > 0) { | |
return intersectionWidth * intersectionHeight; | |
} | |
return 0; | |
} | |
let isMoving = false; // Flag to track if the object is currently being moved | |
// Register an event handler for object moving | |
canvas.on("object:moving", (event) => { | |
isMoving = true; // Set the flag to indicate that the object is being moved | |
}); | |
// Register an event handler for object moved | |
canvas.on("object:moved", (event) => { | |
if (isMoving) { | |
const movedObject = event.target; | |
if (!movedObject.original || !movedObject.original.isBase) { | |
let intersectedAreas = []; | |
let isInArea = false; | |
// Iterate through all objects on the canvas | |
canvas.forEachObject((object) => { | |
// Check if the current object is an area object | |
if (object.original && object.original.isBase) { | |
// Check if the moved object intersects with the area object | |
if (checkIntersection(object, movedObject)) { | |
intersectedAreas.push({ | |
area: object, | |
intersectionArea: calculateIntersectionArea( | |
object, | |
movedObject | |
), | |
}); | |
// Check if the moved object is fully inside the area object | |
if (object.containsPoint(movedObject.getCenterPoint())) { | |
isInArea = true; | |
} | |
} | |
} | |
}); | |
if (intersectedAreas.length > 0) { | |
// Sort the intersected areas based on the intersection area in descending order | |
intersectedAreas.sort( | |
(a, b) => b.intersectionArea - a.intersectionArea | |
); | |
if (!isInArea) { | |
// Object is not fully inside an area, snap it to the nearest area's position | |
const nearestArea = intersectedAreas[0].area; | |
movedObject.set({ | |
left: nearestArea.left, | |
top: nearestArea.top, | |
}); | |
// Auto-scale the moved object to fit within the nearest area | |
const scale = Math.min( | |
nearestArea.width / movedObject.width, | |
nearestArea.height / movedObject.height | |
); | |
movedObject.scale(scale); | |
} | |
} | |
} | |
isMoving = false; // Reset the flag once the object movement is completed | |
canvas.renderAll(); | |
} | |
}); | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Extend fabric.Object.prototype to add magnet functionality to all Fabric.js objects | |
fabric.Object.prototype.magnetize = function (canvas) { | |
let isMoving = false; | |
// Utility function to check intersection between two objects | |
function checkIntersection(obj1, obj2) { | |
// Intersection logic here... | |
} | |
// Utility function to calculate the intersection area between two objects | |
function calculateIntersectionArea(obj1, obj2) { | |
// Intersection area calculation logic here... | |
} | |
// Method to handle object moving event | |
function handleMoving(event) { | |
isMoving = true; | |
} | |
// Method to handle object moved event | |
function handleMoved(event) { | |
if (isMoving) { | |
const movedObject = event.target; | |
if (!movedObject.original || !movedObject.original.isBase) { | |
let intersectedAreas = []; | |
let isInArea = false; | |
// Iterate through all objects on the canvas | |
canvas.forEachObject((object) => { | |
// Check if the current object is an area object | |
if (object.original && object.original.isBase) { | |
// Check if the moved object intersects with the area object | |
if (checkIntersection(object, movedObject)) { | |
intersectedAreas.push({ | |
area: object, | |
intersectionArea: calculateIntersectionArea( | |
object, | |
movedObject | |
), | |
}); | |
// Check if the moved object is fully inside the area object | |
if (object.containsPoint(movedObject.getCenterPoint())) { | |
isInArea = true; | |
} | |
} | |
} | |
}); | |
if (intersectedAreas.length > 0) { | |
// Sort the intersected areas based on the intersection area in descending order | |
intersectedAreas.sort( | |
(a, b) => b.intersectionArea - a.intersectionArea | |
); | |
if (!isInArea) { | |
// Object is not fully inside an area, snap it to the nearest area's position | |
const nearestArea = intersectedAreas[0].area; | |
movedObject.set({ | |
left: nearestArea.left, | |
top: nearestArea.top, | |
}); | |
// Auto-scale the moved object to fit within the nearest area | |
const scale = Math.min( | |
nearestArea.width / movedObject.width, | |
nearestArea.height / movedObject.height | |
); | |
movedObject.scale(scale); | |
} | |
} | |
} | |
isMoving = false; | |
canvas.renderAll(); | |
} | |
} | |
// Register event handlers for object moving and moved events | |
this.on("moving", handleMoving); | |
this.on("moved", handleMoved); | |
}; | |
// Usage example | |
const canvas = new fabric.Canvas("canvas-container", { | |
width: 600, | |
height: 400, | |
}); | |
const areaObject = new fabric.Rect({ | |
width: 200, | |
height: 200, | |
fill: "transparent", | |
stroke: "red", | |
strokeWidth: 2, | |
selectable: false, | |
original: { isBase: true }, | |
}); | |
const regularObject = new fabric.Rect({ | |
width: 100, | |
height: 100, | |
fill: "blue", | |
left: 50, | |
top: 50, | |
}); | |
canvas.add(areaObject); | |
canvas.add(regularObject); | |
regularObject.magnetize(canvas); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment