Home » Jquery » jquery – To get the xy coordinates of dropped element on svg:image in d3-Exceptionshub

jquery – To get the xy coordinates of dropped element on svg:image in d3-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

Here I’m working with an example where I need to get X and Y value of the dropped element (using Jquery drag & drop) from the svg:image element. Please check out my codepen. In the codepen, you can see an rss icon, which is the draggable element and the main big image is used as the droppable element. The main big image is added using d3 svg:image. While dropping the rss icon to the main big image, on the dropped point an rss icon should be appended. So in the drop function (JQuery), I added the d3 code to append the rss icon to main big image.

I have used 4 methods to retrieve the X & Y value of the point which the rss icon is dropped. In the code pen you can see that:

First method to extract xy positon:

 var currentPos = ui.helper.position();
 var parentOffset = $(this).parent().offset();
 var x1 = event.pageX - parentOffset.left;
 var y1 = event.pageY - parentOffset.top;

Second method to extract xy positon:

var dropPositionX = event.pageX - $(this).offset().left;
var dropPositionY = event.pageY - $(this).offset().top;
// Get mouse offset relative to dragged item:
var dragItemOffsetX = event.offsetX;
var dragItemOffsetY = event.offsetY;
// Get position of dragged item relative to drop target:
var x2 = dropPositionX-dragItemOffsetX;
var y2 = dropPositionY-dragItemOffsetY;

Third method to extract xy positon:

 var x3 = ui.offset.left - $(this).offset().left;
 var y3 = ui.offset.top - $(this).offset().top;

Fourth method to extract xy positon:

    var elem = document.getElementById("floor")
    var rect = elem.getBoundingClientRect();
            var scrollTop = document.documentElement.scrollTop?
                            document.documentElement.scrollTop:document.body.scrollTop;
            var scrollLeft = document.documentElement.scrollLeft?                   
                            document.documentElement.scrollLeft:document.body.scrollLeft;
            var elementLeft = rect.left+scrollLeft;  
            var elementTop = rect.top+scrollTop;

            var x4 = event.pageX-elementLeft;
            var y4 = event.pageY-elementTop;

Appending dropped icon to the main big image:

svg.append("svg:image")
      .attr('xlink:href', 'http://icons.webpatashala.com/icons/Blueberry-Basic-Icons/Png/rss-icon.PNG')
      .attr('x', x1)  //x2, x3, x4, x5
      .attr('y', y1);  //x2, x3, x4, x5

But using all the methods, rss icon is not append on the exact point in which it is dropped. How to retrieve the exact X & Y value?

How to&Answer:

I think I figured out what you’re looking for. There are some things to consider with this problem:

ViewBox Scaling

This is the main item to consider. Since you are using the viewBox property on your svg element and you are not using width / height, then whatever the browser window size is will change the scale of the svg’s viewable canvas. This is important to consider when dropping the cloned image because you cannot use absolute top / left coordinates. It might look OK at 0, 0, but when you start moving away from it you’ll notice that the image is not dropped where you think it should be.

The way around this is to use d3.scaleLinear() (docs here). This will help translate the droppable coordinates to the coordinates of your svg. The way I put it together is that for the X scale, the domain is from the SVG’s left property to the left + width properties and the range is set to your SVG’s viewbox x-min and width values. A similar approach is used for the Y scale (see code).

Where the SVG is on your page

This is the next thing to consider. It goes along with the previous item about the viewbox. If the SVG has some margins, for example, then you’ll need to consider this when setting up the d3.scaleLinear() scale. The way to do is to use getBoundingClientRectangle() like this:

const clientRect = document.getElementById('floor').getBoundingClientRect();

If the window is resized

If the window is resized, you will need to reset the scales.

Where you grab the image from the draggable element

This can add some work if you want to figure out how to get the x,y offset of where you grabbed the image from when you first grab it with the mouse. Instead, I use the cursorAt property on the draggable() method’s configuration and set it to { left: 0, top: 0 } so when you drag the image, the user will see the cursor at the top left of the image. This saves you from doing the work of figuring out the offset of where they grabbed the image.

Here is the code I used in the end (pen here) (also, I had to use a different icon because the one you were using wasn’t loading for me).

let scaleX = getScaleX();
let scaleY = getScaleY();

function getScaleX() {
  const clientRect = document.getElementById('floor').getBoundingClientRect();
  return d3.scaleLinear()
    .domain([clientRect.left, clientRect.left + clientRect.width])
    .range([0, 1500])
}

function getScaleY() {
  const clientRect = document.getElementById('floor').getBoundingClientRect();
  return d3.scaleLinear()
    .domain([clientRect.top, clientRect.top + clientRect.height])
    .range([0, 800])
}

window.onresize = function() {
  scaleX = getScaleX();
  scaleY = getScaleY();
};

var svg = d3.select('#floor');

svg.append("svg:image")
  .attr('xlink:href', 'https://wallpapershome.com/images/pages/pic_h/241.jpg')
  .style('width', "100%");

$(function() {
  $("#draggable" ).draggable({
      helper: "clone",
    cursor: 'move',
    containment: "document",
    cursorAt: { top: 0, left: 0 }
  });

  $( "#droppable" ).droppable({
    drop: function( event, ui ) {
      $( this ).addClass( "ui-state-highlight" );

      svg.append("svg:image")
        .attr('xlink:href', 'https://i2.wp.com/icons.iconarchive.com/icons/cornmanthe3rd/plex/512/Communication-RSS-icon.png')
        .attr('x', scaleX(event.pageX))
        .attr('y', scaleY(event.pageY))
        .attr('width', '50px');
    }
  });
});