Search code examples
javascripthtml5-canvasfabricjs

Retrieving quadrant name (origin) when moving an object - Canvas, Fabric.js


Thank you for the time taken to read, and potentially help. So I've started experimenting with canvas and fabric.js - no experience what so ever.

I've hit a stumbling block, and that's getting the origin, or rather the location of a moving object. It appears that originX, originY is always the same value, regardless of where an object is moved to.

For a visual indicator, I've created an image:

enter image description here

What I don't understand is why originX, and originY is always left, and top? I assume this is the location of the control for offset?

I'm hoping their is some built in canvas/fabric methods, If not could any of you kind folk point me in the right direction on trying to output the correct origins of when an object is dropped?

A small snippet of just trying to log the output when moving an object:

canvas.on('object:moving', e => {
     
      console.log("moving target, X: " + e.target.originX);
      console.log("moving target, Y: " + e.target.originY);

      console.log("moving active obj, X: " + canvas.getActiveObject().originX);
      console.log("moving active obj, Y: " + canvas.getActiveObject().originY);

      const coords = e.target.calcCoords();
      const trans = e.target.calcTransformMatrix();

      console.log(coords);
      console.log(trans);
      console.log(e.target.getOrigin());
  });

Solution

  • Unless you modify the origin of a fabricjs object, querying it's .originX and .originY won't ever return anything else than left and top respectively simply because these properties do not refer to it's on-screen position.

    An object's origin refers to it's own coordinate space and it's meaning becomes more obvious if you think about a transformation like scale applied to an actual object.

    Have a look at the following example. The origin of the square to the left is at center/center while it's top/left for the square to the right.

    let canvas = new fabric.Canvas("c1");
    let context = document.getElementById("c1").getContext("2d");
    let rectA = new fabric.Rect({
      width: 50,
      height: 50,
      fill: "blue",
      left: 50,
      top: 50,
      originX: "center",
      originY: "center"
    });
    canvas.add(rectA);
    
    let rectB = new fabric.Rect({
      width: 50,
      height: 50,
      fill: "blue",
      left: 150,
      top: 50,
      originX: "left",
      originY: "top"
    });
    
    canvas.add(rectB);
    
    let scale = 1;
    let a = 0;
    
    function updateCanvas() {
      rectA.scale(scale);
      rectB.scale(scale);
      scale = 1 + Math.sin(a);
      a += 0.1;
      canvas.renderAll();
    
      context.beginPath();
      context.arc(50, 50, 2, 0, 2 * Math.PI, false);
      context.fillStyle = 'red';
      context.fill();
      context.lineWidth = 2;
      context.strokeStyle = 'red';
      context.stroke();
    
      context.beginPath();
      context.arc(150, 50, 2, 0, 2 * Math.PI, false);
      context.fill();
      context.stroke();
    }
    
    let interval = setInterval(updateCanvas, 100);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/451/fabric.min.js" integrity="sha512-qeu8RcLnpzoRnEotT3r1CxB17JtHrBqlfSTOm4MQzb7efBdkcL03t343gyRmI6OTUW6iI+hShiysszISQ/IahA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <canvas id="c1" width="400" height="200"></canvas>

    If you want to know which quadrant of the canvas is occupied by your object, you have to manually calculate it based on your object's coordinates.

    Here's another example - try dragging the circle around.

    var canvas = new fabric.Canvas('c1', {
      backgroundColor: "#cccccc"
    });
    canvas.add(new fabric.Circle({
      radius: 30,
      fill: '#f55',
      top: 100,
      left: 100
    }));
    
    canvas.on('object:moving', e => {
      if (e.target.left < canvas.width / 2) {
        if (e.target.top < canvas.height / 2) {
          console.log("TopLeft");
        } else {
          console.log("BottomLeft");
        }
      } else {
        if (e.target.top < canvas.height / 2) {
          console.log("TopRight");
        } else {
          console.log("BottomRight");
        }
      }
    
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/451/fabric.min.js" integrity="sha512-qeu8RcLnpzoRnEotT3r1CxB17JtHrBqlfSTOm4MQzb7efBdkcL03t343gyRmI6OTUW6iI+hShiysszISQ/IahA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <canvas id="c1" width="400" height="200"></canvas>