0

I have an SVG object. I want this to be dragged and dropped in an area where it will be engaged. I exchanged the document.body through my "#name of svg object", but that does not work. I heard the problem with the SVG is the dragging offset? Because of top: e.pageY and left: e.pageX, but I do not know why. I tried it with translate and mouse but it doesn't work.

      $(document).ready(function() {
    var $dragging = null;

    $(document.body).on("mousemove", function(e) {
        if ($dragging) {
            $dragging.offset({
                top: e.pageY,
                left: e.pageX
            });
        }
    });


    $(document.body).on("mousedown", function (e) {
        $dragging = $(e.target);
     console.log($dragging);
    });

    $(document.body).on("mouseup", function (e) {
     console.log($dragging);
        $dragging = null;
    });
})
Mechlar
  • 4,946
  • 12
  • 58
  • 83
Ann
  • 1
  • 2

2 Answers2

1

Here is a simple way of thinking about dragging an absolutely positioned object around, and also a way of check if it is in a certain dropzone. Read my small comments to get some more explanation. Hope it helps!

// Lets get the SVG element and the dropZone
var SVG = document.getElementById('mySVG');
var Zone = document.getElementById('dropZone')

// We will store some global mouse info here, to calculate differences
var mouse = {x:0, y:0, down: false}

// This function will return true or false depending on whether the 'zone' and 'what' overlap
// http://stackoverflow.com/questions/12066870/how-to-check-if-an-element-is-overlapping-other-elements
function inDropZone(what, zone){
  zone = zone.getBoundingClientRect();
  what = what.getBoundingClientRect();
  return !(
    zone.right < what.left || 
    zone.left > what.right || 
    zone.bottom < what.top || 
    zone.top > what.bottom
  );
}

// This will set the drag to true so the mousemove can do its thing.
SVG.addEventListener('mousedown', function(e){
  mouse.down = true;
});

// This will only move the svg if mouse.down is true
document.addEventListener('mousemove', function(e){
  if(mouse.down){
    // Because the current position is stored as a String, we will use parseInt.
    // But since the SVG can also have an empty position value, we need to make sure
    // we output a number, so if parseInt fails, we use the value `0`
    SVG.style.left = (parseInt(SVG.style.left, 10) || 0) + e.pageX - mouse.x + 'px';
    SVG.style.top = (parseInt(SVG.style.top, 10) || 0) + e.pageY - mouse.y + 'px';
  }
  // This will continually reset the mouse position so we can drop and restart dragging at any time
  mouse.x = e.pageX;
  mouse.y = e.pageY;
});

// This will deactivate the mousedown and mark the drozone when the element is released.
document.addEventListener('mouseup', function(e){
  mouse.down = false;
  Zone.className = inDropZone(SVG, Zone) ? 'dropped' : '';
});
svg {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
}
#dropZone {
  position: absolute;
  right: 20px;
  bottom: 20px;
  width: 100px;
  height: 100px;
  border: 1px solid black;
}
#dropZone.dropped {
  background: red;
}
<svg viewBox="0 0 10 10" width="100" height="100" id="mySVG">
  <rect x="1" y="1" width="8" height="8" fill="green" />
</svg>
<div id="dropZone"></div>

This is pure Javascript, not jQuery, but it is quite simple to read and it is not doing anything complicated. I think another reason your system wouldn;t work is that e.target also includes children of the svg, which are not really under the influence of CSS (I tried to implement a similar something, but it kept selecting the rect instead of the svg).

I decided to go suggest a slightly different tack, check it out below:

// Lets get the dropZone
var Zone = document.getElementById('dropZone')

// Let's also get all the elements with a class of 'draggable'.
var Draggables = document.querySelectorAll('.draggable');
// We use the Array.prototype.slice.call to turn the above into a simple Array for eadsier use later
Draggables = Array.prototype.slice.call(Draggables);

// We will store some global mouse info here, to calculate differences
var mouse = {x:0, y:0, down: false}

// This function will return true or false depending on whether the 'zone' and 'what' overlap
// http://stackoverflow.com/questions/12066870/how-to-check-if-an-element-is-overlapping-other-elements
function inDropZone(what, zone){
  zone = zone.getBoundingClientRect();
  what = what.getBoundingClientRect();
  return !(
    zone.right < what.left || 
    zone.left > what.right || 
    zone.bottom < what.top || 
    zone.top > what.bottom
  );
}

Draggables.forEach(function(element){
  // This will set the drag to true so the mousemove can do its thing.
  element.addEventListener('mousedown', function(e){
    mouse.down = element;
  });
});

// This will only move the svg if mouse.down is true
document.addEventListener('mousemove', function(e){
  if(mouse.down){
    mouse.down.style.left = (parseInt(mouse.down.style.left, 10) || 0) + e.pageX - mouse.x + 'px';
    mouse.down.style.top = (parseInt(mouse.down.style.top, 10) || 0) + e.pageY - mouse.y + 'px';
  }
  // This will continually reset the mouse position so we can driop and restart dragging
  mouse.x = e.pageX;
  mouse.y = e.pageY;
});

// This will deactivate the mousedown and mark the drozone when the element is released.
document.addEventListener('mouseup', function(e){
  if(mouse.down) Zone.className = inDropZone(mouse.down, Zone) ? 'dropped' : '';
  mouse.down = false;
});
svg {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
}
#dropZone {
  position: absolute;
  right: 20px;
  bottom: 20px;
  width: 100px;
  height: 100px;
  border: 1px solid black;
}
#dropZone.dropped {
  background: red;
}
<svg viewBox="0 0 10 10" width="100" height="100" class="draggable">
  <rect x="1" y="1" width="8" height="8" fill="green" />
</svg>
<svg viewBox="0 0 10 10" width="100" height="100" class="draggable">
  <rect x="1" y="1" width="8" height="8" fill="yellow" />
</svg>
<div id="dropZone"></div>
somethinghere
  • 16,311
  • 2
  • 28
  • 42
0

You need an event handler to mousedown event on svg element.
Inside the event handler, set two event handlers more, to the mousemove and mouseup events.
The mousemove event handler must handle the svg viewport coords calculations.
The mouseup just cleans the handlers you set to mousemove and mouse events.

This is, in short, how to deal with mouse events to drag.

About calculations, it's a try and error process. I've done it just now and you can see it in this fiddle.

  var viewBox = svg.getAttribute('viewBox');
  viewBox = viewBox.split(' ');
  var vwx = parseFloat(viewBox[0]);
  var vwy = parseFloat(viewBox[1]);

  var xy = getSvgCordinates(event);
  var x = xy.x;
  var y = xy.y;

  var x0 = xy0.x;
  var y0 = xy0.y;

  var dx = (x - x0);
  var dy = (y - y0);
  var xnew, ynew;

  var xnew = vwx - dx;
  var ynew = vwy - dy;

  var s = 'XY0 (coord.click): ' + x0 + ',' + y0 + '\n' +
    'XY (coord.drag): ' + x + ',' + y + '\n' +
    'VW (coord.viewport): ' + vwx + ',' + vwy + '\n' +
    'XY-XY0: ' + dx + ',' + dy + '\n' +
    'VMNEW: ' + xnew + ',' + ynew;
  texto.innerText = s;

  viewBox = xnew + ' ' + ynew + ' ' + viewBox[2] + ' ' + viewBox[3];
  svg.setAttribute('viewBox', viewBox);
Diego Buendia
  • 97
  • 1
  • 7