Search code examples
javascriptfabricjsfabricjs2

How to create object stick on center of parent object in FabricJS?


I have an object e.g circle and I want to add more circles on border of the parent circle that must be center aligned.

On the attached gif image you can see my cursor - it only allows me to create a child circle on border of the parent.

enter image description here

Till now I was only able to create the parent circle.

var circle = new fabric.Circle({
        strokeWidth: 1,
        stroke: "#222",
        noScaleCache: false,
        strokeUniform: true,
        scaleX: 1,
        scaleY: 1,
        left: canvas.getWidth() / 2,
        top: canvas.getHeight() / 2,
        radius: fabric.util.parseUnit(circleSize + 'in')
    });

Solution

  • Basically you need to calculate Euclidean distance between the circle that you want to draw and the main circle, and if the distance is near to the border of the main circle you just draw a circle at the mouse position where you clicked.

    The distance is calculated using Math.hypot function.

    And here is the code.

    var canvas = new fabric.Canvas('c', {
      selection: false
    });
    
    var circleRadius = 100;
    
    var c = new fabric.Circle({
      strokeWidth: 3,
      stroke: "#222",
      fill: "#55ff55",
      noScaleCache: false,
      strokeUniform: true,
      selectable: false,
      scaleX: 1,
      scaleY: 1,
      left: canvas.getWidth() / 2 - circleRadius,
      top: canvas.getHeight() / 2 - circleRadius,
      radius: circleRadius
    });
    
    canvas.add(c);
    
    var circleAngle = Math.atan2(c.getCenterPoint().x,c.getCenterPoint().y);
    var circle = new fabric.Circle({
      strokeWidth: 3,
      stroke: "#222",
      radius: 30,
      selectable: true,
      originX: 'center',
      originY: 'center',
      fill: "#ff5555ff",
      left: Math.sin(circleAngle) * circleRadius + canvas.getWidth() / 2,
      top: Math.cos(circleAngle) * circleRadius + canvas.getHeight() / 2
    });
    
    canvas.add(circle);
    
    var isDown, origX, origY;
    
    canvas.on('mouse:down', function(o) {
      var pointer = canvas.getPointer(o.e);
      origX = pointer.x;
      origY = pointer.y;
      if (isDown) {
        circle.set({
          strokeWidth: 3,
          stroke: "#222",
          radius: 30,
          left: pointer.x,
          top: pointer.y,
          fill: "#ff5555ff"
        });
        canvas.add(circle);
        circle = new fabric.Circle({
          strokeWidth: 3,
          stroke: "#222",
          radius: 1,
          selectable: true,
          originX: 'center',
          originY: 'center'
        });
        canvas.add(circle);
      }
    });
    
    canvas.on('mouse:move', function(o) {
      var pointer = canvas.getPointer(o.e);
      var x = pointer.x - c.getCenterPoint().x;
      var y = pointer.y - c.getCenterPoint().y;
      circle.set({
        strokeWidth: 3,
        stroke: "#222",
        radius: 30,
        left: Math.sin(Math.atan2(x,y))*c.radius + canvas.getWidth() / 2,
        top: Math.cos(Math.atan2(x,y))*c.radius + canvas.getHeight() / 2,
        fill: "#ff5555ff"
      });
    
      if (nearToBorder(pointer, c.getCenterPoint(), c.radius)) {
        isDown = true;
      } else {
        isDown = false;
      }
      canvas.renderAll();
    });
    
    canvas.on('mouse:up', function(o) {
      isDown = false;
    });
    
    function nearToBorder(pointer, circlePoint, r) {
      var d = Math.hypot(pointer.x - (circlePoint.x), pointer.y - (circlePoint.y));
      return d >= r - 10 && d <= r + 10;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.0.0-rc.1/fabric.min.js"></script>
    <canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>