2

標的

  1. 要素を一緒にスナップするか、位置に応じてスナップを拒否します
  2. スナップ位置に応じて要素の外観を変更するには

すでに調べた情報源

Jquery UI – ドラッグ可能な「スナップ」イベント

スナップを有効にしてドラッグ可能な jQuery UI を使用して「スナップ先」要素を取得する

jQuery UI ドラッグ可能オプション スナップトレランス

視覚化 コネクタを備えた div を想像してみてください。左のボトルに接続する場合はコネクタを右に向ける必要があり、右のボトルに接続する場合はコネクタを左に向ける必要があります。

このようなコネクタは、ボトルの上部または下部にスナップできないため、この場合、上部または下部のスナップを拒否する必要があります。他のケースでは、垂直スナップのみである場合もあれば、垂直または水平にスナップする場合もあります。最も可能性が高いのは、水平または垂直のいずれかです

ここに画像の説明を入力 ここに画像の説明を入力

サンプルコード DEMO

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 ピクセルでは不十分です。ドラッグした要素の幅または高さの半分を検討しています。数学は重要です。

4

1 に答える 1

1

更新:以下のコメントに照らして、これには私の元の回答よりも一般的な解決策が必要です。

コメントで説明されているようにsnapped、スナップされた要素が同じままである場合、ヘルパー要素がスナップされたエッジが変更された場合でも、ドラッグ時にイベントが再びトリガーされることはありません (これは私の元の実装では設計によるものでした)。

この制限を克服するには、dragハンドラーでエッジ検出を実行snappedする必要があり、エッジが変化した場合は追加のイベントをトリガーする必要があります。

要素と別の要素の相対位置を計算するユーティリティ関数から始めることができます。"top left"この関数は、またはのような人間が読める文字列を返します"bottom right"。以下のコードでは、要素の幅と高さの半分を角の許容範囲として使用することにしました。もちろん、ドラッグ可能な要素の形状に適していない場合は、これを変更できます。

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

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ハンドラーを次のように更新できます。

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));
    }
}

このソリューションは、私のテストで一貫して正確な結果をもたらします。更新されたフィドルはこちらにあります


元の答えは次のとおりです。

計算でヘルパー要素の幅と高さを考慮していないようです。エッジ検出コードは次のようになります。

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いて、コードが の場合にのみ実行される場合、より良い結果をもたらしsnappingますtrue。あなたの例から作成したこのフィドルでテストできます。

そうは言っても、あなたの問題はかなり単純化できると思います。水平方向のエッジ検出のみに関心があるため、ヘルパーの左位置とスナップされた要素の左位置を比較するだけです。前者が後者より小さい場合、ヘルパーは左にスナップされます。それ以外の場合は、右にスナップされます。

これを実装するために、元の質問に対する回答のフィドルを変更しました(あなたの例よりも慣れているため)。結果は次のとおりです。

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("");
    }
}

ここでテストできます。

2 番目の質問については、ドラッグ可能なウィジェットが部分的な (水平のみの) スナップを (まだ) サポートしていないことを残念に思います。ただし、垂直スナップを無視して、水平位置に応じてヘルパーの画像を切り替え続けることもできます。ヘルパーが要素の上端と下端に近づいたときに発生する「ジャンプ」を除けば、コードは正常に動作するはずです。

于 2013-03-24T07:19:30.093 に答える