我在画布上画了n个矩形。矩形是可拖动和可伸缩的。 我想阻止它们重叠或相交。最好的情况是,如果他们只是互相攻击。
我想出去检查路口。在我的例子中,我将触摸对象的不透明度设置为0.1。巧合的是,在我尝试解决这个问题时,我的物体在碰到另一个物体时无法释放。见http://jsfiddle.net/gcollect/jZw7P/
这是因为第91行没有执行警报。 alert(math.abs(distx));
实际上它是一种解决方案,但绝对不是优雅的解决方案。
有什么想法吗?
答案 0 :(得分:3)
这是基于gco的答案,更新后与FabricJS 1.5.0一起使用,具有以下改进:
JS小提琴: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);
}
}
});
});
答案 1 :(得分:2)
对于那些仍然对解决方案感兴趣的人:我在这里解决了这个问题:https://stackoverflow.com/a/22649022/3207478请参阅jsfiddle:http://jsfiddle.net/gcollect/FD53A/
使用
.oCoords.tl .tr .bl. and .br solved it.
答案 2 :(得分:2)
这些解决方案相当不错,但不使用旋转对象。 我想出了这个。它阻止了物体的交叉。即使旋转了。
我编造了这个小提琴。看看吧。
[Fiddle]
(http://jsfiddle.net/m0jjc23v/9)
说明:
解决方法是你必须保存最后一个非交叉顶部&amp;移动物体时左侧位置。当对象与另一个对象相交或被包含时,只需使用以前保存的top&amp;左侧位置和重新定位对象。有很多可能的改进。随意使用此代码并进行改进。
代码:
//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;
}
}
答案 3 :(得分:0)
我弄明白了如何防止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;
}
见JSFiddle。 实际上它非常酷,并且对象在x轴上相互捕捉。麻烦制造者现在是y轴。从顶部或底部开始的方法会将对象移动到左边缘或右边缘...将在此问题上工作。
答案 4 :(得分:0)
我一直在考虑类似的问题而我+ @kangax的最后一个小提琴。如果有路径冲突检测算法(例如此处找到的多边形交叉代码),我们甚至可以使用这种类型的机制对路径本身进行冲突检测:http://www.kevlindev.com/geometry/2D/intersections/index.htm。
但我不喜欢这个&#39;粘性&#39;解决方案是,对我来说,对象实际上应该沿着表面滑动以用于我的应用程序而不是强迫用户“解开”#39;物体。对于这种效果,我意识到2D物理引擎可能会很好地启用这种类型的功能,http://brm.io/matter-js/的一些示例用法演示了解决问题,定位和旋转肯定应该是能够映射到fabric.js。但是,如果需要固定轮换,看起来成功的可能性会降低。