我正在尝试编写一些允许拖动SVG元素的代码。使用jQuery,jQuery UI和jQuery SVG这很容易,并且在Firefox中工作得很好,但是在我拖动SVG元素的Chrome中,显然会在其坐标中添加偏移量。我无法看到任何我做错的事情,或发现这方面的任何已知错误。
我构建了一个说明问题的小例子:
<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>
可在以下网址找到在线版本:
答案 0 :(得分:1)
问题是你没有从svg画布的0,0位开始。您可以通过从对象中减去x和y属性来实现所需的结果。计算手动拖动时,您将重新定位整个svg元素,而不仅仅是矩形。
见这里:http://jsfiddle.net/v477X/6/
注意:不同的浏览器似乎正在为您要翻译的元素返回不同的对象。此解决方案适用于Webkit浏览器。此时,您似乎有2个选项。修改选择器以选择相同的元素(特别是行t.getItem(0)),或者确定用户当前正在查看的浏览器,如果是webkit则添加偏移量。就个人而言,我会选择后者,因为你可以设置一个变量并检查它。
请参阅此处了解如何检测浏览器引擎:How to detect if a browser is Chrome using jQuery?
答案 1 :(得分:0)
jQueryUI中似乎存在一个错误:在非Webkit浏览器中,ui.position对象使用绝对坐标,而在其他浏览器中,它是与ui.originalPosition的偏移量。我不知道哪个是正确的;错误是行为不一致。我在:
提交了一份错误报告http://bugs.jqueryui.com/ticket/8335
因此,解决方法是存储顶级元素的原始坐标,并在Webkit浏览器中设置translate转换时将坐标设置为(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});
答案 2 :(得分:0)
我最近花了几天时间来解决这个问题,我想出了一个对我来说效果很好的解决方案。这绝对是一种黑客攻击,并不适用于所有情况。
This link对如何解决这个问题进行了很好的描述,这绝对是“更好”的解决方案。但是,我想继续使用JQuery优秀的Draggable API。
你可以快速模拟我在小提琴here中所做的事情。关键的想法是使用一个代理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放在目标上。在其他情况下,可能会困难得多。使用'收容'选项确保不将框架拖到框架外也不是太难......只需要仔细考虑一下,你就必须在每次拖动之间重置收容。
答案 3 :(得分: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>
答案 4 :(得分:0)
我有类似的问题。我发现由于某种原因,Chrome会将转换属性转换为CSS样式属性。因此它创建一个CSS样式 - CSS引擎调整坐标 - 然后SVG引擎然后尝试应用变换:translate。然后两者互相干扰。
CSS似乎有优先权。这是一个代码显示这个。
http://codepen.io/dax006/pen/ONNWXv
我的理论是,当Chrome风格正在更新CSS样式时,它会不断被破坏和重新创建,有时在SVG隐藏转换的那些时刻之间: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/