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

允我心安 提交于 2021-01-05 08:39:20

问题


Here is a fiddle of what actually have. https://jsfiddle.net/Lofdujwr/

I'm using a library for zoom and pan an SVG svgpanzoom. I have a button when clicked it zooms on spain for example, so I put the coordinates in:

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

The problem is when I resize the page that coordinates doens't work anymore I need to recalculate them, I have found a function that might work but I don't know how to use it:

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

And another question, is it possible to get coordinates from a path ID inside the svg for example?

EDIT: Seems like I have to calculate the actual X and Y viewport from my svg and then recalculate it giving my point (x:188, y: 185) on 0.0 viewport, anyone know any example I can see?


回答1:


To answer the actual question:

The plugin you are using is controlling the transforms by changing the matrix of g.svg-pan-zoom_viewport and not the viewBox of the svg.

In your function cursorPoint() you keep transforming the mouse-coordinates referring to the svg, yet you discard the underlying transformation on g.svg-pan-zoom_viewport. That is the reason why you are getting different coordinates while the svg is either resized or moved (=transformed).

If you refer the coordinates to the g.svg-pan-zoom_viewport instead, you will get consistent results.

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

Another way would be to change the viewBox of the svg instead of using the groups matrix. Yet since your plugin works that way, you should go with it.

Update

I played around with that linked plugin a bit and for me the function zoomAtPoint() is doing something wrong. Let us assume Spain in the linked fiddle is at 165:165. Now to constantly zoom to that location correctly you need to reset it before:

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

Else the function either does nothing or zooms somewhere else.

Now to get the coordinates of "argentinia" and zoom to it:

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)

Working fiddle with example buttons: https://jsfiddle.net/04Lg9ruj/




回答2:


What about a small change to how you attach the event listener?

Attach to each country instead?

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

});

This way the event is only fired when you click those red countries. Also, the coordinates of the click seem accurate to me, I played around with it and I got the values expected.

Tried to initially loop through all elements with the class '.map-hover-svg' and add their top right bottom left to a dictionary/hash-table with the key as the id, then you can reference these dictionary items using the event.target.id.

Then you can use the offset of the svg element's top and left properties and half the width and height of the country path elements to always zoom to the middle of the country clicked:

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



来源:https://stackoverflow.com/questions/59579575/how-to-get-x-y-coordinates-from-path-element-inside-svg

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!