javascriptsvgecmascript-6

Get absolute coordinates of element inside SVG using JS


Good time forum users. I apologize in advance for my English. I could not find the answer (I decided to ask the English-speaking audience).

Absolute positioning (coordinates) of an element nested in groups relative to the parent container ().

<svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" tabindex="1">
  <g transform="translate(100 100)">
       <g transform="translate(100 100)"> 
            <circle r="50" cx="25" cy="25" fill="yellow" />
       </g>
  </g>
<svg>

I would like to get using ES6 + circle coordinates relative to SVG. That is x = 100 + 100 + 25, y = 100 + 100 + 25 for .

How can I get these coordinates? (can be up to an infinite nesting of groups). Thanks for helping.

enter image description here


Solution

    1. Find the cx and cy values of the circle
    2. Apply any transform that the circle has
    3. Step up to each ancestor element and apply any tranforms found
    4. Stop when you reach the root SVG element

    function getCirclePosition(circleElemId)
    {
      var elem = document.getElementById(circleElemId);
      var svg = elem.ownerSVGElement;
    
      // Get the cx and cy coordinates
      var pt = svg.createSVGPoint();
      pt.x = elem.cx.baseVal.value;
      pt.y = elem.cy.baseVal.value;
    
      while (true)
      {
        // Get this elements transform
        var transform = elem.transform.baseVal.consolidate();
        // If it has a transform, then apply it to our point
        if (transform) {
          var matrix = elem.transform.baseVal.consolidate().matrix;
          pt = pt.matrixTransform(matrix);
        }
        // If this element's parent is the root SVG element, then stop
        if (elem.parentNode == svg)
          break;
        // Otherwise step up to the parent element and repeat the process
        elem = elem.parentNode;
      }
      return pt;
    }
    
    
    var pos = getCirclePosition("thecircle");
    console.log("Coordinates are: " + pos.x + "," + pos.y);
    <svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" tabindex="1">
      <g transform="translate(100 100)">
           <g transform="translate(100 100)"> 
                <circle id="thecircle" r="50" cx="25" cy="25" fill="yellow" />
           </g>
      </g>
    <svg>

    Update

    As @Vad0k pointed out, there is a simpler, but slighlty less accurate approach, that can be used instead:

    function getCirclePosition(circleElemId)
    {
      var elem = document.getElementById(circleElemId);
      var svg = elem.ownerSVGElement;
    
      // Get the cx and cy coordinates
      var pt = svg.createSVGPoint();
      pt.x = elem.cx.baseVal.value;
      pt.y = elem.cy.baseVal.value;
    
      return pt.matrixTransform(getTransformToElement(elem, svg));
    }
    
    
    function getTransformToElement(fromElement, toElement) {
      return toElement.getCTM().inverse().multiply(fromElement.getCTM());
    };
    
    
    var pos = getCirclePosition("thecircle");
    console.log("Coordinates are: " + pos.x + "," + pos.y);
    <svg width="100%" height="100%" viewBox="0 0 1000 1000" preserveAspectRatio="xMidYMin slice" x="0" y="0" tabindex="1">
      <g transform="translate(100 100)">
           <g transform="translate(100 100)"> 
                <circle id="thecircle" r="50" cx="25" cy="25" fill="yellow" />
           </g>
      </g>
    <svg>