最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - object alignment in fabric js - Stack Overflow

programmeradmin1浏览0评论

I am trying to implement object alignment (i.e Right, Center, Left, Top, Bottom) in canvas. Lets say user selected one object (i.e 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=".2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script type='text/javascript' src=".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 am trying to implement object alignment (i.e Right, Center, Left, Top, Bottom) in canvas. Lets say user selected one object (i.e 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).

Share Improve this question edited Jul 21, 2019 at 3:41 user128511 asked Nov 21, 2017 at 8:49 DS9DS9 3,0336 gold badges58 silver badges106 bronze badges 2
  • 1 What if object is rotated, how you want to align that object? – Durga Commented Nov 21, 2017 at 8:55
  • in Same position it was earlier. – DS9 Commented Nov 21, 2017 at 8:56
Add a comment  | 

5 Answers 5

Reset to default 10

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':
            obj.set({
              top:-groupHeight/2
              
            });
            obj.setCoords();
            this.canvas.renderAll();

          break;
          case 'bottom':
            obj.set({
              
              top: groupHeight / 2 - itemHeight 
              
            
            });
            obj.setCoords();
            this.canvas.renderAll();
          break;
      default:
        break;
    }
  });
}
发布评论

评论列表(0)

  1. 暂无评论