Home > Mobile >  Javascript to perform custom placement of svg text
Javascript to perform custom placement of svg text

Time:06-17

I am working with a SVG element as following. In this I am using <textPath></textPath> to place a text on a path.

The svg is following

<!DOCTYPE html>
<html>
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
      <rect  width="1280" height="720" fill="green" rx="35" style="overflow: visible;"></rect>
      <rect  x="70" y="70" width="1090" height="500" fill="green"></rect>
      <g  style="transform: translate(70px, 70px);">
        <path id="WorldAvg"  fill="none" stroke="white" stroke-width="5" opacity="1" d="M0,418.8448266855795L45.416666666666664,414.575606092466L90.83333333333333,411.52106545918446L136.25,407.6362492744353L181.66666666666666,404.77160362539087L227.08333333333334,393.4071256244998L272.5,391.02044947895274L317.9166666666667,386.66829128561017L363.3333333333333,379.8367479839252L408.75,378.1893224105598L454.1666666666667,369.95354728799214L499.5833333333333,368.77725419172447L545,363.8472517451793L590.4166666666666,363.6062134379884L635.8333333333334,357.4943259634553L681.25,351.27652154825586L726.6666666666666,344.34458188257486L772.0833333333334,342.18836536625594L817.5,338.5473949587725L862.9166666666666,336.1670218633692L908.3333333333334,332.6090887556393L953.75,328.1112436453848L999.1666666666666,321.4448005003716L1044.5833333333335,312.9573561957251L1090,306.4131526885112"></path>
        <g >
          <text text-anchor="start" stroke="red">
            <textPath href="#WorldAvg" startOffset="0%">10.3%</textPath>
          </text>
          <text text-anchor="end" stroke="red">
            <textPath href="#WorldAvg" startOffset="100%">24.7%</textPath>
          </text>
        </g>
    </svg>
  </body>
</html>

My end goal is to place each text just above the path so that the text is clearly visible. To achieve that, I tried the following which did not work. the label for 24.7% is pushed far down.

Is there a way to programatically achieve this?

const element = document.querySelectorAll('.avgLbl>text');
const comp = document.querySelector(`.bound>#WorldAvg`);
element.forEach(
    (a, i) => {
        const diff = comp.getBoundingClientRect().bottom - a.getBoundingClientRect().bottom;
        a.setAttribute('transform', `translate(0,${diff})`)
    }
);
<!DOCTYPE html>
<html>
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
      <rect  width="1280" height="720" fill="green" rx="35" style="overflow: visible;"></rect>
      <rect  x="70" y="70" width="1090" height="500" fill="green"></rect>
      <g  style="transform: translate(70px, 70px);">
        <path id="WorldAvg"  fill="none" stroke="white" stroke-width="5" opacity="1" d="M0,418.8448266855795L45.416666666666664,414.575606092466L90.83333333333333,411.52106545918446L136.25,407.6362492744353L181.66666666666666,404.77160362539087L227.08333333333334,393.4071256244998L272.5,391.02044947895274L317.9166666666667,386.66829128561017L363.3333333333333,379.8367479839252L408.75,378.1893224105598L454.1666666666667,369.95354728799214L499.5833333333333,368.77725419172447L545,363.8472517451793L590.4166666666666,363.6062134379884L635.8333333333334,357.4943259634553L681.25,351.27652154825586L726.6666666666666,344.34458188257486L772.0833333333334,342.18836536625594L817.5,338.5473949587725L862.9166666666666,336.1670218633692L908.3333333333334,332.6090887556393L953.75,328.1112436453848L999.1666666666666,321.4448005003716L1044.5833333333335,312.9573561957251L1090,306.4131526885112"></path>
        <g >
          <text text-anchor="start" stroke="red">
            <textPath href="#WorldAvg" startOffset="0%">10.3%</textPath>
          </text>
          <text text-anchor="end" stroke="red">
            <textPath href="#WorldAvg" startOffset="100%">24.7%</textPath>
          </text>
        </g>
    </svg>
  </body>
</html>

CodePudding user response:

You could achieve a baseline offset by adjusting dominant-baseline and dy values like so:

svg {
  display: block;
  height: 100vmin;
}

text {
  font-size: 30px
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
      <rect  width="1280" height="720" fill="green" rx="35" style="overflow: visible;"></rect>
      <rect  x="70" y="70" width="1090" height="500" fill="green"></rect>
      <g  style="transform: translate(70px, 70px);">
        <path id="WorldAvg"  fill="none" stroke="white" stroke-width="5" opacity="1" d="M 0 418.8 L 45.4 414.6 L 90.8 411.5 L 136.3 407.6 L 181.7 404.8 L 227.1 393.4 L 272.5 391 L 317.9 386.7 L 363.3 379.8 L 408.8 378.2 L 454.2 370 L 499.6 368.8 L 545 363.8 L 590.4 363.6 L 635.8 357.5 L 681.3 351.3 L 726.7 344.3 L 772.1 342.2 L 817.5 338.5 L 862.9 336.2 L 908.3 332.6 L 953.8 328.1 L 999.2 321.4 L 1044.6 313 L 1090 306.4"></path>
        <g >
          <text text-anchor="start" dominant-baseline="hanging" dy="10" stroke="red">
            <textPath href="#WorldAvg" startOffset="0%">10.3%</textPath>
          </text>
          <text text-anchor="end" dominant-baseline="hanging" dy="10" stroke="red">
            <textPath href="#WorldAvg" startOffset="100%">24.7%</textPath>
          </text>
        </g>
    </svg>

dominant-baseline="hanging"
will position your text under your <textPath>

dy="10"
allows you to finetune the vertical alignment further:
Quite often capital letters ascenders will collide with your <textPath>.

  • Related