SVG draggable using JQuery and Jquery-svg

2020-01-26 03:55发布

I have an HTML 5 page where I load an svg circle. When I click on the circle I create another small circle where I click. I want to be able to drag the second circle but cant seem to do it with jquery-ui .draggable();

I am able to move the circle by accessing its cx and cy attributes so there must be a way to drag it.

    <!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/jquerysvg/jquery.svg.js" type="text/javascript" ></script>
<script src="js/jquery-ui.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;

    });

    $('.drag').mousedown(function(e){
        var shape = this.id;
        this.setAttribute("cx", e.pageX);
        this.setAttribute("cy", e.pageY);
    });
})

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

function drawShape(e) {
    var svg = $("#target").svg('get');
    $('#result').text(e.clientX + ": " +  e.pageX);
    var dragme = svg.circle(e.clientX, e.clientY, 5, {fill: 'green', stroke: 'red', 'stroke-width': 3, class_: 'drag'});    
    //$(dragme).draggable();
}
</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>

5条回答
Explosion°爆炸
2楼-- · 2020-01-26 04:08

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

查看更多
Juvenile、少年°
3楼-- · 2020-01-26 04:15

I have created a basic drag drop function to target my svg objects. I dont have any containment or collistion detection. There is an issue if I move the mouse too quicly 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>
查看更多
等我变得足够好
4楼-- · 2020-01-26 04:16

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

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

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

You can see a quick mock-up of what I did in a fiddle here. The key idea is to use a proxy div which you keep hovering exactly over the svg element you want to drag. Then you change the svg element's x and y coordinates as you drag the proxy 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 SVG element I wanted to drag would always fill a certain square on the screen, so it was very easy to position the proxy div over the target. In other situations it could be much more difficult. It's also not too hard to use the 'containment' option to make sure you don't drag the background outside the frame...it just takes some careful math and you have to reset the containment in between each drag.

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

查看更多
Fickle 薄情
5楼-- · 2020-01-26 04:27

Just put the svg in a draggable div.

//*
$(document).ready(function(){
  $('#mydiv').draggable();
});
//*/
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<div id="mydiv">
  <svg xml:lang="en"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink">
    <text class="main" x="10" transform="rotate(-28 0 0)" y="90">Sol min</text>
    <text class="sous" x="4" y="110" transform="rotate(-28 0 20)">(SOUS DOM.)</text>
   <line stroke="black" stroke-width="2" x1="10" y1="100" x2="110" y2="46" />
   <line stroke="red" stroke-width=2 x1=10 y1=99 x2=10 y2=140 />
 </svg>
</div>

查看更多
男人必须洒脱
6楼-- · 2020-01-26 04:28

jQuery UI draggable behavior does work, but you need to update the position manually in the drag handler, as relative CSS positioning 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);
  });
查看更多
登录 后发表回答