Is there anyway to make canvas object auto snap al

2019-04-14 12:09发布

问题:

Im working on a project of building a editor, i am using the Fabric.js, but i don know how to make the canvas object auto align.

Is there any example of making canvas object auto align with another object like this?

image of snap example
auto snap example from other site

link here:
auto snap example site

回答1:

I was working on the same thing. This seems to be a pretty common feature request so I thought I'd share what I worked out. It could use some refinement. For instance, I've noticed if you scale the red box, the scale is not applied and it doesn't align right. However, I think it demonstrates the basic principal well and you can elaborate on it to fit your needs.

(Note: 8/1/2017: Working to place a more comprehensive code base on GitHub. (https://github.com/JerrodV/FabricObjectAlignment) More Details Soon!)

You can view the fiddle here

Html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <canvas id="c" height="600" width="600" style="border:1px solid #c1c1c1;"></canvas>
    </div>
    </form>
    <script src="Scripts/jquery-3.1.1.min.js"></script>
    <script src="Scripts/fabric.min.js"></script>
    <script src="Scripts/default.js"></script>
</body>
</html>

Javascript:

Def = {
    canvas: null,    
    rect: null,
    lines: {
        top: null, 
        left: null,
        right: null,
        bottom: null
    },
    init: function () {
        Def.canvas = new fabric.Canvas('c');

        Def.canvas.on('object:moving', Def.events.objectMoving);

        Def.canvas.add(new fabric.Rect({
            height: 100,
            width: 100,
            top: 100,
            left: 200,
            fill: 'black',
            selectable: false
        }));

        Def.canvas.add(new fabric.Rect({
            height: 100,
            width: 100,
            top: 300,
            left: 100,
            fill: 'black',
            selectable: false
        }));        

        Def.rect = new fabric.Rect({
            height: 100,
            width: 100,
            top: 200,
            left: 250,
            fill: 'red'
        });

        Def.canvas.add(Def.rect);
    },
    events: {
        objectMoving: function (e) {
            //Get the object being manipulated
            var obj = e.target;

            //Set up an object representing its current position
            var curPos = {
                top: parseInt(obj.get('top')),
                left: parseInt(obj.get('left')),
                right: parseInt(obj.get('left') + obj.get('width')),
                bottom: parseInt(obj.get('top') + obj.get('height'))
            };

            //Set up an object that will let us be able to keep track of newly created lines
            var matches = {
                top: false,
                left: false,
                right: false,
                bottom: false
            }

            //Get the objects from the canvas
            var objects = Def.canvas.getObjects();

            //For each object
            for (var i in objects) {

                //If the object we are looing at is a line or the object being manipulated, skip it
                if (objects[i] === obj || objects[i].get('type') === 'line') { continue; }

                //Set up an object representing the position of the canvas object
                var objPos = {
                    top: parseInt(objects[i].get('top')),
                    left: parseInt(objects[i].get('left')),
                    right: parseInt(objects[i].get('left') + obj.get('width')),
                    bottom: parseInt(objects[i].get('top') + obj.get('height'))
                }

                //Look at all 4 sides of the object and see if the object being manipulated aligns with that side.

                //Top////////////////////////////////////
                if (Def.inRange(objPos.top, curPos.top)) {
                    //We match. If we don't already have aline on that side, add one.
                    if (!Def.lines.top) {
                        Def.drawLine('top', objPos.top);
                        //Keep track of the fact we found a match so we don't remove the line prematurely.
                        matches.top = true;
                        //Snap the object to the line
                        obj.set('top', objPos.top).setCoords();
                    }
                }               
                //Left////////////////////////////////////
                if (Def.inRange(objPos.left, curPos.left)) {
                    if (!Def.lines.left) {
                        Def.drawLine('left', objPos.left);                        
                        matches.left = true;
                        obj.set('left', objPos.left).setCoords();
                    }
                }                
                //Right////////////////////////////////////
                if (Def.inRange(objPos.right, curPos.right)) {
                    if (!Def.lines.right) {
                        Def.drawLine('right', objPos.right);                        
                        matches.right = true;                        
                        obj.set('left', objPos.right - objects[i].get('width')).setCoords();
                    }
                }                
                //Bottom////////////////////////////////////
                if (Def.inRange(objPos.bottom, curPos.bottom)) {
                    if (!Def.lines.bottom) {
                        Def.drawLine('bottom', objPos.bottom);                        
                        matches.bottom = true;
                        obj.set('top', objPos.bottom - objects[i].get('height')).setCoords();
                    }
                }

                //Look at the side we matched on. If we did not match, and we have a line, remove the line.
                for (var j in matches) {
                    var m = matches[j];
                    var line = Def.lines[j]; 
                    if (!m && line) {
                        Def.canvas.remove(line);
                        Def.lines[j] = null;
                    }

                }

            }
            Def.canvas.renderAll();
        }
    },
    drawLine: function (side, pos) {
        var ln = null
        switch (side) {
            case 'top':
                ln = new fabric.Line([Def.canvas.get('width'), 0, 0, 0], {
                    left: 0,
                    top: pos,
                    stroke: 'rgb(178, 207, 255)'
                });
                Def.lines.top = ln;
                break;
            case 'left':
                ln = new fabric.Line([0, Def.canvas.get('height'), 0, 0], {
                    left: pos,
                    top: 0,
                    stroke: 'rgb(178, 207, 255)'
                });
                Def.lines.left = ln;
                break;
            case 'right':
                ln = new fabric.Line([0, Def.canvas.get('height'), 0, 0], {
                    left: pos,
                    top: 0,
                    stroke: 'rgb(178, 207, 255)'
                });
                Def.lines.right = ln;
                break;
            case 'bottom':
                ln = new fabric.Line([Def.canvas.get('width'), 0, 0, 0], {
                    left: 0,
                    top: pos,
                    stroke: 'rgb(178, 207, 255)'
                });
                Def.lines.bottom = ln;
                break;
        }
        Def.canvas.add(ln).renderAll();
    },
    alignTolerance : 6,
    inRange: function (val1, val2) {
        if ((Math.max(val1, val2) - Math.min(val1, val2)) <= Def.alignTolerance) { return true; }        
        else { return false; }
    }
};

$(Def.init);

I hope you find this useful. Good Luck!