Reputation: 5578
I created a Vertical line in the center of my canva, I'd like my shape to horizontaly align by it's center, right on the line when it comes close to it (the user can take it out if he doesn't want to align).
I'm having a problem with my JSFiddle : https://jsfiddle.net/z0u05hee/5/
The issue is that when the shape moves it stays block near the center of the line and I cannot get it out anymore.
Here is my FabricJS code :
var canvas = new fabric.Canvas('c', { selection: false });
var line9 = new fabric.Line([
canvas.width / 2, 0,
canvas.width / 2, canvas.width
],{
stroke: 'green',
})
line9.selectable = false;
line9.evented = false;
canvas.add(line9);
canvas.add(new fabric.Circle({
left: 10,
top: 100,
radius: 50,
fill: '#9f9',
originX: 'left',
originY: 'top',
centeredRotation: true
}));
canvas.on('object:moving', function(options) {
if (Math.round(options.target.left) < (canvas.width / 2) - 15 ||
Math.round(options.target.left) > (canvas.width / 2) + 15) {
options.target.set({
left: Math.round(canvas.width / 2),
}).setCoords();
}
});
How should I fix my code ?
UPDATE :
canvas.on('object:moving', function(options) {
if (Math.round(options.target.left) >= (canvas.getActiveObject().getWidth()) - 20 && Math.round(options.target.left) <= (canvas.getActiveObject().getWidth()) + 20) {
options.target.set({
left: (canvas.width / 2) - canvas.getActiveObject().getWidth() / 2,
}).setCoords();
}
});
This works but when I scale the shape the center point change it's coordination.
Upvotes: 1
Views: 2830
Reputation: 11
I added some improvements to the Tim Harker code. This code solves several problems:
var line9 = new fabric.Line([
canvas.width / 2, 0,
canvas.width / 2, canvas.width
], {
strokeDashArray: [5, 5],
stroke: 'red',
})
line9.selectable = false;
line9.evented = false;
var line10 = new fabric.Line([
0, canvas.height / 2,
canvas.width, canvas.height / 2
], {
strokeDashArray: [5, 5],
stroke: 'red',
strokeWidth: 1,
})
line10.selectable = false;
line10.evented = false;
var snapZone = 15;
canvas.on('object:moving', function(options) {
var objectMiddleHorizontal = options.target.left + (options.target.width * options.target.scaleX) / 2;
if (objectMiddleHorizontal > canvas.width / 2 - snapZone &&
objectMiddleHorizontal < canvas.width / 2 + snapZone) {
options.target.set({
left: canvas.width / 2 - (options.target.width * options.target.scaleX) / 2,
}).setCoords();
canvas.add(line9);
document.addEventListener("mouseup", () => {
canvas.remove(line9);
});
} else {
canvas.remove(line9);
}
var objectMiddleVertical = options.target.top + (options.target.height * options.target.scaleY) / 2;
if (objectMiddleVertical > canvas.height / 2 - snapZone &&
objectMiddleVertical < canvas.height / 2 + snapZone) {
options.target.set({
top: canvas.height / 2 - (options.target.height * options.target.scaleY) / 2,
}).setCoords();
canvas.add(line10);
document.addEventListener("mouseup", () => {
canvas.remove(line10);
});
} else {
canvas.remove(line10);
}
});
https://jsfiddle.net/dmitryjast/wb3jmv9g/2/
Enjoy!
Upvotes: 1
Reputation: 9
Tims answer is correct but if you rotate object it will not work. i find a way to fix that problem:
const objectMiddleFromLeft = object.getCenterPoint().x;
const objectMiddleFromTop = object.getCenterPoint().y;
object.setPositionByOrigin(
{
x:
objectMiddleFromLeft > canvas.width / 2 - 15 &&
objectMiddleFromLeft < canvas.width / 2 + 15
? canvas.width / 2
: objectMiddleFromLeft,
y:
objectMiddleFromTop > canvas.height / 2 - 15 &&
objectMiddleFromTop < canvas.height / 2 + 15
? canvas.height / 2
: objectMiddleFromTop,
},
"center",
"center"
);
Upvotes: 0
Reputation:
Things are a little more complicated when you resize the canvas after you put an object on it. For whatever reason fabric js returns canvas.width in viewport coordinates, i.e. when you resize, width changes. object.width however is NOT affected by a canvas resize so stays the same. The following snippet works for me after resize of canvas and/or resize of objects. If anyone has a simpler solution to accomplish this - please share!
// Snapping while moving
canvas.on('object:moving', options => {
const obj = options.target;
// snap only if the object is upright
if (obj.angle !== 0) {
return;
}
// Capture area of snap
let snap = 10;
// Compensate snap size for transform
const mySnap = snap / canvas.viewportTransform[0];
// Sets corner position coordinates based on current angle, width and height
obj.setCoords();
// Snap left
if (Math.abs(obj.oCoords.tl.x) < mySnap) {
obj.left = 0;
}
// Snap right
else if (Math.abs(obj.aCoords.tr.x - canvas.vptCoords.tr.x) < mySnap) {
let canvasVptWidth = canvas.vptCoords.tr.x;
obj.left = canvasVptWidth - obj.getScaledWidth();
}
// Snap center horizontally
else if (Math.abs(obj.oCoords.mt.x - canvas.width / 2) < mySnap) {
canvasVptWidth = canvas.vptCoords.br.x;
obj.left = (canvasVptWidth - obj.getScaledWidth()) / 2;
}
// Snap top
if (Math.abs(obj.aCoords.tl.y) < mySnap) {
obj.top = 0;
}
// Snap bottom
else if (Math.abs(obj.aCoords.br.y - canvas.vptCoords.br.y) < mySnap) {
let canvasVptHeight = canvas.vptCoords.br.y;
obj.top = canvasVptHeight - obj.getScaledHeight();
}
// Snap center vertically
else if (Math.abs(obj.oCoords.ml.y - canvas.height / 2) < mySnap) {
canvasVptHeight = canvas.vptCoords.br.y;
obj.top = (canvasVptHeight - obj.getScaledHeight()) / 2;
}
});
Upvotes: 2
Reputation: 2407
If I understood you correctly, this should do it:
And a small tweak to your code (relevant part):
var snapZone = 15;
canvas.on('object:moving', function(options) {
var objectMiddle = options.target.left + options.target.getWidth() / 2;
if (objectMiddle > canvas.width / 2 - snapZone &&
objectMiddle < canvas.width / 2 + snapZone) {
options.target.set({
left: canvas.width / 2 - options.target.getWidth() / 2,
}).setCoords();
}
});
And the all important JSFiddle update, https://jsfiddle.net/rekrah/9k9hd49u/.
Upvotes: 5