【问题标题】:How to get X,Y coordinates from path element inside SVG?如何从 SVG 中的路径元素获取 X、Y 坐标?
【发布时间】:2020-04-22 01:50:40
【问题描述】:

这里是实际拥有的东西。 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})

function post

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

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

【问题讨论】:

  • 如果您调整页面大小,SVG 坐标不应改变,除非您不使用 SVG 坐标(这很糟糕)。还有什么path coordinates给你?路径有段,而不是 x/y 坐标。
  • 例如在我的 jsfiddle 上,我有一个 svg 地图,它包含每个国家/地区的 id 路径,如果我能获得该路径的坐标会很好
  • 你是指它的中心、它的质心、它的线段还是边界框?
  • 是的,抱歉,该路径的中心,或者一些可以帮助我设置放大的参考
  • 获取路径的 bbox 并将其矩阵添加为 准确 边界框。 developer.mozilla.org/en-US/docs/Web/API/SVGGraphicsElement/…

标签: javascript jquery svg svgpanzoom


【解决方案1】:

回答实际问题:

您使用的插件通过更改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() 做错了。让我们假设链接小提琴中的西班牙位于 165:165。现在要不断正确缩放到该位置,您需要先将其重置:

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

否则该函数要么什么都不做,要么在其他地方缩放。

现在获取“阿根廷”的坐标并缩放到它:

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)

使用示例按钮进行操作: https://jsfiddle.net/04Lg9ruj/

【讨论】:

  • 但是我最初的问题是在按钮上找到路径西班牙的坐标,而不是通过点击地图,我只是把光标功能显示坐标改变
【解决方案2】:

如果对附加事件侦听器的方式进行一点小改动呢?

改为附加到每个国家/地区?

    $(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);

});

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

尝试最初循环遍历所有具有类 '.map-hover-svg' 的元素,并将它们的左上角左下角添加到字典/哈希表中,键为 id,然后您可以使用以下方法引用这些字典项event.target.id。

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

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

【讨论】:

  • 似乎坐标很好,但我将如何在我的按钮上实现它以使其缩放到国家?
  • 嘿克里斯蒂安,我已经开始了,检查小提琴:jsfiddle.net/negy8md1 让我知道?我认为我们只需要对当前平移位置进行更多数学修正。
  • 好像如果你放大它然后按下按钮你就不会再以国家为中心了
  • 是的,我需要弄清楚如何通过对当前平移位置等进行一些数学校正来适应这种情况。我认为这是我们必须将您的函数放入的地方: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}) 等。但我需要花更多的时间来理解这一点,然后再把它放进去。我希望今天晚些时候有时间试试:)
  • 嘿克里斯蒂安,我尝试创建一个包含乡村路径元素坐标、高度和宽度的字典,它似乎有效! jsfiddle.net/ct89f0pj/2
【解决方案3】:

实际上你很幸运,我开发了一个 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 过滤器动画来突出显示目标文本。

【讨论】:

    猜你喜欢
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-03
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-13
    相关资源
    最近更新 更多