Implementing Drag-and-Drop Functionality for Canvas Elements in Fabric.js
To enable drag-and-drop functionality for elements into a Fabric.js canvas, follow these technical steps:
Preparing HTML Elements for Dragging
Set the draggable attribute of HTML elements to true to make them draggable.
<div class="toolbar">
<div class="draggable-rect" draggable="true" ondragstart="startDrag('rectangle')"></div>
<div class="draggable-circle" draggable="true" ondragstart="startDrag('circle')"></div>
<div class="draggable-image" draggable="true" ondragstart="startDrag('image')"></div>
</div>
<canvas id="main-canvas"></canvas>
Initializing the Fabric.js Canvas
Initialize the canvas and implement essential interactive features like viewport translation and zooming.
let fabricCanvas = null;
let activeElementType = null;
function initializeCanvas() {
fabricCanvas = new fabric.Canvas('main-canvas', {
width: 1000,
height: 700
});
// Implement viewport panning when Alt key is held
fabricCanvas.on('mouse:down', function(mouseEvent) {
const event = mouseEvent.e;
if (event.altKey) {
this.isPanning = true;
this.panStartX = event.clientX;
this.panStartY = event.clientY;
}
});
fabricCanvas.on('mouse:move', function(mouseEvent) {
if (this.isPanning) {
const event = mouseEvent.e;
const viewport = this.viewportTransform;
viewport[4] += event.clientX - this.panStartX;
viewport[5] += event.clientY - this.panStartY;
this.requestRenderAll();
this.panStartX = event.clientX;
this.panStartY = event.clientY;
}
});
fabricCanvas.on('mouse:up', function() {
this.isPanning = false;
});
// Implement zooming with mouse wheel
fabricCanvas.on('mouse:wheel', function(wheelEvent) {
const delta = wheelEvent.e.deltaY;
let newZoom = this.getZoom();
newZoom *= 0.999 ** delta;
newZoom = Math.max(0.01, Math.min(20, newZoom));
this.zoomToPoint(
{ x: wheelEvent.e.offsetX, y: wheelEvent.e.offsetY },
newZoom
);
});
}
Tracking the Dragged Element Type
Use a global variable to track the type of element being dragged.
function startDrag(elementKind) {
activeElementType = elementKind;
}
Handling the Drop Event and Coordinate Calculation
Calculate the drop coordinates, accounting for the canvas position, viewport transformation, and zoom level.
fabricCanvas.on('drop', function(dropEvent) {
if (!activeElementType) return;
const canvasBounds = this.wrapperEl.getBoundingClientRect();
const mousePos = {
x: dropEvent.e.clientX - canvasBounds.left,
y: dropEvent.e.clientY - canvasBounds.top
};
const finalCanvasPoint = this.restorePointerVpt(mousePos);
switch (activeElementType) {
case 'rectangle':
addRectangle(finalCanvasPoint.y, finalCanvasPoint.x);
break;
case 'circle':
addCircle(finalCanvasPoint.y, finalCanvasPoint.x);
break;
case 'image':
addImage(finalCanvasPoint.y, finalCanvasPoint.x);
break;
}
activeElementType = null;
});
Creating Fabric.js Objects at the Calculated Position
Define functions to instantiate Fabric.js objects.
function addRectangle(topPos, leftPos) {
const rectangle = new fabric.Rect({
top: topPos,
left: leftPos,
width: 80,
height: 50,
fill: 'lightblue'
});
fabricCanvas.add(rectangle);
}
function addCircle(topPos, leftPos) {
const circle = new fabric.Circle({
top: topPos,
left: leftPos,
radius: 25,
fill: 'lightgreen'
});
fabricCanvas.add(circle);
}
function addImage(topPos, leftPos) {
fabric.Image.fromURL('path/to/image.png', function(img) {
img.set({
top: topPos,
left: leftPos,
scaleX: 0.25,
scaleY: 0.25
});
fabricCanvas.add(img);
});
}
This approach enables precise placement of dragged elements onto a Fabric.js canvas, correctly handling viewport transformations.