Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing Drag-and-Drop Functionality for Canvas Elements in Fabric.js

Tech Apr 29 52

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.

Tags: Fabric.js

Related Articles

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.