Reputation: 1607
I'm trying to implement object alignment (i.e Top - Bottom - Left - Right - CenterH - CenterV) my code is working great but i don't know how to handle the alignment if the object is rotated (with the code provided part of the object disappear when the angle is different than 0, I want to know how to calculate the min left offset to be able to still see the whole rotated object). My code below.
var canvas = new fabric.Canvas('a');
canvas.add(new fabric.Rect({
left: 50,
top: 50,
height: 50,
width: 50,
fill: 'red'
}));
canvas.add(new fabric.Rect({
angle: 76,
left: 180,
top: 50,
height: 50,
width: 50,
fill: 'green'
}));
canvas.add(new fabric.Rect({
left: 90,
top: 130,
height: 50,
width: 50,
fill: 'blue'
}));
canvas.renderAll();
$('.alignment').click(function() {
var cur_value = $(this).attr('data-action');
var activeObj = canvas.getActiveObject() || canvas.getActiveGroup();
if (cur_value != '' && activeObj) {
process_align(cur_value, activeObj);
activeObj.setCoords();
canvas.renderAll();
} else {
alert('Please select a item');
return false;
}
});
/* Align the selected object */
function process_align(val, activeObj) {
switch (val) {
case 'left':
activeObj.set({
left: 0
});
break;
case 'right':
activeObj.set({
left: canvas.width - (activeObj.width * activeObj.scaleX)
});
break;
case 'top':
activeObj.set({
top: 0
});
break;
case 'bottom':
activeObj.set({
top: canvas.height - (activeObj.height * activeObj.scaleY)
});
break;
case 'centerH':
activeObj.set({
left: (canvas.width / 2) - ((activeObj.width * activeObj.scaleX) / 2)
});
break;
case 'centerV':
activeObj.set({
top: (canvas.height / 2) - ((activeObj.height * activeObj.scaleY) / 2)
});
break;
}
}
canvas {
border: 2px solid black;
}
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<button class="alignment" data-action="left">Align Left</button>
<button class="alignment" data-action="centerH">Align CenterH</button>
<button class="alignment" data-action="centerV">Align CenterV</button>
<button class="alignment" data-action="right">Align Right</button>
<button class="alignment" data-action="top">Align Top</button>
<button class="alignment" data-action="bottom">Align Bottom</button>
<canvas id="a" width="400" height="200"></canvas>
How can i update the code to be able to align rotated object as well? (check the alignment on the green rect)
Upvotes: 1
Views: 1384
Reputation: 1607
So as @HelderSepulveda suggested I used aCoords to dynamically calculate the coordinates, I'll paste the working code below in case anyone needs it.
var canvas = new fabric.Canvas('a');
canvas.add(new fabric.Rect({
left: 50,
top: 50,
height: 50,
width: 50,
fill: 'red'
}));
canvas.add(new fabric.Rect({
angle: 76,
left: 180,
top: 50,
height: 50,
width: 50,
fill: 'green'
}));
canvas.add(new fabric.Rect({
left: 90,
top: 130,
height: 50,
width: 50,
fill: 'blue'
}));
canvas.renderAll();
$('.alignment').click(function() {
var cur_value = $(this).attr('data-action');
var activeObj = canvas.getActiveObject() || canvas.getActiveGroup();
if (cur_value != '' && activeObj) {
process_align(cur_value, activeObj);
activeObj.setCoords();
canvas.renderAll();
} else {
alert('Please select a item');
return false;
}
});
/* Align the selected object */
function process_align(val, activeObj) {
switch (val) {
case 'left':
var left;
if(activeObj.angle <= 90) {
left = activeObj.aCoords.tl.x - activeObj.aCoords.bl.x;
}
if(activeObj.angle > 90 && activeObj.angle <= 180) {
left = activeObj.aCoords.tl.x - activeObj.aCoords.br.x;
}
if(activeObj.angle > 180 && activeObj.angle <= 270) {
left = activeObj.aCoords.tl.x - activeObj.aCoords.tr.x;
}
if(activeObj.angle > 270) {
left = 0;
}
activeObj.set({
left: left
});
break;
case 'right':
var left;
if(activeObj.angle <= 90) {
left = activeObj.aCoords.tl.x + (canvas.width - activeObj.aCoords.tr.x);
}
if(activeObj.angle > 90 && activeObj.angle <= 180) {
left = canvas.width;
}
if(activeObj.angle > 180 && activeObj.angle <= 270) {
left = activeObj.aCoords.tl.x + (canvas.width - activeObj.aCoords.bl.x);
}
if(activeObj.angle > 270) {
left = activeObj.aCoords.tl.x + (canvas.width - activeObj.aCoords.br.x);
}
activeObj.set({
left: left
});
break;
case 'top':
var top;
if(activeObj.angle <= 90) {
top = 0;
}
if(activeObj.angle > 90 && activeObj.angle <= 180) {
top = activeObj.aCoords.tl.y - activeObj.aCoords.bl.y;
}
if(activeObj.angle > 180 && activeObj.angle <= 270) {
top = activeObj.aCoords.tl.y - activeObj.aCoords.br.y;
}
if(activeObj.angle > 270) {
top = activeObj.aCoords.tl.y - activeObj.aCoords.tr.y;
}
activeObj.set({
top: top
});
break;
case 'bottom':
var top;
if(activeObj.angle <= 90) {
top = activeObj.aCoords.tl.y + (canvas.height - activeObj.aCoords.br.y);
}
if(activeObj.angle > 90 && activeObj.angle <= 180) {
top = activeObj.aCoords.tl.y + (canvas.height - activeObj.aCoords.tr.y);
}
if(activeObj.angle > 180 && activeObj.angle <= 270) {
top = canvas.height;
}
if(activeObj.angle > 270) {
top = activeObj.aCoords.tl.y + (canvas.height - activeObj.aCoords.bl.y);
}
activeObj.set({
top: top
});
break;
case 'centerH':
activeObj.viewportCenterH();
break;
case 'centerV':
activeObj.viewportCenterV();
break;
}
}
canvas {
border: 2px solid black;
}
<script
src="https://code.jquery.com/jquery-2.2.4.min.js"
integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
crossorigin="anonymous"></script>
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<button class="alignment" data-action="left">Align Left</button>
<button class="alignment" data-action="centerH">Align CenterH</button>
<button class="alignment" data-action="centerV">Align CenterV</button>
<button class="alignment" data-action="right">Align Right</button>
<button class="alignment" data-action="top">Align Top</button>
<button class="alignment" data-action="bottom">Align Bottom</button>
<canvas id="a" width="400" height="200"></canvas>
Upvotes: 2
Reputation: 17664
Take a look at this approach, the animate
has a nice abort option that I'm using to stop if any of the corner points exceeds the boundaries of the canvas, I just tested on chrome and to simplify removed the center aligns, you can add that later if you need.
function move(obj, prop, value) {
obj.saveState();
obj.animate(prop, value, {
duration: 1000,
onChange: canvas.renderAll.bind(canvas),
onComplete: canvas.renderAll.bind(canvas),
easing: fabric.util.ease["easeOutQuad"],
abort: function () {
obj.setCoords();
for (const prop in obj.aCoords) {
point = obj.aCoords[prop]
if (point.x <= 0 || point.x >= canvas.width ||
point.y <= 0 || point.y >= canvas.height) {
obj.left = obj._stateProperties.left;
obj.top = obj._stateProperties.top;
return true
}
}
obj.saveState();
}
});
}
function process_align(val, obj) {
switch (val) {
case 'left':
move(obj, 'left', 1);
break;
case 'right':
move(obj, 'left', canvas.width - 1);
break;
case 'top':
move(obj, 'top', 1);
break;
case 'bottom':
move(obj, 'top', canvas.height - 1);
break;
}
}
var canvas = new fabric.Canvas('a');
canvas.stateful = true;
canvas.add(new fabric.Rect({ left: 50, top: 50, height: 50, width: 50, fill: 'red' }));
canvas.add(new fabric.Rect({ angle: 76, left: 240, top: 50, height: 50, width: 50, fill: 'green' }));
canvas.add(new fabric.Rect({ left: 120, top: 100, height: 50, width: 50, fill: 'blue' }));
canvas.renderAll();
$('.alignment').click(function () {
var cur_value = $(this).attr('data-action');
var activeObj = canvas.getActiveObject() || canvas.getActiveGroup();
if (cur_value != '' && activeObj) {
process_align(cur_value, activeObj);
activeObj.setCoords();
canvas.renderAll();
} else {
alert('Please select a item');
return false;
}
});
canvas {
border: 2px solid black;
}
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script type='text/javascript' src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.19/fabric.js"></script>
<button class="alignment" data-action="left">Align Left</button>
<button class="alignment" data-action="right">Align Right</button>
<button class="alignment" data-action="top">Align Top</button>
<button class="alignment" data-action="bottom">Align Bottom</button>
<canvas id="a" width="400" height="160"></canvas>
And there are a few animation easings you can pick from:
http://fabricjs.com/animation-easing
Upvotes: 1