在拖动开始时捕捉到父网格

Snap to parent grid on start of drag

我是 JavaScript 和 JQuery 的新手,如果我遗漏了一两个技巧,请见谅。

我已经弄清楚如何让 JQuery UI 可拖动对象使用网格选项,并且在页面加载后,"snap to an imaginary grid" 所有可拖动对象都有引用到(在代码注释中解释)。但是,我不知道如何让这种行为发生 .on("dragstart")

HTML:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <script type="text/javascript" src="jquery-1.10.2.js"></script>
  <script type="text/javascript" src="jquery-ui.js"></script>
  <script type="text/javascript" src="dragger.js"></script>
</head>

<body>
  <div id="parent">
    <svg width="300" height="100" class="draggable" id="number1">
      <rect x="0" y="0" rx="10" ry="10" width="300" height="100" style="fill:rgb(121,0,121);stroke-width:3;stroke:rgb(0,0,0);">
    </svg>
    <svg width="300" height="100" class="draggable" id="letterA">
      <rect x="0" y="0" rx="100" ry="10" width="300" height="100" style="fill:rgb(0,121,121);stroke-width:3;stroke:rgb(255,0,0);">
    </svg>
  </div>
</body>

</html>

注: 两个矩形之间有一个白色的空隙,通过第二个JavaScript下面,一旦块被捕捉到网格,它就会消失。或者,这 2 个矩形应该可以相互拖动并相互对齐,以被视为捕捉到网格上。

JavaScript (dragger.js):

var roundedRemainder = function(numer, denom) {
  return numer - (Math.round(numer / denom) * denom);
}

var snapPosition = function(obj, granularity) {
  obj.position({
    my: "left top", // Unchanging reference point on draggable object
    at: "left top", // Unchanging reference point on reference object
    of: "#parent",  // The object that you want to move items with respect to.
    using: function(position, data) {
      var newPositions = {
          // Get the difference between the "imaginary grid" and the current grid
          left: function() {
            return roundedRemainder(position.left, granularity);
          },
          top: function() {
            return roundedRemainder(position.top, granularity);
          }
        }
        // Move to the imaginary grid
      $(this).css(newPositions);
      return newPositions;
    }
  });
}

$(function() {
  var gridSize = 50;
  $(".draggable")
    // Enable grid usage
    .draggable({
      grid: [gridSize, gridSize]
    })
    .on("dragstart", function(event, ui) {
      var newPos = snapPosition(ui.helper, gridSize);
    })
});

证明 snapPosition 中的代码有效:

var roundedRemainder = function(numer, denom) {
  return numer - (Math.round(numer / denom) * denom);
}

$(function() {
  var gridSize = 50;
  $(".draggable")
    // Enable grid usage
    .draggable({
      grid: [gridSize, gridSize]
    })
    .position({
      my: "left top", // Unchanging reference point on draggable object
      at: "left top", // Unchanging reference point on reference object
      of: "#parent", // The object that you want to move items with respect to.
      using: function(position, data) {
        var newPositions = {
            // Get the difference between the "imaginary grid" and the current grid
            left: function() {
              return roundedRemainder(position.left, granularity);
            },
            top: function() {
              return roundedRemainder(position.top, granularity);
            }
          }
          // Move to the imaginary grid
        $(this).css(newPositions);
      }
    })
});

第一个 JavaScript 尝试在拖动开始后更改块的位置,以将其捕捉到假想的网格。第二个在加载页面时自动执行此操作,但不会再执行此操作。例如,如果我要将假想网格的粒度从 50 更改为 79,则拖动不会将对象按需要带回网格。

有没有什么地方可以让我学习如何做到这一点?可行吗?

澄清一下:

我已经经历过 Google,但是类似于 "start"、"drag" 和 "position" 的术语不够独特,无法缩小范围,或者我还没有找到合适的地方。我还搜索了 JQuery (UI) 档案。

非常感谢!

好吧,事实证明有一些代表我无知的例子。我将在下面通过它们来帮助其他人,但如果你只是在寻找解决方案,那就不要再看了:

var roundedRemainder = function(numer, denom) {
  if (denom > 1) // Only calculate when there is a chance the draggable isn't on the grid.
    return numer - (Math.round(numer / denom) * denom); // Note: denom = 0 is invalid.
  else
    return 0; // If denom is invalid, or 1, then assume the draggable is on the grid.
}

$(function() {
  var gridSize = 79;
  var _this = this;
  $(".draggable")
    .draggable({
      // Enable grid constraints
      grid: [gridSize, gridSize],

      // At start of dragging (aka, only do once at the beginning)
      // snap the draggable object onto its parents grid.
      drag: function(event, ui) {
        var gridOffsetLeft;
        var gridOffsetTop;
        ui.helper.position({
          my: "left top", // For the top left of the draggable object
          at: "left top", // Link to the top left of the reference object
          of: $(this).parent(), // Make the reference object the parent of the draggable object
          // Calculate the grid offset from where the object ORIGINATED from
          using: function(position, data) {
            gridOffsetLeft = roundedRemainder(position.left, gridSize);
            gridOffsetTop = roundedRemainder(position.top, gridSize);
          }
        });
        // Calculate the total offset based off of the current
        // location of the draggable object, and the previously
        // calculated offset.
        gridOffsetLeft -= roundedRemainder(ui.position.left, gridSize);
        gridOffsetTop -= roundedRemainder(ui.position.top, gridSize);

        // Apply offsets & hence snap the draggable onto the
        // parents grid.
        ui.position.left += gridOffsetLeft;
        ui.position.top += gridOffsetTop;
      }
    })
});
<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8" />
  <script type="text/javascript" src="jquery-1.10.2.js"></script>
  <script type="text/javascript" src="jquery-ui.js"></script>
  <script type="text/javascript" src="dragger.js"></script>
</head>

<body>
  <div id="parent">
    <svg width="300" height="100" class="draggable" id="number1">
      <rect x="0" y="0" rx="10" ry="10" width="300" height="100" style="fill:rgb(121,0,121);stroke-width:3;stroke:rgb(0,0,0);">
    </svg>
    <svg width="300" height="100" class="draggable" id="letterA">
      <rect x="0" y="0" rx="100" ry="10" width="300" height="100" style="fill:rgb(0,121,121);stroke-width:3;stroke:rgb(255,0,0);">
    </svg>
  </div>
</body>

</html>

错误:

  1. 第一个错误是我试图将定位功能与拖动功能分开的方式。具体来说,当试图发送 then 过来时。我还没有真正理解这一点,这就是为什么它不在解决方案中的原因,但是我确实阅读了(并丢失了 link)堆栈溢出,其中提到使用 var _then = then; 或使用绑定方法.如果我找到 link 我会编辑答案。
  2. 下一个错误与我的 "at start" 规格有关。我想要的方法是仅在每次拖动开始时捕捉到父网格。因此,根据文档,使用 start:.on("dragstart", ...) 功能听起来合乎逻辑。这导致 preventDefault 问题,即启动命令被忽略,因此没有捕捉,但我可以拖动,或者如果我在开始开始时使用 event.preventDefault,它会捕捉但不再拖动。原来 drag: 功能仅在拖动开始时 运行s 一次(如果我错了请纠正我)。通过将捕捉功能放回原处,解决了另一个错误。
  3. 最后,我原来捕捉到网格的逻辑是错误的。它只使用一次时有效,但一旦每次拖动对象时它都是 运行,每次拖动都会 "creep" 一点点。这是因为 "snap" 功能从拖动对象的 ORIGINAL 位置到父对象起作用。因此,每次都添加差异,即使对象已经捕捉,也会导致错误。为了解决这个问题,我只是将对象的 original 位置与 current 位置的差异,以及以 为中心的相同网格对象的原始位置。

(要清楚,有3个参考位置,parent,被拖动对象的original位置,以及当前拖动对象的位置)。