0

Я рисую n прямоугольников на холсте. Прямоугольники перетаскиваются и масштабируются. Я хочу, чтобы они не перекрывались или пересекались. Лучше всего, если они просто привязываются друг к другу.Предотвращение столкновения или пересечения объектов холста

Я решил проверить перекресток. В моем примере я установил непрозрачность касания объектов до 0,1.

Возможно, в попытке решить эту проблему мои объекты не могут быть выпущены, когда они касаются другого объекта. См. http://jsfiddle.net/gcollect/jZw7P/ Это из-за строки 91, где предупреждение не выполняется. alert(math.abs(distx));

Фактически это своего рода решение, но определенно не изящное.

Любые идеи?

+1

Привет. Ваше предупреждение не выполняется, потому что математическая функция чувствительна к регистру. Пожалуйста, замените оповещение (math.abs (distx)); по предупреждению (Math.abs (distx)); 'M' в верхнем регистре ... – ptCoder

ответ

2

Для тех, кто по-прежнему заинтересованы в решении: Я решил его здесь: https://stackoverflow.com/a/22649022/3207478 Смотрите jsfiddle:

Работа с

.oCoords.tl .tr .bl. and .br solved it. 
+0

Вы могли бы работать с полилиниями, кругом или даже с линиями, а не с прямоугольником? – Miron

0

Я понял, как предотвратить столкновение по оси х. Путем добавления этих строк:

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. Troublemaker теперь является осью y. Подход сверху или снизу переместит объект на левый или правый край ... Будет работать над этой проблемой.

2

эти решения довольно хорошо, но не из них работает с повернутой объекты. Я придумал это. Это предотвращает пересечение объектов. Даже повернуто.

Я составил эту скрипку. Проверьте это.

[Fiddle] (http://jsfiddle.net/m0jjc23v/9)

Пояснение:

Обхода является то, что вы должны сохранить последнюю ноны-intersectiong верхней & левой позиции при перемещении объекта. Когда объект пересекается или имеет другой объект, просто используйте ранее сохраненную верхнюю & левую позицию и объект репозиции. Есть много возможных улучшений. Не стесняйтесь использовать этот код и внести улучшения.

Код:

//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; 
    } 
} 
+1

Не добавляйте ссылки, но ваш код и объяснения об этом. – fedorqui

+0

в любом случае, он работает. Но, пожалуйста, объясните свой код. Мне кажется, немного отстает. – gco

+0

проверить измененный пост. – Frantisek

0

Я созерцая подобную проблему, и я + последнюю скрипку здесь от @kangax. Можно даже использовать этот тип механизма для обнаружения столкновения для самих путей, если у вас есть алгоритм обнаружения столкновений пути, такой как код пересечения многоугольника, найденный здесь: http://www.kevlindev.com/geometry/2D/intersections/index.htm.

Но то, что мне не нравится в этом «липком» решении, заключается в том, что для меня объект должен скользить по поверхности для моего приложения, а не заставлять пользователя «отклеивать» объект.Для такого эффекта я понял, что 2D-физический движок, вероятно, сделает большую работу по включению этого типа функциональности, и некоторые из примеров использования http://brm.io/matter-js/ демонстрируют решение проблемы, и позиционирование и ротация должны, безусловно, иметь возможность отображать в fabric.js. Однако, если требуется фиксированное вращение, похоже, что вероятность успеха будет уменьшена.

3

Это основано на ответ ОПО в, обновленный работать с 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.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); 
      } 
     } 
    }); 
}); 
+0

Спасибо за вашу работу, но не могли бы вы дать мне знать, как обращаться с кругами? – Miron

+0

Не жалко, это звучит сложно. Вероятно, вам придется делать некоторые вычисления с помощью pi. –

Смежные вопросы