将对象的边缘彼此对齐,并防止重叠 [英] Snap edges of objects to each other and prevent overlap
问题描述
我的目标是防止在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屋!