如何从 SVG 中的路径元素获取 X、Y 坐标?

How to get X,Y coordinates from path element inside SVG?

这是实际拥有的 fiddle。 https://jsfiddle.net/Lofdujwr/

我正在使用一个库来缩放和平移 SVG svgpanzoom。 例如,我有一个按钮,点击它会放大西班牙,所以我把坐标放在:

   $("#spain").on("click", function() {
        panZoom.zoomAtPoint(12, {x: 188, y: 185});
    });

问题是当我调整页面大小时坐标不再起作用我需要重新计算它们,我找到了一个可能有用但我不知道如何使用它的函数:

var s = instance.getSizes()
var p = instance.getPan()
var relativeX = s.width / 2 / (p.x + s.viewBox.width * s.realZoom)

// After resize
var s = instance.getSizes()
var p = instance.getPan()
var x = (s.width / 2 / relativeX) - s.viewBox.width * s.realZoom
instance.pan({x: x, y: 0})

另一个问题,例如,是否可以从 svg 中的路径 ID 获取坐标?

编辑: 好像我必须从我的 svg 计算实际的 X 和 Y 视口,然后重新计算它给出我在 0.0 上的点 (x:188, y: 185)视口,有人知道我能看到的任何例子吗?

如何对附加事件侦听器的方式进行一些小改动?

改为附加到每个国家?

    $(document).ready(function() {
  var panZoom = svgPanZoom('#mapa-mundi', {
        zoomEnabled: true,
        controlIconsEnabled: true,
        fit: true,
        center: true,
        minZoom: 1, 
        maxZoom: 200,
        zoomScaleSensitivity: 1
      });

   $(window).resize(function(){
        panZoom.resize();
        panZoom.fit();
        panZoom.center();
      })

      var svg_rect = document.querySelector('#mapa-mundi').getBoundingClientRect();
      alert("svg: " + svg_rect.top + " " + svg_rect.right + " " + svg_rect.bottom + " " + svg_rect.left);

      $("#spain").on("click", function() {
        panZoom.zoomAtPoint(12, {x: 188, y: 185});
      });

      //Find your root SVG element
      var svg = document.querySelector('#mapa-mundi');

      //Create an SVGPoint for future math
      var pt = svg.createSVGPoint();

      //Get point in global SVG space
      function cursorPoint(evt){
       pt.x = evt.clientX; pt.y = evt.clientY;
       return pt.matrixTransform(svg.getScreenCTM().inverse());
      }

      var country = document.querySelectorAll('.map-hover-svg');

      var my_dict = {};

      country.forEach(function(element){
        var rect = element.getBoundingClientRect();
        my_dict[element.id] = [rect.top, rect.right, rect.bottom, rect.left, rect.bottom - rect.top, rect.right - rect.left];
        //console.log(element.id);
        //console.log(rect.top, rect.right, rect.bottom, rect.left);
      });

      country.forEach(function(element){
        element.addEventListener('click',function(evt){
         var loc = cursorPoint(evt);
         var rect = element.getBoundingClientRect();
         var curr_pan = panZoom.getPan();
         var curr_zoom = panZoom.getZoom();
         var curr_sizes =panZoom.getSizes();
         var real_zoom = curr_sizes.realZoom;
         alert(curr_pan.x + " " + curr_pan.y + " " + curr_zoom + " " + real_zoom);
         panZoom.reset();
         var my_x = my_dict[evt.target.id][3] - svg_rect.left + (my_dict[evt.target.id][5] / 2);
         var my_y = my_dict[evt.target.id][0] - svg_rect.top + (my_dict[evt.target.id][4] / 2);
         //panZoom.zoomAtPoint(3, {x: loc.x - curr_pan.x - svg_rect.left, y: loc.y - curr_pan.y - svg_rect.top});
         panZoom.zoomAtPoint(3, {x: my_x, y: my_y});
         alert(evt.target.id + " at " + loc.x +" "+ loc.y);
        },false);
      });

      ///svg.addEventListener('click',function(evt){
       ///var loc = cursorPoint(evt);
       ///alert(loc.x+" "+loc.y);
      ///},false);

});

这样,只有在您单击那些红色国家/地区时才会触发该事件。此外,点击的坐标对我来说似乎是准确的,我试了一下它并得到了预期的值。

尝试使用 class '.map-hover-svg' 初始循环遍历所有元素,并将它们的右上角左下角添加到 dictionary/hash-table 中,键为 id,然后你可以使用 event.target.id.

引用这些字典项

然后您可以使用 svg 元素的 top 和 left 属性的偏移量以及国家/地区路径元素的宽度和高度的一半来始终缩放到点击的国家/地区的中间:

https://jsfiddle.net/ct89f0pj/2/

回答实际问题:

您正在使用的插件通过更改 g.svg-pan-zoom_viewport 的矩阵而不是 svgviewBox 矩阵来控制转换。

在您的函数 cursorPoint() 中,您不断变换参考 svg 的鼠标坐标,但您放弃了 g.svg-pan-zoom_viewport 上的基础变换。这就是为什么在 svg 调整大小或移动(=转换)时得到不同坐标的原因。

如果您将坐标引用到 g.svg-pan-zoom_viewport,您将获得一致的结果。

function cursorPoint(evt){
    pt.x = evt.clientX; pt.y = evt.clientY;
    //return pt.matrixTransform(svg.getScreenCTM().inverse());

    var tGroup = document.querySelector('.svg-pan-zoom_viewport');
    return pt.matrixTransform(tGroup.getScreenCTM().inverse());
}

另一种方法是更改​​ svgviewBox 而不是使用组矩阵。然而,由于您的插件是这样工作的,所以您应该使用它。

更新

我试了一下那个链接的插件,对我来说函数 zoomAtPoint() 做错了。让我们假设链接 fiddle 中的西班牙位于 165:165。现在要不断正确缩放到该位置,您需要先重置它:

panZoom.reset();
panZoom.zoomAtPoint(6, {x: 165, y: 165});

否则该函数要么不执行任何操作,要么在其他地方缩放。

现在获取 "argentinia" 的坐标并对其进行缩放:

panZoom.reset();

//REM: Apparently the values need some time to adjust after reset() is called, yet is seems to have no callback.
window.setTimeout(function(){
    var tViewport = document.querySelector('g.svg-pan-zoom_viewport');
    var tMatrix = tViewport.transform.baseVal.getItem(0).matrix;
    var tBBox = document.querySelector('#argentina').getBBox();
    var tPoint = {x: (tBBox.x + tBBox.width / 2) * tMatrix.a + tMatrix.e, y: (tBBox.y + tBBox.height / 2) * tMatrix.d + tMatrix.f}

    //REM: Approximate values, I leave the exact calculation up to you.
    panZoom.zoomAtPoint(6, tPoint);
}, 500)

使用示例按钮fiddle: https://jsfiddle.net/04Lg9ruj/

实际上你很幸运,我开发了一个 hugo 短代码来放大作为 url 参数传递的 SVG 中的文本,为此我必须搜索文本,找到它的 id,从缩放该 ID 与您的目标相同,因此找到边界框并应用缩放。

这里有现场演示 https://www.homesmartmesh.com/docs/microcontrollers/nrf52/thread_sensortag/?svg=nrf52-sensor-tag&text=VEML6030

源代码是将svg-pan-zoom嵌入到一个hugo短代码中,感兴趣的代码从这里开始 https://github.com/WebSVG/hugo-book/blob/7a7d44ea0f7f91a51c15bcb8e8efd294ef29f42f/static/js/setup-svg-pan-zoom.js#L70

text_highlight_zoom(embed,svg,element)

奇迹发生在这里

    let e_box = element.getBoundingClientRect();
    let svg_box = svg.getBBox();
    const x = (e_box.x + e_box.width/2)  / svg_box.width
    const y = (e_box.y + e_box.height/2) / svg_box.height

然后就是对svg_pz.zoomAtPoint(z,{x:x,y:y},true)

的简单调用

让我知道这个级别的详细信息是否对您有帮助,或者如果您有更多问题,请随时询问。

作为奖励,您可以获得平滑的 css 缩放操作过渡,否则用户不明白发生了什么,并且您将获得 svg 过滤器动画以突出显示目标文本。