web-dev-qa-db-ja.com

CSS変換要素のドラッグとサイズ変更

たとえば、長方形に-vendor-transform: rotate(40deg)css属性を設定した場合<div>、突然のドラッグとサイズ変更はすべて非常に奇妙で欠陥があります。

単純なjQueryUIの例を次に示します。 http://jsfiddle.net/Ja4dY/1/

変換時にその長方形をドラッグまたはサイズ変更すると、長方形が上下にジャンプし、カーソルが正しい位置に留まらないことに気付くでしょう。実際のコードでは、サイズ変更とドラッグにカスタムコードを使用していますが、同じ問題が発生しました。

もちろん、「問題」は要素の方向が変わることです。したがって、左が右、上が下、その中間になり、Javascriptコードは変換されないように各方向を処理します。

だから、質問:どうすればtransformed/rotated要素を補正できますか?

良いリソース/本/ブログも大歓迎です。

43
jAndy

GetComputedStyle()を使用して、要素に適用されている現在の変換行列を取得できます。これを使用して、現在のマウスの位置を変換された空間内の位置に変換し、クリック/ドラッグイベントが要素の境界やコーナー内にあるかどうかを確認できます。このための良いリソース:

http://www.useragentman.com/blog/2011/01/07/css3-matrix-transform-for-the-mathematically-challenged/

http://www.eleqtriq.com/2010/05/css-3d-matrix-transformations/

ところで、あなたが経験しているように、これはコーディングにとって簡単ではありません。私たちは煎茶アニメーターのためにそれをしなければならなかった、そしてそれは獣だった。

14
Michael Mullany

問題は、jQuery UIを使用しているかどうかに関係なく、要素をドラッグ可能にする関数が、要素の位置などを把握するためにネイティブのgetBoundingClientRect()関数に大きく依存していることです。

回転などのCSS3変換を適用すると、jQueryUIで使用されるgetBoundingClientRect()または同等のjQueryoffset()関数の値が期待どおりに機能しなくなり、マウスポインターの位置が混乱します。要素を回転させた後、要素のサイズが突然間違っているためです。

これを修正するには、値を再計算する何らかのヘルパー関数を追加する必要があります。これには、jQueryUIのドラッグ可能で動作するモンキーパッチがあります。

カスタムコードで同じパッチを機能させる方法については何も言うのは難しいですが、おそらく何らかの方法でカスタム関数に統合する必要があり、コーディングが必要になり、思いつくのがさらに難しくなります箱から出してすぐに機能するカスタムコードのヘルパー関数として機能するものは見たことがなく、これらの計算の実行にかなり関与していることに注意してください。以下のコードを参照してください。

function monkeyPatch_mouseStart() {
     var oldFn = $.ui.draggable.prototype._mouseStart ;
     $.ui.draggable.prototype._mouseStart = function(event) {

            var o = this.options;

           function getViewOffset(node) {
              var x = 0, y = 0, win = node.ownerDocument.defaultView || window;
              if (node) addOffset(node);
              return { left: x, top: y };

              function getStyle(node) {
                return node.currentStyle || // IE
                       win.getComputedStyle(node, '');
              }

              function addOffset(node) {
                var p = node.offsetParent, style, X, Y;
                x += parseInt(node.offsetLeft, 10) || 0;
                y += parseInt(node.offsetTop, 10) || 0;

                if (p) {
                  x -= parseInt(p.scrollLeft, 10) || 0;
                  y -= parseInt(p.scrollTop, 10) || 0;

                  if (p.nodeType == 1) {
                    var parentStyle = getStyle(p)
                      , localName   = p.localName
                      , parent      = node.parentNode;
                    if (parentStyle.position != 'static') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;

                      if (localName == 'TABLE') {
                        x += parseInt(parentStyle.paddingLeft, 10) || 0;
                        y += parseInt(parentStyle.paddingTop, 10) || 0;
                      }
                      else if (localName == 'BODY') {
                        style = getStyle(node);
                        x += parseInt(style.marginLeft, 10) || 0;
                        y += parseInt(style.marginTop, 10) || 0;
                      }
                    }
                    else if (localName == 'BODY') {
                      x += parseInt(parentStyle.borderLeftWidth, 10) || 0;
                      y += parseInt(parentStyle.borderTopWidth, 10) || 0;
                    }

                    while (p != parent) {
                      x -= parseInt(parent.scrollLeft, 10) || 0;
                      y -= parseInt(parent.scrollTop, 10) || 0;
                      parent = parent.parentNode;
                    }
                    addOffset(p);
                  }
                }
                else {
                  if (node.localName == 'BODY') {
                    style = getStyle(node);
                    x += parseInt(style.borderLeftWidth, 10) || 0;
                    y += parseInt(style.borderTopWidth, 10) || 0;

                    var htmlStyle = getStyle(node.parentNode);
                    x -= parseInt(htmlStyle.paddingLeft, 10) || 0;
                    y -= parseInt(htmlStyle.paddingTop, 10) || 0;
                  }

                  if ((X = node.scrollLeft)) x += parseInt(X, 10) || 0;
                  if ((Y = node.scrollTop))  y += parseInt(Y, 10) || 0;
                }
              }
            }

                this.helper = this._createHelper(event);
                this._cacheHelperProportions();

                if($.ui.ddmanager)
                    $.ui.ddmanager.current = this;

                this._cacheMargins();

                this.cssPosition = this.helper.css("position");
                this.scrollParent = this.helper.scrollParent();

            this.offset = this.positionAbs = getViewOffset(this.element[0]);
                this.offset = {
                    top: this.offset.top - this.margins.top,
                    left: this.offset.left - this.margins.left
                };

                $.extend(this.offset, {
                    click: {
                        left: event.pageX - this.offset.left,
                        top: event.pageY - this.offset.top
                    },
                    parent: this._getParentOffset(),
                    relative: this._getRelativeOffset()
                });

                this.originalPosition = this.position = this._generatePosition(event);
                this.originalPageX = event.pageX;
                this.originalPageY = event.pageY;

                (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));

                if(o.containment)
                    this._setContainment();

                if(this._trigger("start", event) === false) {
                    this._clear();
                    return false;
                }

                this._cacheHelperProportions();

                if ($.ui.ddmanager && !o.dropBehaviour)
                    $.ui.ddmanager.prepareOffsets(this, event);

                this.helper.addClass("ui-draggable-dragging");
                this._mouseDrag(event, true);

                if ( $.ui.ddmanager ) $.ui.ddmanager.dragStart(this, event);
                return true;
     };
 }
monkeyPatch_mouseStart();

そして、これが [〜#〜] fiddle [〜#〜] jQueryUIのドラッグ可能およびサイズ変更可能で期待どおりに機能していることを示しています!

7
adeneo

私はこれを見つけました...これは実用的な例に加えて、情報、デモ、ダウンロードリンクです。

jquery-ui-rotation-using-css-transform -> live-demo

彼は自分のライブラリを使用していますが、この主題に興味がある場合は、彼がどのようにそれを取得するかを読んで学ぶことができます。

歓声と幸運。

Gmo.-

ところで、ウェブはロシア語ですが、 google translate で管理できます;-)

5
gmo

JQueryのバグではありません。単にサポートされていません。 jQuery UIのソースコードを確認すると、変換されたオブジェクトとページの差を計算するために変換行列を使用していないことがわかります。

あなたの例、そしておそらくすべてのjQ UIドラッグ実装は、JQ UIソースコードの2つのメソッド(jquery.ui.draggable.jsファイルv1.8.23の約314行)のこの問題の原因に苦しんでいます。回転は要素の中心で行われるため、計算されたオフセットはオフセットの変化に関係ありません。

その変化が何であるかを計算する必要があります。これが回避策であり、迅速で汚れています。変換された要素のバウンディングボックスの違いを確認するのが目的です。

ここでサンプルを確認してください http://jsfiddle.net/mjaric/9Nqrh/

最初の2回転の部分は無視してください。これらは、コード行を最小限に抑えるために実行されます。 3つ目は、計算された差の座標系の変換です。変換が実行された後、左右にオフセットされます(フィルターの最初にあることに注意してください)。

最初の2つの回転フィルターを避けたい場合は、2D回転の式を使用してコードを作成できます。

x '= x cos f-y sin f

y '= y cos f + x sin f

ここで、fは回転角ですが、それほど単純ではなく、x座標とy座標がxと比較している左上隅の初期角度が必要なため、元の境界ボックスの対角角を計算する必要があるコード行も多く含まれています。軸(正の部分)。次に、x-x 'とy-y'の変化を計算します。しかし、私は変化の兆候に関するいくつかの問題を予測しており、コーディング/デバッグには現在よりも時間がかかるでしょう。申し訳ありませんが、この投稿を読んだ後、あなたは何をすべきかを理解できると確信しています。

2
Milan Jaric

CursorAtをオーバーライドすると見栄えが良くなります。

$("#foo").mousedown(function (e) { 
    var x = e.pageX - this.offsetLeft;
    var y = e.pageY - this.offsetTop;
    console.log(x);
    $("#foo").draggable("option", "cursorAt", {left: x, top:y});
});

更新されたフィドル: http://jsfiddle.net/johnkoer/Ja4dY/8/

1
John Koerner

これは確かに、jQueryのバグのようです。簡単な回避策は次のとおりです。サイズ変更可能なdivをコンテナdivで囲みます。 .draggable()を外側のdivに設定し、.resizable()を内側のdivに設定します。これは、Ubuntuで実行されているChromiumでは正常に機能しているようです。 フィドルを参照

ボンネットの下で何が起こっているのかがわかるように、外側のdivに色を付けました。

0
Jasper de Vries

そのとき、JQueryソリューションには興味がないとおっしゃいましたが、

  • 1つの解決策は次のとおりです。

    独自のドラッグおよびサイズ変更関数を作成することをお勧めします。回転したオブジェクトのサイズ変更とドラッグを処理して、その程度のサインとコサインで上下を追加できます。

  • 別の解決策は次のとおりです。

    Raphael JSなどのライブラリを使用して、変換、ドラッグ、サイズ変更するオブジェクトを作成できます。 Raphael JSはsvgを使用しています!

    Raphael JSの詳細について

  • さらに別の解決策は次のとおりです。

    Raphael JSのようなライブラリを使用したくない場合は、JQueryでSVGを直接使用できます。

    SVGの詳細について

現在、詳細を書くことはできません。明日、このソリューションを拡張します。

今のところこれらの助けを願っています。

0
totten