获取 d3 中 svg:image 上放置元素的 xy 坐标

To get the xy coordinates of dropped element on svg:image in d3

这里我正在处理一个示例,我需要从 svg:image 元素中获取拖放元素的 X 和 Y 值(使用 Jquery 拖放)。请查看我的codepen。在代码笔中,您可以看到一个 rss 图标,它是可拖动元素,主要大图像用作可放置元素。使用d3 svg:image 添加主要的大图像。将 rss 图标拖放到主大图像上时,应在拖放点上附加一个 rss 图标。所以在 drop 函数 (JQuery) 中,我添加了 d3 代码以将 rss 图标附加到主大图像。

我使用了4种方法来检索rss图标被放置点的X&Y值。在代码笔中可以看到:

提取xy位置的第一种方法:

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

提取xy位置的第二种方法:

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;

提取xy位置的第三种方法:

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

第四种提取xy位置的方法:

    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;

将放置的图标附加到主大图:

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

但是使用所有方法,rss 图标并没有附加到它被放置的确​​切位置。如何检索准确的 X 和 Y 值?

我想我明白你在找什么了。对于这个问题,有一些事情需要考虑:

ViewBox 缩放

这是要考虑的主要项目。由于您在 svg 元素上使用 viewBox 属性 而您没有使用 width / height,那么无论浏览器 window 大小是多少,都会改变 svg 的可见范围 canvas。删除克隆图像时要考虑这一点很重要,因为您不能使用绝对 top / left 坐标。它在 0, 0 处可能看起来还不错,但是当您开始远离它时,您会注意到图像没有放在您认为应该放置的位置。

解决这个问题的方法是使用 d3.scaleLinear()(文档 here)。这将有助于将可放置坐标转换为 svg 的坐标。我把它放在一起的方式是,对于 X 比例,domain 是从 SVG 的 left 属性 到 left + width 属性和 range 设置到您的 SVG 视图框 x-minwidth 值。 Y 尺度使用了类似的方法(参见代码)。

SVG 在您页面上的位置

这是接下来要考虑的事情。它与上一个关于视图框的项目一起出现。例如,如果 SVG 有一些边距,那么您在设置 d3.scaleLinear() 比例时需要考虑到这一点。方法是像这样使用 getBoundingClientRectangle()

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

如果 window 调整大小

如果 window 调整了大小,您将需要重置秤。

从可拖动元素抓取图像的位置

如果您想弄清楚如何获得第一次用鼠标抓取图像时抓取图像的位置的 x,y 偏移量,这可以增加一些工作量。相反,我在 draggable() 方法的配置上使用 cursorAt 属性 并将其设置为 { left: 0, top: 0 } 因此当您拖动图像时,用户将在左上角看到光标的形象。这使您无需计算他们抓取图像的位置的偏移量。

这是我最后使用的代码(pen here)(另外,我不得不使用不同的图标,因为你使用的图标没有为我加载)。

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