Core Principles
To create rectangles via a "drag-to-select" method, focus on two key actions:
- Capture start coordinates (mouse button pressed) and end coordinates (mouse button released).
- 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:
- Top-left to bottom-right: Start coordinates match the rectangle’s top-left corner, end coordinates match bottom-right.
- Bottom-right to top-left: End coordinates become the top-left corner, and the dimensions are the difference between start and end.
- Bottom-left to top-right: Start x (smaller value) is left, end y (smaller value) is top; dimensions are absolute differences.
- 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>