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 es close to it (the user can take it out if he doesn't want to align).
I'm having a problem with my JSFiddle : /
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.
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 es 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/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.
Share Improve this question edited Mar 26, 2017 at 17:57 Horai Nuri asked Mar 26, 2017 at 3:21 Horai NuriHorai Nuri 5,58817 gold badges84 silver badges136 bronze badges 2- I'm having trouble understanding what is the behaviour you want. Could you explain a bit more? – jakeehoffmann Commented Mar 26, 2017 at 3:47
- @jakeehoffmann Yes, I want my round shape to snap horizontally to center whenever it es close to the line. The user can take off the shape from center whenever he wants. – Horai Nuri Commented Mar 26, 2017 at 11:37
4 Answers
Reset to default 5If 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/rekrah/9k9hd49u/.
Things are a little more plicated 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 acplish 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;
}
});
I added some improvements to the Tim Harker code. This code solves several problems:
- Allows to get vertical and horizontal magnetic centering;
- Fixed "getWidth() - not a function" bug;
- Fixed centering bug for different objects with setted width or height (in example images from unsplash);
- Fixed bug with centering after objects scale;
- Added lines hiding feature after mouse up event.
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/dmitryjast/wb3jmv9g/2/
Enjoy!
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"
);