7

I based this fiddle off examples from this question. It includes:

  • Corner snapping
  • Edge snapping
  • Objects can overlap
  • Objects are fully contained within the canvas
  • Objects cannot have a size larger than the canvas area

Here is the code:

window.canvas = new fabric.Canvas('fabriccanvas');
window.counter = 0;
var newleft = 0,
    edgedetection = 20, //pixels to snap
    canvasWidth = document.getElementById('fabriccanvas').width,
    canvasHeight = document.getElementById('fabriccanvas').height;

canvas.selection = false;
plusrect();
plusrect();
plusrect();

function plusrect(top, left, width, height, fill) {
    window.canvas.add(new fabric.Rect({
        top: 300,
        name: 'rectangle ' + window.counter,
        left: 0 + newleft,
        width: 100,
        height: 100,
        fill: 'rgba(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ', 0.75)',
        lockRotation: true,
        originX: 'left',
        originY: 'top',
        cornerSize: 15,
        hasRotatingPoint: false,
        perPixelTargetFind: true,
        minScaleLimit: 1,
        maxHeight: document.getElementById("fabriccanvas").height,
        maxWidth: document.getElementById("fabriccanvas").width,
    }));
    window.counter++;
    newleft += 200;
}

this.canvas.on('object:moving', function (e) {
    var obj = e.target;
    obj.setCoords(); //Sets corner position coordinates based on current angle, width and height

    // Prevent object from leaving canvas
    if(obj.getLeft() < edgedetection) obj.setLeft(0);
    if(obj.getTop() < edgedetection) obj.setTop(0);
    if((obj.getWidth() + obj.getLeft()) > (canvasWidth - edgedetection)) obj.setLeft(canvasWidth - obj.getWidth());
    if((obj.getHeight() + obj.getTop()) > (canvasHeight - edgedetection)) obj.setTop(canvasHeight - obj.getHeight());

    canvas.forEachObject(function (targ) {
        activeObject = canvas.getActiveObject();

        if(targ === activeObject) return;

        // Standard snapping when within range
        if(Math.abs(activeObject.oCoords.tr.x - targ.oCoords.tl.x) < edgedetection) {
            activeObject.left = targ.left - activeObject.currentWidth;
        }
        if(Math.abs(activeObject.oCoords.tl.x - targ.oCoords.tr.x) < edgedetection) {
            activeObject.left = targ.left + targ.currentWidth;
        }
        if(Math.abs(activeObject.oCoords.br.y - targ.oCoords.tr.y) < edgedetection) {
            activeObject.top = targ.top - activeObject.currentHeight;
        }
        if(Math.abs(targ.oCoords.br.y - activeObject.oCoords.tr.y) < edgedetection) {
            activeObject.top = targ.top + targ.currentHeight;
        }

        // Snap top/left together when moving up/down or side to side if within range
        if(Math.abs(activeObject.top - targ.top) < edgedetection) {
            activeObject.top = targ.top;
        }
        if(Math.abs(activeObject.left - targ.left) < edgedetection) {
            activeObject.left = targ.left;
        }

        if(activeObject.intersectsWithObject(targ) && targ.intersectsWithObject(activeObject)) {
            targ.set({ strokeWidth: 2, stroke: 'red' });
        } else {
            targ.set({ strokeWidth: 0, stroke: false });
        }

        if(!activeObject.intersectsWithObject(targ)) {
            activeObject.set({ strokeWidth: 0, stroke: false });
        }
    });
});

What I'd like to know is if it's possible to extend this to add the following features:

  • Dynamic snapping. Continuing to drag an object after the initial snap will temporarily disable snapping until the object stops moving. For example, if I drag one box next to another, they will snap together once they are within range. However if I continue moving the first box, I can "drop" it in a position where it is within the snapping range but not aligned to the other box.
  • Show guide lines when selected object is within range of another object. Currently we add a border around the target object, but it would be better to show guidelines that extend outwards (possibly to the edge of the canvas) to more easily visualize the bounds of the target object.

I'm open to other feature suggestions as well. Essentially I am seeking a fully-featured snapping implementation.

Daniel Bonnell
  • 4,817
  • 9
  • 48
  • 88

0 Answers0