Freehand Rectangle Drawing with Fabric.js

Core Principles

To create rectangles via a "drag-to-select" method, focus on two key actions:

  1. Capture start coordinates (mouse button pressed) and end coordinates (mouse button released).
  2. After releasing the mouse, use the two coordinates to calculate the rectangle’s dimensions (width/height) and position.

Fabric.js provides event listeners for these actions:

  • Mouse down: canvas.on('mouse:down', callback)
  • Mouse up: canvas.on('mouse:up', callback)

Considerations for Different Dragging Directions

Dragging from various corners affects the calculated dimensions and position:

  1. Top-left to bottom-right: Start coordinates match the rectangle’s top-left corner, end coordinates match bottom-right.
  2. Bottom-right to top-left: End coordinates become the top-left corner, and the dimensions are the difference between start and end.
  3. Bottom-left to top-right: Start x (smaller value) is left, end y (smaller value) is top; dimensions are absolute differences.
  4. Top-right to bottom-left: End x (smaller value) is left, start y (smaller value) is top; dimensions are absolute differences.

Summary of Calculation Formulas

Use Math.min (returns the smaller value) and Math.abs (returns absolute value) to simplify calculations:

new fabric.Rect({
    top: Math.min(startPos.y, endPos.y),
    left: Math.min(startPos.x, endPos.x),
    width: Math.abs(startPos.x - endPos.x),
    height: Math.abs(startPos.y - endPos.y),
    fill: 'transparent',
    stroke: '#000'
})

Implementation Code

<!-- Toolbar -->
<div class="controls">
  <select onchange="switchOperationMode(this.value)">
    <option value="default">Default (Selection)</option>
    <option value="rect">Rectangle Drawing</option>
  </select>
</div>

<!-- Canvas -->
<canvas id="drawingCanvas" width="800" height="800"></canvas>

<!-- Fabric.js Library -->
<script src="https://cdn.bootcdn.net/ajax/libs/fabric.js/460/fabric.js"></script>

<script>
let drawingBoard = null
let operationMode = 'default'
let startPos = null
let endPos = null

function initializeCanvas() {
  drawingBoard = new fabric.Canvas('drawingCanvas')
  drawingBoard.on('mouse:down', onMouseDown)
  drawingBoard.on('mouse:up', onMouseUp)
}

function switchOperationMode(mode) {
  operationMode = mode
  switch (mode) {
    case 'default':
      drawingBoard.selection = true
      drawingBoard.selectionColor = 'rgba(100, 100, 255, 0.3)'
      drawingBoard.selectionBorderColor = 'rgba(255, 255, 255, 0.3)'
      drawingBoard.skipTargetFind = false
      break
    case 'rect':
      drawingBoard.selectionColor = 'transparent'
      drawingBoard.selectionBorderColor = 'rgba(0, 0, 0, 0.2)'
      drawingBoard.skipTargetFind = true
      break
  }
}

funciton onMouseDown(event) {
  startPos = event.absolutePointer
}

function onMouseUp(event) {
  if (operationMode === 'rect') {
    endPos = event.absolutePointer
    generateRect()
  }
}

function generateRect() {
  if (JSON.stringify(startPos) === JSON.stringify(endPos)) {
    return
  }

  const rectTop = Math.min(startPos.y, endPos.y)
  const rectLeft = Math.min(startPos.x, endPos.x)
  const rectWidth = Math.abs(startPos.x - endPos.x)
  const rectHeight = Math.abs(startPos.y - endPos.y)

  const rect = new fabric.Rect({
    top: rectTop,
    left: rectLeft,
    width: rectWidth,
    height: rectHeight,
    fill: 'transparent',
    stroke: '#000'
  })

  drawingBoard.add(rect)
  startPos = null
  endPos = null
}

window.addEventListener('DOMContentLoaded', initializeCanvas)
</script>

Tags: fabric.js Canvas javascript Graphic Design Drawing

Posted on Thu, 14 May 2026 15:30:51 +0000 by aldoh