[ACCEPTED]-SVG draggable using JQuery and Jquery-svg-svg

Accepted answer
Score: 46

jQuery UI draggable behavior does work, but 3 you need to update the position manually 2 in the drag handler, as relative CSS positioning 1 doesn't work inside SVG.

svg.rect(20,10,100,50, 10, 10, {fill:'#666'});
svg.rect(40,20,100,50, 10, 10, {fill:'#999'});
svg.rect(60,30,100,50, 10, 10, {fill:'#ccc'});

$('rect')
  .draggable()
  .bind('mousedown', function(event, ui){
    // bring target to front
    $(event.target.parentElement).append( event.target );
  })
  .bind('drag', function(event, ui){
    // update coordinates manually, since top/left style props don't work on SVG
    event.target.setAttribute('x', ui.position.left);
    event.target.setAttribute('y', ui.position.top);
  });
Score: 2

The solution I'm tinkering with involves 11 (to tie it to your case) creating a new 10 div and svg, positioned over the original 9 shape, to act as the handle for the targeted 8 svg object. Make the handle div draggable 7 and store the starting top/left offset externally 6 (think hidden div). Once the "stop" event 5 for the draggable div is fired, figure out 4 the degree of change for the top and left 3 (stopX-startX=changeX) and apply that to 2 the original shapes coordinates. Then .remove() your 1 temporary shape.

Score: 2

This link has an excellent description of how to 31 solve the problem in general (i.e., without 30 requiring JQuery), and that is definitely 29 the best solution I've seen. However, I 28 wanted to keep using JQuery's excellent 27 Draggable API.

I recently spent a couple 26 days hammering at this problem. The accepted 25 answer above is what I tried first, but 24 I couldn't get it to work right in Firefox. There's 23 something about how browsers handle SVG coordinates differently.

I came up with 22 a solution that worked fairly well for me, in 21 both Chrome and Firefox, and lets me keep 20 using JQuery UI. I haven't tested it everywhere. It's 19 definitely a hack.

You can see a quick mock-up 18 of what I did in a fiddle here. The key idea 17 is to use a proxy div which you keep hovering 16 exactly over the svg element you want to 15 drag. Then you change the svg element's 14 x and y coordinates as you drag the proxy 13 div. Something like this:

$('#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}
                  );
    });

In my case the 12 SVG element I wanted to drag would always 11 fill a certain square on the screen, so 10 it was very easy to position the proxy div 9 over the target. In other situations it 8 could be much more difficult. It's also 7 not too hard to use the 'containment' option 6 to make sure you don't drag the background 5 outside the frame...it just takes some careful 4 math and you have to reset the containment 3 in between each drag.

To make this applicable 2 to more SVG elements, you could use transforms 1 rather than x and y coordinates.

Score: 0

I have created a basic drag drop function 4 to target my svg objects. I dont have any 3 containment or collistion detection. There 2 is an issue if I move the mouse too quicly 1 I will leave the draggable object behind.

<!DOCTYPE HTML> 
<html >
<head>
<title></title>
<link href="css/reset.css" rel="stylesheet" type="text/css">
<link href="css/layout.css" rel="stylesheet" type="text/css">
<link href="css/style.css" rel="stylesheet" type="text/css">
<script src="js/jquery.js" type="text/javascript" ></script>
<script src="js/jquery-ui.js" type="text/javascript" ></script>
<script src="js/jquerysvg/jquery.svg.js" type="text/javascript" ></script>
<script src="js/jquerysvg/jquery.svgdom.js" type="text/javascript" ></script>

<script type="text/javascript" >
jQuery(document).ready(function(){
    $('#target').svg({onLoad: drawInitial});
    $('circle').click(function(e){
        drawShape(e);
        var shape = this.id;

    }); 
})

function drawInitial(svg) {
    svg.add($('#svginline')); 
}

function onMouseDown(evt){
        //var shape = this.id;

        var target = evt.target;        
        target.onmousemove = onMouseMove; 

        return false; 
}

function onMouseMove(evt){
    circle = evt.target

    var cx = circle.getAttribute("cx");
    offsetX = $('#target').offset().left;
    offsetY = $('#target').offset().top
    circle.setAttribute("cx", evt.clientX -offsetX);
    circle.setAttribute("cy", evt.clientY - offsetY);

    circle.onmouseup = OnMouseUp;
}

function OnMouseUp(evt) { 
    var target = evt.target;        
    target.onmousemove = null; 
}

function drawShape(e) {
    var svg = $("#target").svg('get');
    offsetX = $('#target').offset().left;
    offsetY = $('#target').offset().top;
    $('#result').text(e.clientX + ": " +  e.pageX);
    var dragme = svg.circle(e.clientX - offsetX, e.clientY - offsetY, 5, {onmousedown: "onMouseDown(evt)",fill: 'green', stroke: 'red', 'stroke-width': 3});    
    $(dragme).addClass('drag');
}
</script>
</head>
<body>
    <div id="target" ></div>
    <svg:svg id="svginline">
        <svg:circle id="circ11" class="area" cx="75" cy="75" r="50" stroke="black" stroke-width="2" fill="red"/>
    </svg:svg>
    <div id="result" >ffff</div>
</body>
</html>

More Related questions