jQuery UI - 可拖动的“吸附”事件

12

我正在寻找绑定 snap 事件的方法。

当我将一个元素拖动到我的表面上并且可拖动元素被吸附到一个声明的吸附位置时,我想要触发一个事件。

类似于这样:

$(".drag").draggable({
  snap: ".grid",
  snaped: function( event, ui ) {}
});

附加分:需要引用 .grid 元素,该元素是 draggable 元素被 snapped 到的位置。

2个回答

27
draggable小部件默认情况下不会暴露这样的事件。您可以修改它并维护您的自定义版本,或者更好的是从中派生一个新小部件,并在那里实现新事件。然而,还有第三种方法。
此问题中,我们知道该小部件在其snapElements属性中存储了一组可能“可捕捉”的元素。反过来,此数组中的每个元素都公开了一个snapping属性,如果可拖动的帮助器当前被卡住到该元素,则该属性为true,否则为false(helper可以同时吸附到多个元素)。 snapElements数组会在每个drag事件中更新,因此在drag处理程序中始终是最新的。从那里,我们只需要从相关元素中使用data()获取draggable小部件实例,并调用其_trigger()方法来触发我们自己的snapped事件(实际上是底层的dragsnapped)。顺便说一句,我们可以使用$.extend()ui对象与包装被卡住的元素的jQuery对象扩展。
$(".drag").draggable({
    drag: function(event, ui) {
        var draggable = $(this).data("draggable");
        $.each(draggable.snapElements, function(index, element) {
            if (element.snapping) {
                draggable._trigger("snapped", event, $.extend({}, ui, {
                    snapElement: $(element.item)
                }));
            }
        });
    },
    snap: ".grid",
    snapped: function(event, ui) {
        // Do something with 'ui.snapElement'...
    }
});
上面的代码仍然可以改进。目前为止,只要可拖动助手仍然吸附在一个元素上,每次发生拖动事件(这种情况非常频繁),都会触发一次“snapped”事件。此外,当吸附结束时不会触发任何事件,这不是很实用,并且与事件成对出现(“snapped-in”,“snapped-out”)的约定相违背。
幸运的是,“snapElements”数组是持久的,因此我们可以使用它来存储状态。我们可以为每个数组元素添加一个“snappingKnown”属性,以便跟踪我们已经为该元素触发了“snapped”事件。此外,我们可以使用它来检测自上次调用以来是否已经将元素吸附出去并做出相应反应。
请注意,以下代码选择通过在“ui”对象中传递附加的“snapping”属性(反映元素的当前状态)而不是引入另一个“snapped-out”事件(当然,这只是一种偏好)。
$(".drag").draggable({
    drag: function(event, ui) {
        var draggable = $(this).data("draggable");
        $.each(draggable.snapElements, function(index, element) {
            ui = $.extend({}, ui, {
                snapElement: $(element.item),
                snapping: element.snapping
            });
            if (element.snapping) {
                if (!element.snappingKnown) {
                    element.snappingKnown = true;
                    draggable._trigger("snapped", event, ui);
                }
            } else if (element.snappingKnown) {
                element.snappingKnown = false;
                draggable._trigger("snapped", event, ui);
            }
        });
    },
    snap: ".grid",
    snapped: function(event, ui) {
        // Do something with 'ui.snapElement' and 'ui.snapping'...
        var snapper  = ui.snapElement.attr("id"),snapperPos = ui.snapElement.position(),
            snappee  = ui.helper.attr("id"),     snappeePos = ui.helper.position(),
            snapping = ui.snapping;
        // ...
    }
});

你可以在这里测试这个解决方案。

总之,另一个改进可能是使snapped事件可以被取消,就像drag事件一样。为了实现这一点,如果_trigger()的某个调用返回false,我们必须从drag处理程序中返回false。不过,在一般情况下,取消捕捉或退出捕捉拖动操作并不像一个非常用户友好的功能,你可能需要三思而后行。

更新:从jQuery UI 1.9版本开始,data()键变成小部件的完全限定名称,其中点号被破折号代替。因此,上面用于获取小部件实例的代码变为:

var draggable = $(this).data("ui-draggable");

改为:

var draggable = $(this).data("draggable");

在1.9版本中仍支持使用未经修饰的名称,但已弃用,并且在1.10版中将不再支持。


你能否非常友好地更新你的代码,加上snapping和snapped元素的id - 我使用1.9.1版本,但需要找出snapping相对于snapped的位置 - 如果它在上方、下方、左侧或右侧,内容将不同 - 但现在我只找到了snapping对象及其position()。 - mplungjan
如果我正确理解了您的评论,那么这些信息已经存在。 "snapping"元素在ui.helper中可用,而“snapped to”元素是ui.snapElement,当ui.snappingtrue时。 - Frédéric Hamidi
2
应该注意的是,在新版本中"$(this).data("draggable")"已被重命名为$(this).data("ui-draggable")。 - Andrew
@Andrew,你是否完整地阅读了我的回答,包括结尾处的更新内容? - Frédéric Hamidi
有没有办法在网格捕捉(即,draggable({grid: [20,20]}))的情况下使用这个解决方案?我尝试过实现它,但在这种情况下没有事件被触发。 - daviestar
显示剩余2条评论

1
在jquery-ui 1.10.0中,上述代码无效。拖动函数应该是:
drag: function(event, ui) {
  var draggable = $(this).data("ui-draggable")
  $.each(draggable.snapElements, function(index, element) {
    if(element.snapping) {
      draggable._trigger("snapped", event, $.extend({}, ui, {
        snapElement: $(element.item)
      }));
    }
  });
}

2
我知道我的回答有点长,但你尝试完整地阅读了它吗? :) - Frédéric Hamidi
区别在于$(this).data('draggable')在1.10+中不存在。你必须使用'ui-draggable'代替。他真正应该做的是不仅仅发布整个代码集,而是直接说出来。 - Floss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接