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 浏览器。此时,您似乎有两个选择。修改您的选择器以选择相同的元素(特别是 t.getItem(0) 行),或者确定用户当前在哪个浏览器上查看,如果是 webkit,则添加偏移量。就个人而言,我会选择后者,因为这样你就可以设置一个变量并检查它。

请参阅此处了解如何检测浏览器引擎:How to detect if a browser is Chrome using jQuery?

于 2012-05-16T23:01:50.663 回答
0

这里的 jQueryUI 似乎有一个错误:在非 Webkit 浏览器中,ui.position 对象使用绝对坐标,而在其他浏览器中,它是 ui.originalPosition 的偏移量。我不知道哪个是正确的;错误是行为不一致。我已在以下位置提交了错误报告:

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

因此,解决方法是存储顶级元素的原始坐标,并在 Webkit 浏览器中设置翻译转换时将坐标设置为 (ui.position - ui.originalPosition + origCoords)。(Jlange 的答案使用了我的最小示例中使用的 rect 的 x 和 y 属性,在那里可以正常工作,但不适用于更复杂的对象 a)可能没有 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

我最近花了几天时间来解决这个问题,我想出了一个对我来说效果很好的解决方案。这绝对是一个 hack,并且不会在所有情况下都有效。

这个链接对如何解决一般问题有很好的描述,这绝对是“更好”的解决方案。但是,我想继续使用 JQuery 出色的 Draggable API。

你可以在这里看到我在小提琴中所做的快速模型。关键思想是使用代理 div,您可以将其精确地悬停在要拖动的 svg 元素上。然后在拖动代理 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 回答
0

我有一个类似的问题。我发现由于某种原因,Chrome 将转换属性转换为 CSS 样式属性。所以它创建了一个 CSS 样式 - CSS 引擎调整坐标 - 然后 SVG 引擎尝试应用变换:翻译。然后两人互相干扰。

CSS 似乎有优先权。这是一个显示这一点的代码笔。
http://codepen.io/dax006/pen/ONNWXv

我的理论是,当 Chrome 更新 CSS 样式时,它会不断地被破坏和重新创建,有时在这些时刻之间,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 回答