Canvas 防止画布对象的碰撞或相交
我正在画布上画n个矩形。矩形是可拖动和可伸缩的。 我想防止它们重叠或交叉。最好的情况是,如果它们只是相互咬合 我想检查一下十字路口。在我的示例中,我将触摸对象的不透明度设置为0.1 巧合的是,在我试图解决这个问题时,当我的物体接触到另一个物体时,它们不能被释放。看见 这是因为第91行没有执行警报。alertmath.absdistx 实际上这是一种解决方案,但绝对不是一种优雅的解决方案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
有什么想法吗?我想出了防止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);
}
}
});
});