Canvas 防止画布对象的碰撞或相交

Canvas 防止画布对象的碰撞或相交,canvas,html5-canvas,collision-detection,intersection,fabricjs,Canvas,Html5 Canvas,Collision Detection,Intersection,Fabricjs,我正在画布上画n个矩形。矩形是可拖动和可伸缩的。 我想防止它们重叠或交叉。最好的情况是,如果它们只是相互咬合 我想检查一下十字路口。在我的示例中,我将触摸对象的不透明度设置为0.1 巧合的是,在我试图解决这个问题时,当我的物体接触到另一个物体时,它们不能被释放。看见 这是因为第91行没有执行警报。alertmath.absdistx 实际上这是一种解决方案,但绝对不是一种优雅的解决方案 有什么想法吗?我想出了防止x轴碰撞的办法。通过添加以下行: canvas.forEachObject(func

我正在画布上画n个矩形。矩形是可拖动和可伸缩的。 我想防止它们重叠或交叉。最好的情况是,如果它们只是相互咬合

我想检查一下十字路口。在我的示例中,我将触摸对象的不透明度设置为0.1

巧合的是,在我试图解决这个问题时,当我的物体接触到另一个物体时,它们不能被释放。看见 这是因为第91行没有执行警报。alertmath.absdistx

实际上这是一种解决方案,但绝对不是一种优雅的解决方案


有什么想法吗?

我想出了防止x轴碰撞的办法。通过添加以下行:

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.left + obj.width)/2) - ((options.target.left + options.target.width)/2);
                    var disty = ((obj.top + obj.height)/2) - ((options.target.top + options.target.height)/2);                  

                    if (distx > 0){
                        options.target.left = obj.left - options.target.width;
                    } else {
                        options.target.left = obj.left + obj.width;
                    }
看。
事实上,这很酷,对象在x轴上相互捕捉。麻烦制造者现在是y轴。从顶部或下方接近会将对象移动到左侧或右侧边缘。。。将解决这个问题。

对于那些仍然对解决方案感兴趣的人:我在这里解决了它:请参阅JSFIDLE:

合作

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

这些解决方案相当不错,但它们中没有一个能够处理旋转的对象。 我想到了这个。它可以防止物体相交,甚至旋转

这把小提琴是我编的。看看吧

[小提琴]

说明:

解决方法是在移动对象时必须保存最后一个不相交的左上角位置。当对象与另一个对象相交或相接时,只需使用以前保存的“左上角位置”和“重新定位对象”。有很多可能的改进。请随意使用此代码并进行改进

代码:


我一直在思考一个类似的问题,我+最后一把小提琴来自@kangax。如果有一个路径碰撞检测算法(如此处找到的多边形相交代码),甚至可以使用这种类型的机制对路径本身进行碰撞检测:


但我不喜欢这种“粘性”解决方案,因为对我来说,对象实际上应该沿着应用程序的表面滑动,而不是强迫用户“松开”对象。对于这种效果,我意识到2D物理引擎在启用这种功能方面可能会做得很好,而演示解决问题以及定位和旋转的一些示例应用肯定能够映射到fabric.js。但是,如果需要固定轮换,成功的概率似乎会降低。

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

形状不重叠。 抓拍更灵敏。 图形包含在画布中。 JS小提琴:


你好由于数学函数区分大小写,因此未执行警报。请替换alertmath.absdistx;通过alertMath.absdistx;'M'大写…不要添加链接,但要添加代码和解释。无论如何,它是有效的。但是请解释一下你的代码。这对我来说似乎有点滞后。你能处理多段线、圆,甚至直线,而不是矩形吗?谢谢你的工作,但你能告诉我如何处理圆吗?不,对不起,听起来很棘手。您可能需要使用pi进行一些计算。
//handle moving object
this.canvas.on('object:moving', function(event) {
        var obj = event.target;
        intersectingCheck(obj);
});

function intersectingCheck(activeObject) {
    activeObject.setCoords();
    if(typeof activeObject.refreshLast != 'boolean') {
        activeObject.refreshLast = true
    };

    //loop canvas objects
    activeObject.canvas.forEachObject(function (targ) {
        if (targ === activeObject) return; //bypass self

        //check intersections with every object in canvas
        if (activeObject.intersectsWithObject(targ) 
            || activeObject.isContainedWithinObject(targ) 
            || targ.isContainedWithinObject(activeObject)) {
                //objects are intersecting - deny saving last non-intersection position and break loop
                if(typeof activeObject.lastLeft == 'number') {
                    activeObject.left = activeObject.lastLeft;
                    activeObject.top = activeObject.lastTop;
                    activeObject.refreshLast = false;
                    return;
                }
       }
       else {
           activeObject.refreshLast = true;
       }
   });

   if(activeObject.refreshLast) {
       //save last non-intersecting position if possible
       activeObject.lastLeft = activeObject.left
       activeObject.lastTop = activeObject.top;
   }
}
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);
            }
        }
    });
});