将对象的边缘彼此对齐,并防止重叠 [英] Snap edges of objects to each other and prevent overlap

查看:320
本文介绍了将对象的边缘彼此对齐,并防止重叠的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是防止在FabricJS画布中重叠两个或更多的矩形。



想象两个矩形有位置和大小的信息,你可以拖放任何



如果矩形A接近矩形B,则矩形A的位置应该对齐到矩形B的边。这应该适用于任何边



我有一个工作示例,用于在一个维度上捕捉(x-轴)。


$ b

我最好的jsfiddle尝试

请参阅 jsfiddle



但我需要它在两个维度上处理矩形。



可能会有帮助的代码片段

  object.oCoords.tl.x //左上角x位置。类似于右上(tr),左下(bl),右下(br)和.y对于y位置
mouse_pos = canvas.getPointer(e.e);
mouse_pos.x //指针x.position
mouse_pos.y //指针y.position
object.intersectsWithObject(targ)//对象=拖动的矩形,targ =目标矩形

捕捉应该适用于无限量的对象(不仅限于两个矩形)。

解决方案

这是基于gco的答案,更新为使用FabricJS 1.5.0,并进行了以下改进:




  • 形状不重叠。

  • 捕捉更灵敏。

  • 形状包含在



JS Fiddle: https://jsfiddle.net/aphillips8/31qbr0vn/1/

  var canvas = new fabric.Canvas('canvas'),
canvasWidth = document.getElementById('canvas')。width,
canvasHeight = document.getElementById('canvas')height,
counter = 0 ,
rectLeft = 0,
snap = 20; // Pixels to snap

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

function plusrect(top,left,width,height,fill){
var rect = new fabric.Rect({
top:300,
name: rectangle'+ counter,
left:0 + rectLeft,
width:100,
height:100,
fill:'rgba('+(Math.random ()* 256))+','+(Math.random()* 256))+','+(Math.random ,
lockRotation:true,
originX:'left',
originY:'top',
cornerSize:15,
hasRotatingPoint:false,
perPixelTargetFind :true,
minScaleLimit:1,
maxWidth:canvasWidth,
maxHeight:canvasHeight
});

rect.custom = {};
rect.custom.counter = counter;

canvas.add(rect);
counter ++;
rectLeft + = 200;
}

function findNewPos(distX,distY,target,obj){
//查看是否关注X轴还是Y轴
if(Math.abs distX)> Math.abs(distY)){
if(distX> 0){
target.setLeft(obj.getLeft() - target.getWidth());
} else {
target.setLeft(obj.getLeft()+ obj.getWidth());
}
} else {
if(distY> 0){
target.setTop(obj.getTop() - target.getHeight());
} else {
target.setTop(obj.getTop()+ obj.getHeight());
}
}
}

canvas.on('object:moving',function(options){
//当前角度,宽度和高度
options.target.setCoords();

//不允许从画布中删除对象
if(options.target.getLeft ; snap){
options.target.setLeft(0);
}

if(options.target.getTop()< snap){
options。 target.setTop(0);
}

if((options.target.getWidth()+ options.target.getLeft())>(canvasWidth - snap)){
options.target.setLeft(canvasWidth - options.target.getWidth());
}

if((options.target.getHeight()+ options.target.getTop )>(canvasHeight - snap)){
options.target.setTop(canvasHeight - options.target.getHeight());
}

//循环遍历对象
canvas.forEachObject(function(obj){
if(obj === options.target)return;

//如果对象与
相交if(options.target .isContainedWithinObject(obj)|| options.target.intersectsWithObject(obj)|| obj.isContainedWithinObject(options.target)){

var distX =((obj.getLeft()+ obj.getWidth())/ 2) - ((options.target.getLeft .target.getWidth())/ 2);
var distY =((obj.getTop()+ obj.getHeight())/ 2) - ((options.target.getTop()+ options.target.getHeight())/ 2);

//设置新位置
findNewPos(distX,distY,options.target,obj);
}

//水平地对齐对象

//如果底点在同一个Y轴上
if(Math.abs(( options.target.getTop()+ options.target.getHeight()) - (obj.getTop()+ obj.getHeight()))< snap){
//将目标BL对象BR
if(Math.abs(options.target.getLeft() - (obj.getLeft()+ obj.getWidth()))< snap){
options.target.setLeft(obj.getLeft + obj.getWidth());
options.target.setTop(obj.getTop()+ obj.getHeight() - options.target.getHeight());
}

//将目标BR对象BL
if(Math.abs((options.target.getLeft()+ options.target.getWidth()) - obj .getLeft())< snap){
options.target.setLeft(obj.getLeft() - options.target.getWidth());
options.target.setTop(obj.getTop()+ obj.getHeight() - options.target.getHeight());
}
}

//如果顶点在同一个Y轴上
if(Math.abs(options.target.getTop() - obj.getTop ))< snap){
//将目标TL捕捉到对象TR
if(Math.abs(options.target.getLeft() - (obj.getLeft()+ obj.getWidth()) )< snap){
options.target.setLeft(obj.getLeft()+ obj.getWidth());
options.target.setTop(obj.getTop());
}

//将目标TR捕获到对象TL
if(Math.abs((options.target.getLeft()+ options.target.getWidth()) - obj .getLeft())< snap){
options.target.setLeft(obj.getLeft() - options.target.getWidth());
options.target.setTop(obj.getTop());
}
}

//垂直对齐对象

//如果右点在同一个X轴上
if Math.abs((options.target.getLeft()+ options.target.getWidth()) - (obj.getLeft()+ obj.getWidth()))&snap; {
// Snap target TR对象BR
if(Math.abs(options.target.getTop() - (obj.getTop()+ obj.getHeight()))< snap){
options.target.setLeft obj.getLeft()+ obj.getWidth() - options.target.getWidth());
options.target.setTop(obj.getTop()+ obj.getHeight());
}

//将目标BR对象TR
if(Math.abs((options.target.getTop()+ options.target.getHeight()) - obj .getTop())< snap){
options.target.setLeft(obj.getLeft()+ obj.getWidth() - options.target.getWidth());
options.target.setTop(obj.getTop() - options.target.getHeight());
}
}

//如果左点在同一个X轴上
if(Math.abs(options.target.getLeft() - obj.getLeft ))< snap){
//将目标TL捕获到对象BL
if(Math.abs(options.target.getTop() - (obj.getTop()+ obj.getHeight()) )< snap){
options.target.setLeft(obj.getLeft());
options.target.setTop(obj.getTop()+ obj.getHeight());
}

//将目标BL捕捉到对象TL
if(Math.abs((options.target.getTop()+ options.target.getHeight()) - obj .getTop())< snap){
options.target.setLeft(obj.getLeft());
options.target.setTop(obj.getTop() - options.target.getHeight());
}
}
});

options.target.setCoords();

//如果对象仍然重叠

var outerAreaLeft = null,
outerAreaTop = null,
outerAreaRight = null,
innerAreaBottom = null ;

canvas.forEachObject(function(obj){
if(obj === options.target)return;

if(options.target.isContainedWithinObject )|| options.target.intersectsWithObject(obj)|| obj.isContainedWithinObject(options.target)){

var intersectLeft = null,
intersectTop = null,
intersectWidth = null,
intersectHeight = null,
intersectSize = null,
targetLeft = options.target.getLeft(),
targetRight = targetLeft + options.target.getWidth $ b targetTop = options.target.getTop(),
targetBottom = targetTop + options.target.getHeight(),
objectLeft = obj.getLeft(),
objectRight = objectLeft + obj。 getWidth(),
objectTop = obj.getTop(),
objectBottom = objectTop + obj.getHeight();

//查找X轴的交叉信息
if(targetLeft> = objectLeft&&& targetLeft< = objectRight){
intersectLeft = targetLeft;
intersectWidth = obj.getWidth() - (intersectLeft - objectLeft);

} else if(objectLeft> = targetLeft&& objectLeft< = targetRight){
intersectLeft = objectLeft;
intersectWidth = options.target.getWidth() - (intersectLeft - targetLeft);
}

//查找Y轴的交叉信息
if(targetTop> = objectTop&&& targetTop< = objectBottom){
intersectTop = targetTop ;
intersectHeight = obj.getHeight() - (intersectTop - objectTop);

} else if(objectTop> = targetTop&& objectTop< = targetBottom){
intersectTop = objectTop;
intersectHeight = options.target.getHeight() - (intersectTop - targetTop);
}

//找到相交尺寸(如果对象接触但不重叠,则为0)
if(intersectWidth> 0&& intersectHeight> 0) {
intersectSize = intersectWidth * intersectHeight;
}

//设置外部捕捉区域
if(obj.getLeft()< outerAreaLeft || outerAreaLeft == null){
outerAreaLeft = obj.getLeft ();
}

if(obj.getTop()< outerAreaTop || outerAreaTop == null){
outerAreaTop = obj.getTop();
}

if((obj.getLeft()+ obj.getWidth())> outerAreaRight || outerAreaRight == null){
outerAreaRight = obj.getLeft + obj.getWidth();
}

if((obj.getTop()+ obj.getHeight())> outerAreaBottom || outerAreaBottom = null){
outerAreaBottom = obj.getTop + obj.getHeight();
}

//如果对象相交,重新定位所有接触
的形状之外if(intersectSize){
var distX =(outerAreaRight / 2) - options.target.getLeft()+ options.target.getWidth())/ 2);
var distY =(outerAreaBottom / 2) - ((options.target.getTop()+ options.target.getHeight())/ 2);

//设置新位置
findNewPos(distX,distY,options.target,obj);
}
}
});
});


My goal is to prevent overlapping of two or more rectangles inside my FabricJS canvas.

Imagine two rectangles having info on position and size and you can drag and drop any rectangle inside the canvas.

If rectangle A gets close enough to rectangle B, the position of rectangle A should snap to the edge of rectangle B. This should work for any edge of rectangle B. The vertices do not have to match, cause the sizes of the rectangles are variable.

I have a working example for this snapping on one dimension (x-axes).

My best jsfiddle attempt

See jsfiddle.

But I need it to work around the rectangle on both dimensions. I am quite sure, that my code is not well enough to manage this.

Code-snippets which might help:

object.oCoords.tl.x //top-left corner x position. similar goes for top-right (tr), bottom-left (bl), bottom-right (br) and .y for y-position
mouse_pos = canvas.getPointer(e.e);
mouse_pos.x //pointer x.position
mouse_pos.y //pointer y.position
object.intersectsWithObject(targ) // object = dragged rectangle, targ = targeted rectangle

The snapping should work for an unlimited amount of objects (not only for two rectangles).

解决方案

This is based on gco's answer, updated to work with FabricJS 1.5.0, with the following improvements:

  • Shapes don't overlap.
  • Snapping is more responsive.
  • Shapes are contained within the canvas.

JS Fiddle: https://jsfiddle.net/aphillips8/31qbr0vn/1/

var canvas = new fabric.Canvas('canvas'),
canvasWidth = document.getElementById('canvas').width,
canvasHeight = document.getElementById('canvas').height,
counter = 0,
rectLeft = 0,
snap = 20; //Pixels to snap

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

function plusrect(top, left, width, height, fill) {
    var rect = new fabric.Rect({
        top: 300,
        name: 'rectangle ' + counter,
        left: 0 + rectLeft,
        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,
        maxWidth: canvasWidth,
        maxHeight: canvasHeight
    });

    rect.custom = {};
    rect.custom.counter = counter;

    canvas.add(rect);
    counter++;
    rectLeft += 200;
}

function findNewPos(distX, distY, target, obj) {
    // See whether to focus on X or Y axis
    if(Math.abs(distX) > Math.abs(distY)) {
        if (distX > 0) {
            target.setLeft(obj.getLeft() - target.getWidth());
        } else {
            target.setLeft(obj.getLeft() + obj.getWidth());
        }
    } else {
        if (distY > 0) {
            target.setTop(obj.getTop() - target.getHeight());
        } else {
            target.setTop(obj.getTop() + obj.getHeight());
        }
    }
}

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

    // Don't allow objects off the canvas
    if(options.target.getLeft() < snap) {
        options.target.setLeft(0);
    }

    if(options.target.getTop() < snap) {
        options.target.setTop(0);
    }

    if((options.target.getWidth() + options.target.getLeft()) > (canvasWidth - snap)) {
        options.target.setLeft(canvasWidth - options.target.getWidth());
    }

    if((options.target.getHeight() + options.target.getTop()) > (canvasHeight - snap)) {
        options.target.setTop(canvasHeight - options.target.getHeight());
    }

    // Loop through objects
    canvas.forEachObject(function (obj) {
        if (obj === options.target) return;

        // If objects intersect
        if (options.target.isContainedWithinObject(obj) || options.target.intersectsWithObject(obj) || obj.isContainedWithinObject(options.target)) {

            var distX = ((obj.getLeft() + obj.getWidth()) / 2) - ((options.target.getLeft() + options.target.getWidth()) / 2);
            var distY = ((obj.getTop() + obj.getHeight()) / 2) - ((options.target.getTop() + options.target.getHeight()) / 2);

            // Set new position
            findNewPos(distX, distY, options.target, obj);
        }

        // Snap objects to each other horizontally

        // If bottom points are on same Y axis
        if(Math.abs((options.target.getTop() + options.target.getHeight()) - (obj.getTop() + obj.getHeight())) < snap) {
            // Snap target BL to object BR
            if(Math.abs(options.target.getLeft() - (obj.getLeft() + obj.getWidth())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight() - options.target.getHeight());
            }

            // Snap target BR to object BL
            if(Math.abs((options.target.getLeft() + options.target.getWidth()) - obj.getLeft()) < snap) {
                options.target.setLeft(obj.getLeft() - options.target.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight() - options.target.getHeight());
            }
        }

        // If top points are on same Y axis
        if(Math.abs(options.target.getTop() - obj.getTop()) < snap) {
            // Snap target TL to object TR
            if(Math.abs(options.target.getLeft() - (obj.getLeft() + obj.getWidth())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth());
                options.target.setTop(obj.getTop());
            }

            // Snap target TR to object TL
            if(Math.abs((options.target.getLeft() + options.target.getWidth()) - obj.getLeft()) < snap) {
                options.target.setLeft(obj.getLeft() - options.target.getWidth());
                options.target.setTop(obj.getTop());
            }
        }

        // Snap objects to each other vertically

        // If right points are on same X axis
        if(Math.abs((options.target.getLeft() + options.target.getWidth()) - (obj.getLeft() + obj.getWidth())) < snap) {
            // Snap target TR to object BR
            if(Math.abs(options.target.getTop() - (obj.getTop() + obj.getHeight())) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth() - options.target.getWidth());
                options.target.setTop(obj.getTop() + obj.getHeight());
            }

            // Snap target BR to object TR
            if(Math.abs((options.target.getTop() + options.target.getHeight()) - obj.getTop()) < snap) {
                options.target.setLeft(obj.getLeft() + obj.getWidth() - options.target.getWidth());
                options.target.setTop(obj.getTop() - options.target.getHeight());
            }
        }

        // If left points are on same X axis
        if(Math.abs(options.target.getLeft() - obj.getLeft()) < snap) {
            // Snap target TL to object BL
            if(Math.abs(options.target.getTop() - (obj.getTop() + obj.getHeight())) < snap) {
                options.target.setLeft(obj.getLeft());
                options.target.setTop(obj.getTop() + obj.getHeight());
            }

            // Snap target BL to object TL
            if(Math.abs((options.target.getTop() + options.target.getHeight()) - obj.getTop()) < snap) {
                options.target.setLeft(obj.getLeft());
                options.target.setTop(obj.getTop() - options.target.getHeight());
            }
        }
    });

    options.target.setCoords();

    // If objects still overlap

    var outerAreaLeft = null,
    outerAreaTop = null,
    outerAreaRight = null,
    outerAreaBottom = null;

    canvas.forEachObject(function (obj) {
        if (obj === options.target) return;

        if (options.target.isContainedWithinObject(obj) || options.target.intersectsWithObject(obj) || obj.isContainedWithinObject(options.target)) {

            var intersectLeft = null,
            intersectTop = null,
            intersectWidth = null,
            intersectHeight = null,
            intersectSize = null,
            targetLeft = options.target.getLeft(),
            targetRight = targetLeft + options.target.getWidth(),
            targetTop = options.target.getTop(),
            targetBottom = targetTop + options.target.getHeight(),
            objectLeft = obj.getLeft(),
            objectRight = objectLeft + obj.getWidth(),
            objectTop = obj.getTop(),
            objectBottom = objectTop + obj.getHeight();

            // Find intersect information for X axis
            if(targetLeft >= objectLeft && targetLeft <= objectRight) {
                intersectLeft = targetLeft;
                intersectWidth = obj.getWidth() - (intersectLeft - objectLeft);

            } else if(objectLeft >= targetLeft && objectLeft <= targetRight) {
                intersectLeft = objectLeft;
                intersectWidth = options.target.getWidth() - (intersectLeft - targetLeft);
            }

            // Find intersect information for Y axis
            if(targetTop >= objectTop && targetTop <= objectBottom) {
                intersectTop = targetTop;
                intersectHeight = obj.getHeight() - (intersectTop - objectTop);

            } else if(objectTop >= targetTop && objectTop <= targetBottom) {
                intersectTop = objectTop;
                intersectHeight = options.target.getHeight() - (intersectTop - targetTop);
            }

            // Find intersect size (this will be 0 if objects are touching but not overlapping)
            if(intersectWidth > 0 && intersectHeight > 0) {
                intersectSize = intersectWidth * intersectHeight;
            }

            // Set outer snapping area
            if(obj.getLeft() < outerAreaLeft || outerAreaLeft == null) {
                outerAreaLeft = obj.getLeft();
            }

            if(obj.getTop() < outerAreaTop || outerAreaTop == null) {
                outerAreaTop = obj.getTop();
            }

            if((obj.getLeft() + obj.getWidth()) > outerAreaRight || outerAreaRight == null) {
                outerAreaRight = obj.getLeft() + obj.getWidth();
            }

            if((obj.getTop() + obj.getHeight()) > outerAreaBottom || outerAreaBottom == null) {
                outerAreaBottom = obj.getTop() + obj.getHeight();
            }

            // If objects are intersecting, reposition outside all shapes which touch
            if(intersectSize) {
                var distX = (outerAreaRight / 2) - ((options.target.getLeft() + options.target.getWidth()) / 2);
                var distY = (outerAreaBottom / 2) - ((options.target.getTop() + options.target.getHeight()) / 2);

                // Set new position
                findNewPos(distX, distY, options.target, obj);
            }
        }
    });
});

这篇关于将对象的边缘彼此对齐,并防止重叠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆