Created
February 4, 2020 02:10
-
-
Save rlingineni/a77d4e7a3fcc2a1beb499baefa7a7b89 to your computer and use it in GitHub Desktop.
Aligning Guidelines with Fabric.js + Typescript. This is a modifed version of the code here: https://github.com/fabricjs/fabric.js/blob/master/lib/aligning_guidelines.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { fabric } from "fabric"; | |
import { ILineOptions } from "fabric/fabric-impl"; | |
/** | |
* Should objects be aligned by a bounding box? | |
* [Bug] Scaled objects sometimes can not be aligned by edges | |
* | |
*/ | |
export function initAligningGuidelines(canvas: fabric.Canvas) { | |
var ctx = canvas.getSelectionContext(), | |
aligningLineOffset = 5, | |
aligningLineMargin = 4, | |
aligningLineWidth = 1, | |
aligningLineColor = 'rgb(0,255,0)', | |
viewportTransform = canvas.viewportTransform, | |
zoom = 1; | |
function drawVerticalLine(coords:ILineOptions) { | |
drawLine( | |
coords.x1! + 0.5, | |
coords.y1! > coords.y2! ? coords.y2! : coords.y1!, | |
coords.x1! + 0.5, | |
coords.y2! > coords.y1! ? coords.y2! : coords.y1!); | |
} | |
function drawHorizontalLine(coords:ILineOptions) { | |
drawLine( | |
coords.x1! > coords.x2! ? coords.x2! : coords.x1!, | |
coords.y1! + 0.5, | |
coords.x2! > coords.x1! ? coords.x2! : coords.x1!, | |
coords.y1 !+ 0.5); | |
} | |
function drawLine(x1:number, y1:number, x2:number, y2:number) { | |
ctx.save(); | |
ctx.lineWidth = aligningLineWidth; | |
ctx.strokeStyle = aligningLineColor; | |
ctx.beginPath(); | |
if(viewportTransform){ | |
ctx.moveTo(((x1+viewportTransform[4])*zoom), ((y1+viewportTransform[5])*zoom)); | |
ctx.lineTo(((x2+viewportTransform[4])*zoom), ((y2+viewportTransform[5])*zoom)); | |
} | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
function isInRange(value1:number, value2:number) { | |
value1 = Math.round(value1); | |
value2 = Math.round(value2); | |
for (var i = value1 - aligningLineMargin, len = value1 + aligningLineMargin; i <= len; i++) { | |
if (i === value2) { | |
return true; | |
} | |
} | |
return false; | |
} | |
var verticalLines:ILineOptions[] = [], | |
horizontalLines:ILineOptions[] = []; | |
canvas.on('mouse:down', function () { | |
viewportTransform = canvas.viewportTransform; | |
zoom = canvas.getZoom(); | |
}); | |
canvas.on('object:moving', function(e) { | |
let activeObject = e.target; | |
if(!activeObject || !viewportTransform) return; | |
let canvasObjects = canvas.getObjects(); | |
let activeObjectCenter = activeObject.getCenterPoint(), | |
activeObjectLeft = activeObjectCenter.x, | |
activeObjectTop = activeObjectCenter.y, | |
activeObjectBoundingRect = activeObject.getBoundingRect(), | |
activeObjectHeight = activeObjectBoundingRect.height / viewportTransform[3], | |
activeObjectWidth = activeObjectBoundingRect.width / viewportTransform[0], | |
horizontalInTheRange = false, | |
verticalInTheRange = false, | |
transform = canvas.viewportTransform | |
if (!transform) return; | |
// It should be trivial to DRY this up by encapsulating (repeating) creation of x1, x2, y1, and y2 into functions, | |
// but we're not doing it here for perf. reasons -- as this a function that's invoked on every mouse move | |
for (var i = canvasObjects.length; i--; ) { | |
if (canvasObjects[i] === activeObject) continue; | |
var objectCenter = canvasObjects[i].getCenterPoint(), | |
objectLeft = objectCenter.x, | |
objectTop = objectCenter.y, | |
objectBoundingRect = canvasObjects[i].getBoundingRect(), | |
objectHeight = objectBoundingRect.height / viewportTransform[3], | |
objectWidth = objectBoundingRect.width / viewportTransform[0]; | |
// snap by the horizontal center line | |
if (isInRange(objectLeft, activeObjectLeft)) { | |
verticalInTheRange = true; | |
verticalLines.push({ | |
x1: objectLeft, | |
y1: (objectTop < activeObjectTop) | |
? (objectTop - objectHeight / 2 - aligningLineOffset) | |
: (objectTop + objectHeight / 2 + aligningLineOffset), | |
y2: (activeObjectTop > objectTop) | |
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset) | |
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(objectLeft, activeObjectTop), 'center', 'center'); | |
} | |
// snap by the left edge | |
if (isInRange(objectLeft - objectWidth / 2, activeObjectLeft - activeObjectWidth / 2)) { | |
verticalInTheRange = true; | |
verticalLines.push({ | |
x1: objectLeft - objectWidth / 2, | |
y1: (objectTop < activeObjectTop) | |
? (objectTop - objectHeight / 2 - aligningLineOffset) | |
: (objectTop + objectHeight / 2 + aligningLineOffset), | |
y2: (activeObjectTop > objectTop) | |
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset) | |
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(objectLeft - objectWidth / 2 + activeObjectWidth / 2, activeObjectTop), 'center', 'center'); | |
} | |
// snap by the right edge | |
if (isInRange(objectLeft + objectWidth / 2, activeObjectLeft + activeObjectWidth / 2)) { | |
verticalInTheRange = true; | |
verticalLines.push({ | |
x1: objectLeft + objectWidth / 2, | |
y1: (objectTop < activeObjectTop) | |
? (objectTop - objectHeight / 2 - aligningLineOffset) | |
: (objectTop + objectHeight / 2 + aligningLineOffset), | |
y2: (activeObjectTop > objectTop) | |
? (activeObjectTop + activeObjectHeight / 2 + aligningLineOffset) | |
: (activeObjectTop - activeObjectHeight / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(objectLeft + objectWidth / 2 - activeObjectWidth / 2, activeObjectTop), 'center', 'center'); | |
} | |
// snap by the vertical center line | |
if (isInRange(objectTop, activeObjectTop)) { | |
horizontalInTheRange = true; | |
horizontalLines.push({ | |
y1: objectTop, | |
x1: (objectLeft < activeObjectLeft) | |
? (objectLeft - objectWidth / 2 - aligningLineOffset) | |
: (objectLeft + objectWidth / 2 + aligningLineOffset), | |
x2: (activeObjectLeft > objectLeft) | |
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset) | |
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop), 'center', 'center'); | |
} | |
// snap by the top edge | |
if (isInRange(objectTop - objectHeight / 2, activeObjectTop - activeObjectHeight / 2)) { | |
horizontalInTheRange = true; | |
horizontalLines.push({ | |
y1: objectTop - objectHeight / 2, | |
x1: (objectLeft < activeObjectLeft) | |
? (objectLeft - objectWidth / 2 - aligningLineOffset) | |
: (objectLeft + objectWidth / 2 + aligningLineOffset), | |
x2: (activeObjectLeft > objectLeft) | |
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset) | |
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop - objectHeight / 2 + activeObjectHeight / 2), 'center', 'center'); | |
} | |
// snap by the bottom edge | |
if (isInRange(objectTop + objectHeight / 2, activeObjectTop + activeObjectHeight / 2)) { | |
horizontalInTheRange = true; | |
horizontalLines.push({ | |
y1: objectTop + objectHeight / 2, | |
x1: (objectLeft < activeObjectLeft) | |
? (objectLeft - objectWidth / 2 - aligningLineOffset) | |
: (objectLeft + objectWidth / 2 + aligningLineOffset), | |
x2: (activeObjectLeft > objectLeft) | |
? (activeObjectLeft + activeObjectWidth / 2 + aligningLineOffset) | |
: (activeObjectLeft - activeObjectWidth / 2 - aligningLineOffset) | |
}); | |
activeObject.setPositionByOrigin(new fabric.Point(activeObjectLeft, objectTop + objectHeight / 2 - activeObjectHeight / 2), 'center', 'center'); | |
} | |
} | |
if (!horizontalInTheRange) { | |
horizontalLines.length = 0; | |
} | |
if (!verticalInTheRange) { | |
verticalLines.length = 0; | |
} | |
}); | |
canvas.on('before:render', function() { | |
canvas.clearContext(ctx); | |
}); | |
canvas.on('after:render', function() { | |
for (var i = verticalLines.length; i--; ) { | |
drawVerticalLine(verticalLines[i]); | |
} | |
for (var i = horizontalLines.length; i--; ) { | |
drawHorizontalLine(horizontalLines[i]); | |
} | |
verticalLines.length = horizontalLines.length = 0; | |
}); | |
canvas.on('mouse:up', function() { | |
verticalLines.length = horizontalLines.length = 0; | |
canvas.renderAll(); | |
}); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment