How to align Top/Left/Bottom/Right of selected objects (FabricJS) - fabricjs

var canvas = window._canvas = new fabric.Canvas('c');
var red = new fabric.Rect({
top: 100,
left: 0,
width: 80,
height: 50,
fill: 'red'
});
var blue = new fabric.Rect({
top: 0,
left: 100,
width: 50,
height: 70,
fill: 'blue'
});
var green = new fabric.Rect({
top: 100,
left: 100,
width: 60,
height: 60,
fill: 'green'
});
canvas.add(red, blue, green);
const alignLeft = document.getElementById('align_left');
alignLeft.onclick = function() {
const objs = canvas.getActiveObjects();
}
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<canvas id="c" ></canvas>
<button id="align_left">Align Left</button>
Looking for a utility method to align edges (top,right,bottom,left) of array of objects from canvas.getActiveObjects in FabricJS v3
http://jsfiddle.net/rlightner/bzs0798x/1/

$('#align_left').click(function(){canvas.getActiveObject().set({left:0);})

Related

fabric.js rotate object around canvas center smoothly

I try to rotate an object around the canvas center smoothly using a slider,
I use fabric.util.rotatePoint to find the new center point and set the new angle,but it seems not exactly around the center point and the object position is jumping.
https://jsfiddle.net/j0g1tLsb/26/
here is my code doing this:
const canvas = new fabric.Canvas('c', { width: innerWidth, height: innerHeight });
canvas.controlsAboveOverlay = true;
canvas.preserveObjectStacking = true;
const rect = new fabric.Rect({
width: 300,
height: 150,
left: 400,
top: 400,
fill: "lightgray",
originX: 'center',
originY: 'center'
});
const centerPoint = new fabric.Circle({
originX: 'center',
originY: 'center',
top: innerHeight/2,
left: innerWidth/2,
radius: 10,
fill: 'red',
hasControls: false,
selectable:false
});
const log = new fabric.Text('', {
left: 30,
top: 30,
originX: 'left',
originY: 'top',
fontSize: 18,
evented: false,
selectable: false
});
canvas.add(rect, centerPoint, log);
document.getElementById('img-rotation').oninput = function() {
rect.set('angle', this.value);
var posNewCenter = fabric.util.rotatePoint(
rect.getCenterPoint(),
canvas.getVpCenter(),
fabric.util.degreesToRadians(this.value)
);
rect.set({
left: posNewCenter.x,
top: posNewCenter.y,
angle: this.value
});
log.set('text', `angle: ${Math.round(rect.angle)} \nleft: ${rect.left} \ntop: ${rect.top}`);
canvas.requestRenderAll();
};
rect.on('modified', () => {
log.set('text', `angle: ${Math.round(rect.angle)} \nleft: ${rect.left} \ntop: ${rect.top}`);
canvas.renderAll();
});
Your problem is in getting center point from the rectangle every time. You need just set new Point from the original rectangle position.
var posNewCenter = fabric.util.rotatePoint(
new fabric.Point(400, 400), //here is your mistake
canvas.getVpCenter(),
fabric.util.degreesToRadians(this.value)
);
rect.set({
left: posNewCenter.x,
top: posNewCenter.y,
angle: this.value
});
Working fiddle
UPDATE:
In order of the modification of the Rectangle you need to use object:modified event to reassign top and left coordinates.
First of all declare top and left variables and use them inside rectangle object:
let top = 400;
let left = 400;
const rect = new fabric.Rect({
...
left: left,
top: top,
...
targetObj: 'rectangle' //you can use any value or ID, it's only for targeting purpose
});
Then you need to check when object is modified. If event is fired then check if it is rectangle.
canvas.on('object:modified', (e) => {
if (e.target.hasOwnProperty('targetObj') && e.target.targetObj === 'rectangle') {
left = e.target.get('left');
top = e.target.get('top');
}
})
Finally, use left and top variables inside oninput event of the slider:
var posNewCenter = fabric.util.rotatePoint(
new fabric.Point(left, top),
canvas.getVpCenter(),
fabric.util.degreesToRadians(this.value)
);
Working updated fiddle

How to align object by bounding box in FabricJS?

Wondering if there is a way to align objects in FabricJs by their bounding box?
I'm using obj.getBoundingRect() function to determine objects bounders, then compare them with a bounding box (BB) coordinates of an Active one (that one which I move). If I see that something falls between some gap (let's say 10px) I assign an active object top to be the same top as a comparable element by using a .setTop() property.
The problem is that TOP is not a right attribute to use, since the top of the bounding box may differ between elements. For example, 2 elements with the same top but different angle will have different Bounding Box Top...
Hope you see my point...
https://jsfiddle.net/redlive/hwcu1p4f/
var canvas = this.__canvas = new fabric.Canvas('canvas');
//fabric.Object.prototype.transparentCorners = false;
var red = new fabric.Rect({
id: 1,
left: 100,
top: 50,
width: 100,
height: 100,
fill: 'red',
angle: 0,
padding: 10
});
canvas.add(red);
var green = new fabric.Rect({
id: 2,
left: 250,
top: 180,
width: 100,
height: 100,
fill: 'green',
angle: 45,
padding: 10
});
canvas.add(green);
canvas.renderAll();
canvas.on("object:moving", function(e){
const draggableObj = e.target;
const draggableObjBound = draggableObj.getBoundingRect();
canvas.forEachObject(function(obj) {
if (obj.id !== draggableObj.id) {
var bound = obj.getBoundingRect();
if (draggableObjBound.top > bound.top - 10 && draggableObjBound.top < bound.top + 10) {
draggableObj.setTop(obj.getTop());
}
}
});
});
canvas.forEachObject(function(obj) {
var setCoords = obj.setCoords.bind(obj);
obj.on({
moving: setCoords,
scaling: setCoords,
rotating: setCoords
});
});
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect();
canvas.contextContainer.strokeRect(
bound.left,
bound.top,
bound.width,
bound.height
);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<canvas id="canvas" width="800" height="500" style="border:1px solid #ccc"></canvas>
You should use the center to align them, that is not gonna change.
to align the bounding box at left 5 for example:
1) calculate bounding box.
2) set the position of the object to 5 + bb.width/2 considering center.
In this case the bounding rects get aligned.
var canvas = this.__canvas = new fabric.Canvas('canvas');
//fabric.Object.prototype.transparentCorners = false;
var red = new fabric.Rect({
id: 1,
left: 100,
top: 50,
width: 100,
height: 100,
fill: 'red',
angle: 0,
padding: 10
});
canvas.add(red);
var green = new fabric.Rect({
id: 2,
left: 250,
top: 180,
width: 100,
height: 100,
fill: 'green',
angle: 45,
padding: 10
});
canvas.add(green);
//ALIGN EVERYTHING TO 5
canvas.forEachObject(function(object) {
var bb = object.getBoundingRect();
object.setPositionByOrigin({ x: 5 + bb.width/2, y: bb.top }, 'center', 'center');
object.setCoords();
});
canvas.renderAll();
canvas.on("object:moving", function(e){
const draggableObj = e.target;
const draggableObjBound = draggableObj.getBoundingRect(true, true);
canvas.forEachObject(function(obj) {
if (obj.id !== draggableObj.id) {
var bound = obj.getBoundingRect(true, true);
if (draggableObjBound.top > bound.top - 10 && draggableObjBound.top < bound.top + 10) {
draggableObj.setPositionByOrigin({ x: draggableObj.left, y: bound.top + draggableObjBound.height/2 }, draggableObj.originX, 'center');
}
}
});
});
canvas.forEachObject(function(obj) {
var setCoords = obj.setCoords.bind(obj);
obj.on({
moving: setCoords,
scaling: setCoords,
rotating: setCoords
});
});
canvas.on('after:render', function() {
canvas.contextContainer.strokeStyle = '#555';
canvas.forEachObject(function(obj) {
var bound = obj.getBoundingRect(true, true);
canvas.contextContainer.strokeRect(
bound.left,
bound.top,
bound.width,
bound.height
);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.0.0-rc.3/fabric.js"></script>
<canvas id="canvas" width="800" height="500" style="border:1px solid #ccc"></canvas>

FabricJS. How to add dimensions displaying in controls?

How to add dimensions displaying in controls like on screenshot?
Create a text object and set it hidden. And when you are scalling object set the scale width and height to hidden text and make visible. On object modified set visible false to the text.
DEMO
var canvas = new fabric.Canvas("c");
canvas.setHeight(200);
canvas.setWidth(300);
var dimText = new fabric.Text("demo", {
fontSize: 15,
visible: false
});
canvas.add(dimText);
var circle = new fabric.Circle({
left: 15,
top: 15,
radius: 20,
fill:'',
stroke: 'red'
});
canvas.add(circle);
var text = new fabric.Text("2018", {
padding: 30,
lineHeight: 30
});
canvas.add(text);
canvas.centerObject(text);
text.setCoords();
canvas.on('object:scaling', function(option) {
var object = option.target;
var pointer = canvas.getPointer(option.e);
dimText.set({
left: pointer.x - 20,
top: pointer.y - 20,
text: parseInt(object.width * object.scaleX) + 'x' + parseInt(object.height * object.scaleY),
visible: true
})
});
canvas.on('object:modified', function(option) {
dimText.set('visible', false);
});
canvas {
border: 1px solid #dddddd;
margin-top: 10px;
border-radius: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.6.7/fabric.min.js"></script>
<canvas id="c"></canvas>

fabricjs can works for a donout chart?

i try to make this with circle and use startAnge and endAngle but when i want to draw filled part with one color and other color for outside not let me ..or at least not found a way..i try to make groups..one circle for filled part and other circle in same position for outfilled part ..seems not work for me..
any ideas?
here the demo https://jsfiddle.net/mavirroco/on0xg64k/
var canvas = new fabric.Canvas('c');
var circle = new fabric.Circle({
radius: 40,
left: 100,
top: 300,
angle: -90,
startAngle: 0,
endAngle: 1.5*Math.PI,
stroke: '#000',
strokeWidth: 25,
fill: ''
});
var circle2 = new fabric.Circle({
radius: 40,
left: 100,
top: 300,
angle: -90,
startAngle: 0,
endAngle: 1.5*Math.PI,
stroke: '#000',
strokeWidth: 25,
fill: ''
});
var circle3 = new fabric.Circle({
radius: 40,
left: 100,
top: 300,
angle: -90,
startAngle: 0,
endAngle: 1.5*Math.PI,
stroke: '#000',
strokeWidth: 25,
fill: ''
});
canvas.add(circle);
regards
Look updated fiddle.
https://jsfiddle.net/asturur/on0xg64k/3/
var canvas = new fabric.Canvas('c');
var circle = new fabric.Circle({
radius: 40,
left: 0,
top: 0,
angle: 0,
startAngle: -0.3,
endAngle: 0,
stroke: '#0F0',
strokeWidth: 25,
fill: ''
});
var circle2 = new fabric.Circle({
radius: 40,
left: 0,
top: 0,
angle: 0,
startAngle: 0.9,
endAngle: -0.4,
stroke: '#F00',
strokeWidth: 25,
fill: ''
});
var circle3 = new fabric.Circle({
radius: 40,
left: 0,
top: 0,
angle: 0,
startAngle: 0.3,
endAngle: 0.7,
stroke: '#00F',
strokeWidth: 25,
fill: ''
});
var group = new fabric.Group([circle, circle2, circle3]);
canvas.add(group);
I think there are some discrepancy in the code that need fixing.
It looks like fabricjs uses radians instead of angle in the startAngle and endAngle property and looks like that filling is not affected by this property.

Move kineticJS shape with keyboard

I want to move some shapes with the keyboard arrows. I've read some tutorials, but nothing have seemed to help me so far. I think the problem is how I've handled the keyboard event, so please take a look.
<div id="fullscreenDiv"/>
<script defer="defer">
var stage = new Kinetic.Stage({
container: 'fullscreenDiv',
width: 1180,
height: 700
});
var layer = new Kinetic.Layer();
var gamepart = new Kinetic.Rect({
x: 0,
y: 0,
width: 1180,
height: 500,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
var statuspart = new Kinetic.Rect({
x: 0,
y: 500,
width: 1180,
height: 100,
fill: 'blue',
stroke: 'black',
strokeWidth: 4
});
var box1 = new Kinetic.Rect({
x: 100,
y: 225,
width: 130,
height: 90,
fill: 'white',
stroke: 'black',
strokeWidth: 3
});
var box2 = new Kinetic.Rect({
x: 400,
y: 225,
width: 130,
height: 90,
fill: 'white',
stroke: 'black',
strokeWidth: 3
});
var line12 = new Kinetic.Line({
points: [230, 270, 400, 270],
stroke: 'black',
strokeWidth: 3
});
var box3 = new Kinetic.Rect({
x: 1400,
y: 225,
width: 130,
height: 90,
fill: 'white',
stroke: 'black',
strokeWidth: 3
});
var line23 = new Kinetic.Line({
points: [530, 270, 1400, 270],
stroke: 'black',
strokeWidth: 3
});
// add the shape to the layer
layer.add(gamepart);
layer.add(statuspart);
layer.add(box1);
layer.add(box2);
layer.add(line12);
layer.add(box3);
layer.add(line23);
// add the layer to the stage
stage.add(layer);
layer.draw();
window.addEventListener('keydown', function(e) {
if (e.keyCode == 37) //Left Arrow Key
box1.attrs.x -= 10;
if (e.keyCode == 38) //Up Arrow Key
box1.attrs.y += 10;
if (e.keyCode == 39) //Right Arrow Key
box1.x += 10;
if (e.keyCode == 40) //Top Arrow Key
box1.attributes.x -= 10;
stage.draw();
});
</script>
Edit
This is picture of the webpage, and I want to move the rectandgles with that's white inside.
You are very close. Just use other API to change attributes:
if (e.keyCode == 37) //Left Arrow Key
box1.x(box1.x() - 10);
if (e.keyCode == 38) //Up Arrow Key
box1.y(box1.y() + 10);
if (e.keyCode == 39) //Right Arrow Key
box1.x(box1.x() + 10);

Resources