Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Canvas: Rectangles -- Snap to grid / Snap to objects

I managed to manipulate Fabric.js to add a snap and scale to grid functionality by:

var grid = 100;
//Snap to Grid
canvas.on('object:moving', function (options) {
    options.target.set({
        left: Math.round(options.target.left / grid) * grid,
        top: Math.round(options.target.top / grid) * grid
    });
});
canvas.on('object:scaling', function (options) {
    options.target.set({
        left: Math.round(options.target.left / grid) * grid,
        top: Math.round(options.target.top / grid) * grid
    });
});

Now I want to add snap to objects functionality. My idea was to check intersection of two objects and then lock somehow the movement. I know its not the best attempt, but at least it snaps to it, but does not allow to move the object away anymore. And: right now it is not implemented well. See: http://jsfiddle.net/gcollect/y9kyq/

I have three issues:

  1. The "snap" doesn't work well, because the object left attribute depends on the pointer somehow. Replication by dragging object and watching my controls output. For example when moving the red rectangle to position left:62, the rectangle isn't intersected with the blue rectangle and can still moved away. How can I reload the actual left value of the rectangle. Because of my snap to grid lines, it is at left:100 and not at left:62.
  2. Any idea how I can add a snap to object functionality? And prohibit intersection?
  3. How can I check this for n objects and not only for two?

Thanks for your comments.

PS: The jsfiddle example doesn't show the scale to grid functionality, because it needed Fabric.js manipulation in line: 11100

var distroundedforgrid = Math.round(dist/100)*100;      
      transform.newScaleX = Math.round((transform.original.scaleX * distroundedforgrid / lastDist)*10)/10;
      transform.newScaleY = Math.round((transform.original.scaleY * distroundedforgrid / lastDist)*10)/10;

      target.set('scaleX', transform.newScaleX);
      target.set('scaleY', transform.newScaleY);
    }
like image 633
gco Avatar asked Jan 27 '14 15:01

gco


2 Answers

For those who are still interested in the solution: I solved it here: https://stackoverflow.com/a/22649022/3207478 See jsfiddle: http://jsfiddle.net/gcollect/FD53A/

Working with .oCoords.tl .tr .bl. and .br solved it.

like image 176
gco Avatar answered Oct 13 '22 01:10

gco


For rescaling based on the grid see this JSfiddle

function snapScaling(options) {
    var target = options.target;
    var type = canvas.getActiveObject().get('type');
    var corner = target.__corner;
    var w = target.getWidth();
    var h = target.getHeight();
    var snap = {   // Closest snapping points
      top: Math.round(target.top / grid) * grid,
      left: Math.round(target.left / grid) * grid,
      bottom: Math.round((target.top + h) / grid) * grid,
      right: Math.round((target.left + w) / grid) * grid,
    };
    snap.height = snap.top - snap.bottom;
    if(snap.height < 0) {
      snap.height *= - 1;
    }
    snap.width = snap.left - snap.right;
    if(snap.width < 0) {
      snap.width *= - 1;
    }
    switch (corner) {
      case 'mt':
      case 'mb':
          target.top = snap.top;
          target.height = snap.height;
          target.scaleY = 1;
        break;
      case 'ml':
      case 'mr':
        target.left = snap.left;
        target.width = snap.width;
        target.scaleX = 1;
        break;
      case 'tl':
      case 'bl':
      case 'tr':
      case 'br':
        target.top = snap.top;
        target.left = snap.left;

        target.height = snap.height;
        target.width = snap.width;

        target.scaleY = 1;
        target.scaleX = 1;
    }

    if(type == 'ellipse') {
      target.rx = (target.width / 2);
      target.ry = (target.height / 2);
    }
  }
like image 23
Kbladel Avatar answered Oct 12 '22 23:10

Kbladel