检测可拖动的捕捉器与其捕捉到的元素的相对位置 [英] Detecting Draggable snapper relative position to the element it snaps to

查看:62
本文介绍了检测可拖动的捕捉器与其捕捉到的元素的相对位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标

  1. 根据位置将元素对齐或拒绝对齐
  2. 根据捕捉位置更改元素外观

已咨询过来源

jQuery UI –可拖动的快照"事件

检索来使用启用了捕捉功能的jQuery UI拖动元素

jQuery UI可拖动选项快照容限

可视化 想象一下带有连接器的div-如果将其连接到左瓶,则需要将连接器指向右,如果将其连接到右瓶,则需要将连接器指向左.

Visualisation Imagine a div with a connector - if I connect it to the left bottle, I need the connector to point right, if to the right bottle, the connector needs to point left.

这种连接器无法卡扣到瓶子的顶部或底部,因此在这种情况下需要拒绝顶部或底部的卡扣-其他情况可能仅是垂直卡扣,而其他情况可能又是垂直或水平卡扣.最可能是水平或垂直

Such a connector cannot be snapped to the top or bottom of the bottle so a top or bottom snap needs to be rejected in this case - Other cases may be vertical snapping only and again others may snap on vertical or horizontal. The most likely is either horizontal OR vertical

示例代码 演示

snapped: function(event, ui) {
    var snapper  = ui.snapElement.attr("id"),snapperPos = ui.snapElement.position(),
        snappee  = ui.helper.attr("id"),     snappeePos = ui.helper.position(),
        snapping = ui.snapping,
        collisionTolerance = 3;

    if (Math.abs(snapperPos.left-snappeePos.left)<=collisionTolerance) {
        if (snapperPos.top > snappeePos.top) ui.helper.html(showPos(snapper,"top",snapperPos,snappeePos));
        else ui.helper.html(showPos(snapper,"bottom",snapperPos,snappeePos));
    }
    else if (Math.abs(snapperPos.top-snappeePos.top)<=collisionTolerance) {
        if (snapperPos.left > snappeePos.left) ui.helper.html(showPos(snapper,"left",snapperPos,snappeePos));
        else ui.helper.html(showPos(snapper,"right",snapperPos,snappeePos));
    }
    else ui.helper.html(showPos(snapper,"corner",snapperPos,snappeePos));
},

问题

  1. 我如何可靠地检测到我确实已经精确地捕捉到了正确的左侧,右侧,顶部或底部? -明白我的意思了,抓住一个偏见,把盒子都抓住,但脚本说出角落
  2. 如何拒绝捕捉/标记不可捕捉的位置?

我需要更改拖动的div中的图像以使其在接近捕捉位置时指向正确的方向,因此,如果您对显示该数学的数学建议有所帮助,那将非常好.如您所见,这3个像素不够用-我正在考虑所拖动元素的宽度或高度的一半.数学很重要.

I need to change the image in the dragged div to point in the correct direction when it gets near a snap position so it would be great if you had a suggestion to the math that would show this. As you can see, the 3 pixels where not enough - I am considering half the width or height of the dragged element. The math is important.

推荐答案

更新:鉴于以下评论,这要求的解决方案比我原始答案中的解决方案更通用.

Update: In light of the comments below, this calls for a more generic solution than the one in my original answer.

如注释中所述,如果捕捉的元素保持不变,即使拖动辅助元素的边缘发生更改(在我的原始实现中设计),拖动时也不会再次触发snapped事件.

As discussed in the comments, the snapped event is not triggered again on drag if the snapped element remains the same, even when the edge the helper element is snapped to changes (that was by design in my original implementation).

要克服此限制,必须在drag处理程序中执行边缘检测,并且如果边缘发生变化,则必须触发其他snapped事件.

To overcome this limitation, edge detection has to be performed in the drag handler, and additional snapped events have to be triggered if the edge changes.

我们可以从实用函数开始,该实用函数计算一个元素与另一个元素的相对位置.该函数返回人类可读的字符串,例如"top left""bottom right".在下面的代码中,我选择使用元素的宽度和高度的一半作为拐角的公差,如果它不太适合可拖动元素的形状,则当然可以对其进行修改:

We can start with a utility function that computes the relative position of an element to another. The function returns human-readable strings like "top left" or "bottom right". I chose to use half the element's width and height as the tolerance for corners in the code below, this can of course be modified if it is not well-suited to the shape of your draggable elements:

function computeRelativePosition($element, $reference)
{
    var result = [],
        vtol = $element.outerHeight() / 2,
        htol = $element.outerWidth() / 2,
        pos = $element.position(),
        bounds = $reference.position();
    $.extend(pos, {
        bottom: pos.top + $element.outerHeight() - 1,
        right: pos.left + $element.outerWidth() - 1
    });
    $.extend(bounds, {
        bottom: bounds.top + $reference.outerHeight() - 1,
        right: bounds.left + $reference.outerWidth() - 1
    });

    if (pos.top + vtol <= bounds.top) {
        result.push("top");
    } else if (pos.bottom - vtol >= bounds.bottom) {
        result.push("bottom");
    }

    if (pos.left + htol <= bounds.left) {
        result.push("left");
    } else if (pos.right - htol >= bounds.right) {
        result.push("right");
    }

    return result.join(" ");
}

从那里开始,我们必须在drag处理程序中使用该函数,并且如果自上次处理程序运行以来助手与捕捉的元素的相对位置发生了变化,则触发snapped事件.

From there, we have to use that function in our drag handler and trigger a snapped event if the relative position of the helper to the snapped element has changed since the last time the handler ran:

drag: function(event, ui) {
    var draggable = $(this).data("draggable"); // "ui-draggable" with jQuery UI 1.10+
    $.each(draggable.snapElements, function(index, element) {
        ui = $.extend({}, ui, {
            snapElement: $(element.item),
            snapping: element.snapping
        });
        if (element.snapping) {
            var at = computeRelativePosition(ui.helper, ui.snapElement);
            if (!element.snappingKnown || at != element.at) {
                element.snappingKnown = true;
                element.at = ui.at = at;
                draggable._trigger("snapped", event, ui);
            }
        } else if (element.snappingKnown) {
            element.snappingKnown = false;
            draggable._trigger("snapped", event, ui);
        }
    });
} 

最后,我们可以将您的snapped处理程序更新为:

And finally, we can update your snapped handler into:

snapped: function(event, ui) {
    var snapper = ui.snapElement.attr("id"),
        snapperPos = ui.snapElement.position(),
        snappee = ui.helper.attr("id"),
        snappeePos = ui.helper.position(),
        snapping = ui.snapping;
    if (snapping) {
        ui.helper.html(showPos(snapper, ui.at, snapperPos, snappeePos));
    }
}

此解决方案在我的测试中始终提供准确的结果.您可以在此处找到更新的小提琴.

This solution gives consistently accurate results in my tests. You will find an updated fiddle here.

原始答案如下:

似乎您在计算中没有考虑辅助元素的宽度和高度.边缘检测代码应类似于:

It looks like you are not taking the helper element's width and height into account in your computations. The edge detection code should look like:

if (Math.abs(snapperPos.left - snappeePos.left) <= collisionTolerance) {
    if (snapperPos.top + ui.snapElement.outerHeight() > snappeePos.top) {
        ui.helper.html(showPos(snapper, "top", snapperPos, snappeePos));
    } else {
        ui.helper.html(showPos(snapper, "bottom", snapperPos, snappeePos));
    }
} else if (Math.abs(snapperPos.top - snappeePos.top) <= collisionTolerance) {
    if (snapperPos.left + ui.snapElement.outerWidth() > snappeePos.left) {
        ui.helper.html(showPos(snapper, "left", snapperPos, snappeePos));
    } else {
        ui.helper.html(showPos(snapper, "right", snapperPos, snappeePos));
    }
} else {
    ui.helper.html(showPos(snapper, "corner", snapperPos, snappeePos));
}

如果collisionTolerance设置为50并且仅在snappingtrue的情况下运行,上述代码为我提供了更好的结果.您可以在我根据您的示例创建的此小提琴中对其进行测试.

The code above gives me better results, provided collisionTolerance is set to 50 and the code only runs if snapping is true. You can test it in this fiddle I created from your example.

也就是说,我认为您的问题可以大大简化.由于只对水平边缘检测感兴趣,因此只需要比较辅助对象的左侧位置和捕捉的元素的左侧位置即可.如果前者小于后者,则助手将向左对齐.否则,它会捕捉到右侧.

That said, I think your problem can be simplified quite a bit. Since you are only interested in horizontal edge detection, you only have to compare the left position of the helper with the left position of the snapped element. If the former is less than the latter, the helper is snapped to the left. Otherwise it is snapped to the right.

我修改了对原始问题的答案中的小提琴以实现此目的(因为我比您的示例更熟悉它).结果是:

I modified the fiddle in my answer to the original question to implement this (because I am more familiar with it than with your example). The result is:

snapped: function(event, ui) {
    ui.snapElement.toggleClass("ui-state-highlight ui-state-default");
    var status = ui.snapElement.find(".status");
    if (ui.snapping) {
        if (ui.helper.position().left < ui.snapElement.position().left) {
            status.text("left");
        } else {
            status.text("right");
        }
    } else {
        status.text("");
    }
}

您可以在此处进行测试.

关于您的第二个问题,恐怕可拖动小部件尚不支持部分(仅水平)对齐(尚未).但是,也许您可​​以忽略垂直捕捉,并根据其水平位置不断切换帮助程序的图像.除了当辅助程序接近元素的顶部和底部边缘时发生的跳转"之外,代码仍然可以正常工作.

Regarding your second question, I'm afraid the draggable widget does not support partial (horizontal only) snapping (yet). However, maybe you can just ignore vertical snapping and keep switching the helper's image depending on its horizontal position. Apart from the "jump" that occurs when helper gets close to the top and bottom edges of an element, the code should still work fine.

这篇关于检测可拖动的捕捉器与其捕捉到的元素的相对位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆