JQuery 可拖动 - 防止网格对象进入同一位置

JQuery Draggable - Prevent grid objects from going in the same position

我正在使用 JQuery Draggable 在网格中移动项目。对象捕捉到 32x32 网格区域。如果对象处于同一位置,我希望能够取消网格捕捉。

拖动不能取消,只能阻止它进入正方形。在它被阻止并移回之前的位置后,如果用户继续拖动到一个新的未占用的网格位置,它必须捕捉到那个位置。

我创建了一个用于上述目的的演示,但是 图像在尝试进入新位置时出现故障,但随后被取消回到旧位置。

https://jsfiddle.net/dtx7my4e/1/

这是fiddle中的代码:

HTML:

   <div class="drop-target">
        <div class="drag-item" object-id="0"></div>
        <div class="drag-item" style="left: 32px;" object-id="1"></div>
    </div>

Javascript:

var objects = [
    [0, 0],
    [1, 1]
];

$(function() {
    $(".drag-item").draggable({
        grid: [ 32, 32 ],
        containment: '.drop-target',
        drag: function (event, obj){
            let objectId = $(this).attr('object-id');

            var objectPositionX = $(this).position().left / 32;
            var objectPositionY = $(this).position().top / 32;

            var previousPositionX = Math.floor(objects[objectId][0]);
            var previousPositionY = Math.floor(objects[objectId][1]);

            if (objectPositionX != previousPositionX || objectPositionY != previousPositionY) {
                if(!isObjectInPosition(objects, [objectPositionX, objectPositionY])) {
                    objects[objectId] = [objectPositionX, objectPositionY];
                } else {
                    obj.position.left = previousPositionX * 32;
                    obj.position.top = previousPositionY * 32;
                }
            }
        }
    });
});


function isObjectInPosition(arrayToSearch, positionToFind)
{
    for (let i = 0; i < arrayToSearch.length; i++) {
        if (arrayToSearch[i][0] == positionToFind[0]
                && arrayToSearch[i][1] == positionToFind[1]) {
            return true;
        }
    }
    return false;
}

CSS:

.drag-item {
    background-image: url("http://i.imgur.com/lBIWrWw.png");
    background-size: 32px auto;
    width: 32px;
    height: 32px;
    cursor: move;
}

.drop-target {
    background: whitesmoke url("http://i.imgur.com/uUvTRLx.png") repeat scroll 0 0 / 32px 32px;
    border: 1px dashed orange;
    height: 736px;
    left: 0;
    position: absolute;
    top: 0;
    width: 736px;
}

谢谢,非常感谢您的帮助。

托比。

如果您愿意修改 draggable 本身,我认为这会使逻辑更易于应用。一旦拖动事件被触发,你可以做很多事情,但如果你修改 draggable 的 _generatePosition 方法,你将有更多的控制权。一开始它可能看起来更复杂,但对于这种行为,有时更容易工作。

基本上,在应用网格和包含检查后,您可以 运行 您的 isInPosition 函数。通常下一步是设置新位置,但如果您的 isInPosition returns 为真,则可以防止拖动。像这样:

'use strict'
// This is the function generating the position by calculating
// mouse position, different offsets and option.

$.ui.draggable.prototype._generatePosition = function(event, constrainPosition) {
  var containment, co, top, left,
    o = this.options,
    scrollIsRootNode = this._isRootNode(this.scrollParent[0]),
    pageX = event.pageX,
    pageY = event.pageY;

  // Cache the scroll
  if (!scrollIsRootNode || !this.offset.scroll) {
    this.offset.scroll = {
      top: this.scrollParent.scrollTop(),
      left: this.scrollParent.scrollLeft()
    };
  }

  /*
   * - Position constraining -
   * Constrain the position to a mix of grid, containment.
   */

  // If we are not dragging yet, we won't check for options
  if (constrainPosition) {

    if (this.containment) {
      if (this.relativeContainer) {
        co = this.relativeContainer.offset();
        containment = [
          this.containment[0] + co.left,
          this.containment[1] + co.top,
          this.containment[2] + co.left,
          this.containment[3] + co.top
        ];
      } else {
        containment = this.containment;
      }

      if (event.pageX - this.offset.click.left < containment[0]) {
        pageX = containment[0] + this.offset.click.left;
      }
      if (event.pageY - this.offset.click.top < containment[1]) {
        pageY = containment[1] + this.offset.click.top;
      }
      if (event.pageX - this.offset.click.left > containment[2]) {
        pageX = containment[2] + this.offset.click.left;
      }
      if (event.pageY - this.offset.click.top > containment[3]) {
        pageY = containment[3] + this.offset.click.top;
      }
    }

    if (o.grid) {

      //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
      top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
      pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;

      left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
      pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
    }

    if (o.axis === "y") {
      pageX = this.originalPageX;
    }

    if (o.axis === "x") {
      pageY = this.originalPageY;
    }
  }

// This is the only part added to the original function.
// You have access to the updated position after it's been
// updated through containment and grid, but before the
// element is modified.
// If there's an object in position, you prevent dragging.

  if (isObjectInPosition(objects, [pageX - this.offset.click.left - this.offset.parent.left, pageY - this.offset.click.top - this.offset.parent.top])) {
    return false;

  }

  return {
    top: (
      pageY - // The absolute mouse position
      this.offset.click.top - // Click offset (relative to the element)
      this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
      this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
      (this.cssPosition === "fixed" ? -this.offset.scroll.top : (scrollIsRootNode ? 0 : this.offset.scroll.top))
    ),
    left: (
      pageX - // The absolute mouse position
      this.offset.click.left - // Click offset (relative to the element)
      this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
      this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
      (this.cssPosition === "fixed" ? -this.offset.scroll.left : (scrollIsRootNode ? 0 : this.offset.scroll.left))
    )
  };

}

var objects = [
  [0, 0],
  [1, 1]
];

$(function() {
  $(".drag-item").draggable({
    grid: [32, 32],
    containment: '.drop-target',
    // on start you remove coordinate of dragged item
    // else it'll check its own coordinates
    start: function(event, obj) {
      var objectId = $(this).attr('object-id');
      objects[objectId] = [null, null];
    },
    // on stop you update your array
    stop: function(event, obj) {
      var objectId = $(this).attr('object-id');
      var objectPositionX = $(this).position().left / 32;
      var objectPositionY = $(this).position().top / 32;
      objects[objectId] = [objectPositionX, objectPositionY];

    }
  });
});


function isObjectInPosition(arrayToSearch, positionToFind) {

  for (let i = 0; i < arrayToSearch.length; i++) {
    if (arrayToSearch[i][0] === (positionToFind[0] / 32) && arrayToSearch[i][1] === (positionToFind[1] / 32)) {

      return true;
    }
  }
  return false;
}

https://jsfiddle.net/bfc4tsrh/1/