Implementing Polygon Vertex Dragging for Shape Modification in Fabric.js
This guide demonstrates a method for dynamically altering a polygon's shape by dragging its vertices using Fabric.js. While the official Fabric.js demos offer a more sophisticated approach using custom controls, this tutorial presents a simplified version for clarity and learning purposes.
Core Concept
The functionality is achieved by overlaying interactive circualr handles on each polygon vertex. When a user drags one of these circles, the corresponding polygon point updates its position.
Implementation Steps
- Initialize a Fabric.js canvas.
- Create a polygon object with direct user interaction disabled.
- Generate a draggable circle (handle) for each polygon vertex.
- Listen for object movement events; if a handle circle is moved, update the polygon's vertex coordinates.
- Refresh the canvas to reflect the changes.
Polygon Configuration
To prevent direct manipulation of the polygon itself, configure it with the following properties:
const myPolygon = new fabric.Polygon(vertices, {
fill: 'red',
stroke: 'black',
strokeWidth: 2,
objectCaching: false, // Crucial for real-time updates
selectable: false, // Prevents selection
evented: false // Makes it non-interactive
});
Handle Circle Configuration
Each circular handle should be configured to act as a control point:
new fabric.Circle({
left: vertexX,
top: vertexY,
radius: 10,
fill: 'white',
stroke: '#333',
strokeWidth: 3,
originX: 'center', // Position refers to circle's center
originY: 'center',
hasControls: false, // Hide resize controls
hasBorders: false, // Hide selection border
customId: index // Identifier linking circle to vertex
})
Complete Working Example
<canvas id="drawingCanvas" width="600" height="500"></canvas>
<script src="https://unpkg.com/fabric@5.3.0/dist/fabric.min.js"></script>
<script>
const canvas = new fabric.Canvas('drawingCanvas');
// Define initial polygon vertices
const vertexCoordinates = [
{x: 100, y: 100},
{x: 180, y: 60},
{x: 260, y: 100},
{x: 240, y: 220},
{x: 120, y: 180}
];
// Create the base polygon
const shape = new fabric.Polygon(vertexCoordinates, {
fill: 'rgba(255,0,0,0.5)',
stroke: 'black',
strokeWidth: 2,
objectCaching: false,
selectable: false,
evented: false
});
const handles = [];
// Create a draggable handle for each vertex
vertexCoordinates.forEach((coord, idx) => {
const handle = new fabric.Circle({
left: coord.x,
top: coord.y,
radius: 12,
fill: '#fff',
stroke: '#666',
strokeWidth: 4,
originX: 'center',
originY: 'center',
hasControls: false,
hasBorders: false,
handleIndex: idx // Custom property to track vertex
});
handles.push(handle);
});
// Add all objects to canvas
canvas.add(shape, ...handles);
// Update polygon when a handle is dragged
canvas.on('object:moving', function(event) {
const movedObj = event.target;
// Check if the moved object is one of our handles
if (movedObj.handleIndex !== undefined) {
const pointIndex = movedObj.handleIndex;
// Update the corresponding polygon vertex
shape.points[pointIndex].x = movedObj.left;
shape.points[pointIndex].y = movedObj.top;
// Force canvas redraw
canvas.requestRenderAll();
}
});
</script>
Key Points
- Setting
objectCaching: falseon the polygon is essential for visual updates. - The
handleIndexcustom property creates the link between a circle and a specific polygon vertex. - Using
canvas.requestRenderAll()ensures the polygon redraws with the new vertex positions.