我的目标是防止FabricJS画布中的两个或多个矩形重叠.
想象一下两个矩形,它们具有位置和大小的信息,您可以在画布内拖放任何矩形.
如果矩形A足够接近矩形B,则矩形A的位置应该捕捉到矩形B的边缘.这应该适用于矩形B的任何边缘.顶点不必匹配,因为矩形的大小是可变的.
我有一个在一个维度(x轴)上捕捉的工作示例.
我最好的jsfiddle尝试
见jsfiddle.
但我需要它来处理两个维度上的矩形.我很确定,我的代码不足以管理它.
代码片段可能会有所帮助:
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
捕捉应适用于无限量的对象(不仅适用于两个矩形).
我自己解决了这个问题.见jsfiddle:http://jsfiddle.net/gcollect/FD53A/
这是代码:
this.canvas.on('object:moving', function (e) { var obj = e.target; obj.setCoords(); //Sets corner position coordinates based on current angle, width and height canvas.forEachObject(function (targ) { var objects = this.canvas.getObjects(), i = objects.length; activeObject = canvas.getActiveObject(); if (targ === activeObject) return; if (Math.abs(activeObject.oCoords.tr.x - targ.oCoords.tl.x) < edgedetection) { activeObject.left = targ.left - activeObject.currentWidth; } if (Math.abs(activeObject.oCoords.tl.x - targ.oCoords.tr.x) < edgedetection) { activeObject.left = targ.left + targ.currentWidth; } if (Math.abs(activeObject.oCoords.br.y - targ.oCoords.tr.y) < edgedetection) { activeObject.top = targ.top - activeObject.currentHeight; } if (Math.abs(targ.oCoords.br.y - activeObject.oCoords.tr.y) < edgedetection) { activeObject.top = targ.top + targ.currentHeight; } if (activeObject.intersectsWithObject(targ) && targ.intersectsWithObject(activeObject)) { targ.strokeWidth = 10; targ.stroke = 'red'; } else { targ.strokeWidth = 0; targ.stroke = false; } if (!activeObject.intersectsWithObject(targ)) { activeObject.strokeWidth = 0; activeObject.stroke = false; } });
工作非常合法!干杯!
这是基于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); } } }); });