3

SVG 要素をドラッグできるようにするコードを書き込もうとしています。これは、jQuery、jQuery UI、jQuery SVG では非常に簡単で、Firefox では問題なく動作しますが、Chrome では SVG 要素をドラッグすると、座標に明らかにオフセットが追加されます。私が間違っていることは何も見えませんし、この分野で既知のバグを発見することもできません。

問題を説明する小さな例を作成しました。

<html>
  <head>
    <title>Foo</title>
    <style type="text/css">
    </style>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
    <script type="text/javascript" src="http://keith-wood.name/js/jquery.svg.js"></script>
    <script type="text/javascript">
$(function() {
    var svgOnLoad = function () {
        var svg = $('#canvas').svg('get');
        $('#piece')
            .draggable()
            .bind('drag', function (event, ui) {
                // Update transform manually, since top/left style props don't work on SVG
                var t = event.target.transform.baseVal;
                if (t.numberOfItems == 0) {
                    t.appendItem(svg.root().createSVGTransform());
                }
                t.getItem(0).setTranslate(ui.position.left, ui.position.top);
            });
    }
    $('#canvas').svg({loadURL: "foo.svg", onLoad: svgOnLoad});
});
    </script>
  </head>
  <body>
    <div id="canvas" style="width: 100%; height: 100%;"></div>
  </body>
</html>

foo.svg は次のとおりです。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1"
    xmlns="http://www.w3.org/2000/svg"
    width="450" height="450">
    <rect id="piece" width="50" height="50" x="100" y="100" />
</svg>

オンライン版は次の場所にあります。

http://jsfiddle.net/rrthomas/v477X/2/

4

5 に答える 5

1

問題は、svg キャンバスの位置 0, 0 から開始していないことです。オブジェクトから x 属性と y 属性を差し引くことで、目的の結果を得ることができます。手動ドラッグを計算すると、長方形だけでなく、svg 要素全体が再配置されます。

ここを参照してください:http://jsfiddle.net/v477X/6/

注:ブラウザが異なれば、翻訳しようとしている要素に対して異なるオブジェクトが返されるようです。このソリューションは、Webkit ブラウザーで機能します。この時点で、2 つのオプションがあるようです。セレクターを変更して同じ要素 (具体的には t.getItem(0) の行) を選択するか、ユーザーが現在表示しているブラウザーを特定し、webkit の場合はオフセットを追加します。個人的には、変数を設定して確認するだけでよいので、後者を使用します。

ブラウザ エンジンを検出する方法については、こちらを参照してください:ブラウザが jQuery を使用して Chrome であるかどうかを検出するには?

于 2012-05-16T23:01:50.663 に答える
0

同様の問題があります。何らかの理由で、Chrome が変換属性を CSS スタイル プロパティに変換することがわかりました。CSS スタイルを作成し、CSS エンジンが座標を調整し、SVG エンジンが transform:translate を適用しようとします。その後、2つは互いに干渉します。

CSSが優先されるようです。これを示すコードペンを次に示します。
http://codepen.io/dax006/pen/ONNWXv

私の理論では、CSS スタイルが Chrome によって更新されているとき、それは常に破棄され、再作成されており、その間に SVG が tranform:translate() に潜入することがあります。Chrome は新しい変換値を取得して CSS に適用しますが、CSS が邪魔をしているため、変換が正しく機能しません。

理論的には、transform:translate は CSS の作成と同時に破棄されるはずですが、そうではありません。要素を調べるだけで、座標を変更する CSS スタイルと変換属性の両方が存在することがわかります。

<rect y="100" x="100" height="50" width="50" id="piece" style="position: relative; left: -15px; top: -13px;" transform="translate(-15, -13)"/>

この記事と下部のリンクを必ず確認してください。 https://css-tricks.com/svg-animation-on-css-transforms/

于 2016-03-09T03:24:48.203 に答える
0

ここで jQueryUI にバグがあるようです: 非 Webkit ブラウザーでは、ui.position オブジェクトは絶対座標を使用しますが、他のブラウザーでは ui.originalPosition からのオフセットです。どちらが正しいかわかりません。バグは、動作に一貫性がないことです。バグレポートを次の場所に提出しました。

http://bugs.jqueryui.com/ticket/8335

したがって、回避策は、最上位要素の元の座標を保存し、Webkit ブラウザーで変換変換を設定するときに座標を (ui.position - ui.originalPosition + origCoords) に設定することです。(Jlange の回答は、私の最小限の例で使用されている rect の x および y 属性を使用しています。これはそこでは正常に機能しますが、より複雑なオブジェクトでは機能しません) x および y 属性を持たない可能性があります (たとえば、最上位の要素がag 要素) および b) 返された座標がとにかく最上位要素の座標ではないようです (理由がわからないのではないかと思います)。最小限のコードは次のとおりです。

var svgOnLoad = function () {
var svg = $('#canvas').svg('get');
    $('#piece')
        .draggable()
            .on({
                dragstart: function (event, ui) {
                    var tlist = this.transform.baseVal;
                        if (tlist.numberOfItems == 0) {
                            tlist.appendItem(svg.root().createSVGTransform());
                        }
                        var tm = tlist.getItem(0).matrix;
                        var pos = {left: tm.e, top: tm.f};
                        $.data(this, 'originalPosition', pos);
                    },
                    drag: function (event, ui) {
                        // Update transform manually, since top/left style props don't work on SVG
                        var tlist = this.transform.baseVal;
                        var CTM = this.getCTM();
                        var p = svg.root().createSVGPoint();
                        p.x = ui.position.left + CTM.e;
                        p.y = ui.position.top + CTM.f;
                        if ($.browser.webkit) { // Webkit gets SVG-relative coords, not offset from element
                            var origPos = $.data(this, 'originalPosition');
                            p.x -= ui.originalPosition.left - origPos.left;
                            p.y -= ui.originalPosition.top - origPos.top;
                        }
                        p = p.matrixTransform(CTM.inverse());
                        tlist.getItem(0).setTranslate(p.x, p.y);
                    }});
};
$('#canvas').svg({onLoad: svgOnLoad});

ライブバージョン: http://jsfiddle.net/rrthomas/v477X/

于 2012-05-19T11:54:32.917 に答える
0

私は最近、この問題に数日を費やしましたが、私にとってかなりうまくいく解決策を思いつきました. これは間違いなくハックであり、すべての状況で機能するとは限りません。

このリンクには、一般的に問題を解決する方法の優れた説明があり、それは間違いなく「より良い」解決策です。しかし、JQuery の優れた Draggable API を使い続けたかったのです。

ここでフィドルで行ったことの簡単なモックアップを見ることができます。重要なアイデアは、ドラッグする svg 要素の上に正確にホバリングし続けるプロキシ div を使用することです。次に、プロキシ div をドラッグして、svg 要素の x 座標と y 座標を変更します。このようなもの:

$('#proxy').on('drag', function(e)
    {
        t = $('#background');
        prox = $('#proxy');
        t.attr('x', t.attr('x')*1
                   + prox.css('left').slice(0,-2)*1
                   - prox.data('position').left)
            .attr('y', t.attr('y')*1
                      + prox.css('top').slice(0,-2)*1
                      - prox.data('position').top);
        prox.data('position',{top : prox.css('top').slice(0,-2)*1,
                              left: prox.css('left').slice(0,-2)*1}
                  );
    });

私の場合、ドラッグしたい SVG 要素は常に画面上の特定の正方形を埋めるため、ターゲット上にプロキシ div を配置するのは非常に簡単でした。他の状況では、はるかに困難になる可能性があります。フレームの外側に背景をドラッグしないようにするために、「コンテインメント」オプションを使用することもそれほど難しくありません...注意深い計算が必要で、各ドラッグの間にコンテインメントをリセットする必要があります。

于 2013-06-21T17:56:17.870 に答える
0

これを使って:

<html>
<head>
    <title>Foo</title>
    <style type="text/css">
    </style>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/ui/1.8.18/jquery-ui.min.js"></script>
    <script type="text/javascript" src="http://keith-wood.name/js/jquery.svg.js"></script>
    <script type="text/javascript">
        $(function ()
            {
            var svg = $('#canvas').svg().svg('get');
            $('#piece')
                    .draggable()
                    .on({
                        dragstart: function (event, ui)
                            {
                            var t = this.transform.baseVal;
                            if (t.numberOfItems == 0)
                                { t.appendItem(svg.root().createSVGTransform()); }
                            var tm = t.getItem(0).matrix;
                            var dx = tm.e - ui.position.left;
                            var dy = tm.f - ui.position.top;
                            $.data(this, 'dragTo', function dragTo(newPos)
                                {
                                tm.e = newPos.left + dx;
                                tm.f = newPos.top + dy;
                                });
                            },
                        drag: function (event, ui)
                            { $.data(this, 'dragTo')(ui.position); }});
            });
    </script>
</head>
<body>
<div id="canvas" style="width: 100%; height: 100%;">
    <svg version="1.1"
         xmlns="http://www.w3.org/2000/svg"
         width="450" height="450">
        <rect id="piece" width="50" height="50" x="0" y="0" style="position:absolute"/>
    </svg>
</div>
</body>
</html>
于 2014-02-19T11:19:33.950 に答える