Using fabric.js hovering lines is fired around them (not only exactly over them)

1.8k views Asked by At

I have made variation of stick man example which allows to move not only the circles, but the lines too. The problem that I face is that hovering inclined lines the area that affect the line is not the exact line, but a space around it, too.

To see the problem:

  1. Move a circle to achieve some line which is not horizontal or vertical
  2. Try to move that line

Te result: you will see that this is posible even you are not exactely over the line.

//to save the old cursor position: used on line mooving
var _curX, _curY;
$(function() {
  //create the fabriccanvas object & disable the canvas selection
  var canvas = new fabric.Canvas('c', {
    selection: false
  });
  //move the objects origin of transformation to the center
  fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

  function makeCircle(left, top, line1, line2) {

    var c = new fabric.Circle({
      left: left,
      top: top,
      strokeWidth: 2,
      radius: 6,
      fill: '#fff',
      stroke: '#666'
    });

    c.hasControls = c.hasBorders = false;

    c.line1 = line1;
    c.line2 = line2;

    return c;
  }

  function makeLine(coords, name) {
    var l = new fabric.Line(coords, {
      stroke: 'red',
      strokeWidth: 4,
      selectable: true, //false
      name: name
    });

    l.hasControls = l.hasBorders = false;

    return l;
  }

  //initial shape information
  var line = makeLine([250, 125, 350, 125], "l1"),
    line2 = makeLine([350, 125, 350, 225], "l2"),
    line3 = makeLine([350, 225, 250, 225], "l3"),
    line4 = makeLine([250, 225, 250, 125], "l4");

  canvas.add(line, line2, line3, line4);

  canvas.add(
    makeCircle(line.get('x1'), line.get('y1'), line4, line), makeCircle(line.get('x2'), line.get('y2'), line, line2), makeCircle(line2.get('x2'), line2.get('y2'), line2, line3), makeCircle(line3.get('x2'), line3.get('y2'), line3, line4)
  );

  canvas.on('object:selected', function(e) {
    //find the selected object type
    var objType = e.target.get('type');
    if (objType == 'line') {
      _curX = e.e.clientX;
      _curY = e.e.clientY;
      //console.log(_curX);
      //console.log(_curY);
    }
  });

  canvas.on('object:moving', function(e) {
    //find the moving object type
    var p = e.target;
    var objType = p.get('type');

    if (objType == 'circle') {
      p.line1 && p.line1.set({
        'x2': p.left,
        'y2': p.top
      });
      p.line2 && p.line2.set({
        'x1': p.left,
        'y1': p.top
      });
      //set coordinates for the lines - should be done if element is moved programmely
      p.line2.setCoords();
      p.line1.setCoords();

      canvas.renderAll();
    } else if (objType == 'line') {
      var _curXm = (_curX - e.e.clientX);
      var _curYm = (_curY - e.e.clientY);
      //console.log("moved: " + _curXm);
      //console.log("moved: " + _curYm);

      //loop all circles and if some contains the line - move it
      for (var i = 0; i < canvas.getObjects('circle').length; i++) {
        var currentObj = canvas.getObjects('circle')[i];

        if (currentObj.line1.get("name") == p.get('name') || currentObj.line2.get("name") == p.get('name')) {

          currentObj.set({
            'left': (currentObj.left - _curXm),
            'top': (currentObj.top - _curYm)
          });

          currentObj.setCoords();

          currentObj.line1 && currentObj.line1.set({
            'x2': currentObj.left,
            'y2': currentObj.top
          });
          currentObj.line2 && currentObj.line2.set({
            'x1': currentObj.left,
            'y1': currentObj.top
          });

          currentObj.line2.setCoords();
          currentObj.line1.setCoords();
        }
      }
      _curX = e.e.clientX;
      _curY = e.e.clientY;
    }
  });

});
canvas {
  border: 1px solid #808080;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>

1

There are 1 answers

2
Nistor Cristian On

Inside function makeLine please add perPixelTargetFind: true to the new fabric.Line.

I made you a jsFiddle