简体   繁体   中英

object alignment in fabric js

I am trying to implement object alignment (ie Right, Center, Left, Top, Bottom) in canvas. Lets say user selected one object (ie Text or Image) then if user hit the Align right option then that selected object should align to right side.

I have tried below 2 methods but no success.

 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({ left: 130, 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'); if (cur_value != '') { process_align(cur_value); } else { alert('Please select a item'); return false; } }); /* Align the selected object */ function process_align(val) { switch (val) { case 'right': //Tried below one with //activeObj.setPositionByOrigin(new fabric.Point(activeObj.top, canvas.getWidth()), activeObj.originY,'center'); //Tried below one but its not working var activeObj = canvas.getActiveObject(); activeObj.set({ originX: 'right', //originY: 'center' }); activeObj.setCoords(); canvas.renderAll(); 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="center">Align Center</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>

I have only implemented Align Right, as i am not sure how to do alignment for other Alignment (Top,Bottom, Left, Center).

 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({ left: 130, 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 'center': activeObj.set({ left: (canvas.width / 2) - ((activeObj.width * activeObj.scaleX) / 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="center">Align Center</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>

You can set left/top according as your need, then setCoords() . Check snippet.

While the other answers put me on the right track, none of them really worked for rotated objects, the trick is to set the transform origin to center and work with getBoundingRect() like DivPusher pointed out.

 //Override fabric transform origin to center fabric.Object.prototype.set({ originX:'center', originY:'center', }); 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.Text('Fabricjs', { left: 200, top: 80, fill: '#555', angle: 35, scaleX: .8, scaleY: .8, })); canvas.add(new fabric.Rect({ left: 90, top: 130, height: 50, width: 50, fill: 'blue', angle: 45, })); 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 an item'); return false; } }); /* Align the selected object */ function process_align(val, activeObj) { const bound = activeObj.getBoundingRect() switch (val) { case 'left': activeObj.set({ left: activeObj.left - bound.left }); break; case 'right': activeObj.set({ left: canvas.width - bound.width/2 }); break; case 'top': activeObj.set({ top: activeObj.top - bound.top }); break; case 'bottom': activeObj.set({ top: canvas.height - bound.height/2 }); break; case 'center': activeObj.set({ left: canvas.width / 2 }); break; case 'middle': activeObj.set({ top: canvas.height / 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="center">Align Center</button> <button class="alignment" data-action="right">Align Right</button> <button class="alignment" data-action="top">Align Top</button> <button class="alignment" data-action="middle">Align Middle</button> <button class="alignment" data-action="bottom">Align Bottom</button> <canvas id="a" width="400" height="200"></canvas>

Here is my implementation of setAlign method using FabricJS 2.0

setAlign(align, canvas) {
    let activeObj = canvas.getActiveObject(),
      horizontalCenter = (activeObj.width * activeObj.scaleX) / 2,
      verticalCenter = (activeObj.height * activeObj.scaleY) / 2,
      { width, height } = canvas

    switch (align) {
      case 'top':
        activeObj.set({ top: verticalCenter })
        break
      case 'left':
        activeObj.set({ left: horizontalCenter })
        break
      case 'bottom':
        activeObj.set({ top: height - verticalCenter })
        break
      case 'right':
        activeObj.set({ left: width - horizontalCenter })
        break
      case 'center':
        activeObj.set({ left: (width / 2) })
        break
      case 'middle':
        activeObj.set({ top: (height / 2) })
        break
    }

    activeObj.setCoords()
    canvas.renderAll()
}

Durga's answer is not perfect, because if you rotate a box clockwise and click on the align left button, the box will be aligned by its top left corner, not by its actual left edge.

You need to use the getBoundingRect() function. Demo:

 var canvas = this.__canvas = new fabric.Canvas('c'); fabric.Object.prototype.transparentCorners = false; var rect = new fabric.Rect({ left: 100, top: 50, width: 100, height: 100, fill: 'green', angle: 20, padding: 0 }); canvas.add(rect); canvas.setActiveObject(canvas.item(0)); 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 = 'red'; canvas.forEachObject(function(obj) { var bound = obj.getBoundingRect(); canvas.contextContainer.strokeRect( bound.left + 0.5, bound.top + 0.5, bound.width, bound.height ); }) }); function alignLeft(){ var obj = canvas.getActiveObject(); var bound = obj.getBoundingRect(); // console.log(bound.left); // console.log(obj.left); obj.set('left', (obj.left - bound.left)); canvas.getActiveObject().setCoords(); canvas.renderAll(); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"></script> <button onclick="alignLeft()">align real left</button> <canvas id="c" width="500" height="300" style="border: 1px solid rgb(204, 204, 204); position: absolute; left: 0px; top: 0px; touch-action: none; user-select: none;" ></canvas>

Below code helps me to achieve alignment within the group of objects in fabric js.. hope it will help you :

...

var activeObject = this.canvas.getActiveObject();
if (activeObject) {
  var groupWidth = activeObject.getBoundingRect().width;
  var groupHeight = activeObject.getBoundingRect().height;


  this.canvas.getActiveObjects().map((obj: any) => {
    var itemWidth = obj.getBoundingRect().width;
    var itemHeight = obj.getBoundingRect().height;
   
    switch (alignType) {
      case 'left':
        obj.set({
          left: -(groupWidth / 2),
          originX: 'left'
        });
        obj.setCoords();
        this.canvas.renderAll();
        break;
      case 'center':
        obj.set({
          left: (0 - itemWidth / 2),
          originX: 'left'
        });
        obj.setCoords();
        this.canvas.renderAll();

        break;
      case 'right':
        obj.set({
          left: (groupWidth / 2 - itemWidth / 2),
          originX: 'center'
        });
        obj.setCoords();
        this.canvas.renderAll();
        break;
        case 'top':
          if(groupHeight != itemHeight){
            obj.set({
              top:-groupHeight/2
              
            });
            obj.setCoords();
            this.canvas.renderAll();
          }
          break;
          case 'bottom':
          if(groupHeight != itemHeight){
            obj.set({
              
              top: groupHeight / 2 - itemHeight 
              
            
            });
            obj.setCoords();
            this.canvas.renderAll();
          }
          break;
      default:
        break;
    }
  });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM