una traer superposición superposicion superponer sobre poner otro otra mandar imagen frente fondo enviar encima elementos elemento div colocar capa atras javascript html canvas fabricjs

javascript - traer - Ajustar los bordes de los objetos entre sí y evitar la superposición



superposición de elementos html (3)

Resolví el problema por mi cuenta. Ver jsfiddle: http://jsfiddle.net/gcollect/FD53A/

Este es el código:

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; } });

¡Funciona bastante legítimo! ¡Aclamaciones!

Mi objetivo es evitar la superposición de dos o más rectángulos dentro de mi lienzo FabricJS.

Imagine dos rectángulos con información sobre posición y tamaño, y puede arrastrar y soltar cualquier rectángulo dentro del lienzo.

Si el rectángulo A se acerca lo suficiente al rectángulo B, la posición del rectángulo A debe ajustarse al borde del rectángulo B. Esto debería funcionar para cualquier borde del rectángulo B. Los vértices no tienen que coincidir, porque los tamaños de los rectángulos son variables .

Tengo un ejemplo de trabajo para esta alineación en una dimensión (x-ejes).

Mi mejor intento jsfiddle

Ver jsfiddle .

Pero necesito que trabaje alrededor del rectángulo en ambas dimensiones. Estoy bastante seguro de que mi código no es lo suficientemente bueno para manejar esto.

Code-snippets que pueden ayudar:

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

El ajuste debería funcionar para una cantidad ilimitada de objetos (no solo para dos rectángulos).


Esto se basa en la respuesta de gco, actualizada para trabajar con FabricJS 1.5.0, con las siguientes mejoras:

  • Las formas no se superponen.
  • El ajuste es más receptivo.
  • Las formas están contenidas dentro del lienzo.

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); } } }); });


Basé este violín en los ejemplos de @Anna Phillips y @ gco. Incluye:

  • Corte de esquina
  • Edge snapping
  • Los objetos pueden superponerse
  • Los objetos están completamente contenidos dentro del lienzo
  • Los objetos no pueden tener un tamaño mayor que el área del lienzo

Aquí está el código:

window.canvas = new fabric.Canvas(''fabriccanvas''); window.counter = 0; var newleft = 0, edgedetection = 20, //pixels to snap canvasWidth = document.getElementById(''fabriccanvas'').width, canvasHeight = document.getElementById(''fabriccanvas'').height; canvas.selection = false; plusrect(); plusrect(); plusrect(); function plusrect(top, left, width, height, fill) { window.canvas.add(new fabric.Rect({ top: 300, name: ''rectangle '' + window.counter, left: 0 + newleft, 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, maxHeight: document.getElementById("fabriccanvas").height, maxWidth: document.getElementById("fabriccanvas").width, })); window.counter++; newleft += 200; } this.canvas.on(''object:moving'', function (e) { var obj = e.target; obj.setCoords(); //Sets corner position coordinates based on current angle, width and height if(obj.getLeft() < edgedetection) { obj.setLeft(0); } if(obj.getTop() < edgedetection) { obj.setTop(0); } if((obj.getWidth() + obj.getLeft()) > (canvasWidth - edgedetection)) { obj.setLeft(canvasWidth - obj.getWidth()); } if((obj.getHeight() + obj.getTop()) > (canvasHeight - edgedetection)) { obj.setTop(canvasHeight - obj.getHeight()); } canvas.forEachObject(function (targ) { 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; } }); });

Lo que me gustaría saber es si es posible ampliar esto para agregar las siguientes características:

  • Ajuste dinámico . Continuar arrastrando un objeto después de la instantánea inicial desactivará temporalmente el ajuste hasta que el objeto deje de moverse. Por ejemplo, si arrastro un cuadro al lado de otro, se unirán una vez que estén dentro del alcance. Sin embargo, si sigo moviendo el primer cuadro, puedo "soltarlo" en una posición en la que esté dentro del rango de ajuste, pero no alineado con el otro cuadro.
  • Mostrar líneas de guía cuando el objeto seleccionado está dentro del alcance de otro objeto. Actualmente, agregamos un borde alrededor del objeto de destino, pero sería mejor mostrar las pautas que se extienden hacia afuera (posiblemente hasta el borde del lienzo) para visualizar más fácilmente los límites del objeto de destino.
  • Encajado paralelo . Al mover un objeto que ya está enganchado al objeto objetivo, el objeto seleccionado debe ajustarse al objeto objetivo de tal forma que las partes superiores, inferiores o laterales de ambos objetos sean paralelos. Por ejemplo, supongamos que el cuadrado seleccionado se ajusta a la izquierda del cuadrado objetivo y que la parte superior del cuadrado seleccionado está debajo de la parte superior del cuadrado objetivo. Mover el cuadrado seleccionado hacia arriba debería hacer que su parte superior encaje en alineación con la parte superior del objetivo una vez dentro del alcance. Se debe aplicar la misma lógica al moverlo hacia abajo, o si el objeto seleccionado está por encima / debajo del objetivo y se mueve horizontalmente.