Search code examples
javascriptcanvasfabricjs

Align object to canvas in fabricjs


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)


Solution

  • 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>