Search code examples
svgsvg-animate

Is it possible create animation where Html tag will follow svg path animation?


I am creating custom progress animation in javascript using svg path. I get data from backend about how much path should move. It is working fine but in that place where animation finish I need some text with percentage. I know it is possible to do it with svg text but that is limited in styling. (I can style it only as svg) So my question is: Is it possible create animation where Html tag for example p will follow svg path animation? Or any other ideas how I could fix it? Svg path doesn't have regular shape.


Solution

  • I have made two examples. The first one is "only" using SVG elements, the next has a <foreignObject> with a HTML <span>.

    In this example the <text> is following an animate motion path. The attributes keyPoints and end must be updated to make the text follow the stroke-dasharray:

    var path = document.getElementById('path');
    var text = document.getElementById('text');
    var motion = document.getElementById('motion');
    
    document.forms.rangeform.range.addEventListener('change', e => {
      path.style.strokeDasharray = `${e.target.value} 100`;
      text.textContent = `${e.target.value}%`;
      motion.attributes['keyPoints'].value = `${e.target.value/100};${e.target.value/100}`;
      motion.attributes['end'].value = `${e.target.value}ms`;
    });
    <form name="rangeform">
      <input type="range" name="range" min="0" max="100" value="75">
    </form>
    <svg viewBox="0 0 120 80" width="400" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <path id="path2" d="M 10 50 C 20,50 25,30 50,30 S 80,50 80,50"
          pathLength="100" stroke-linecap="round" fill="none" />
      </defs>
      <use href="#path2" stroke-dasharray="none" stroke="lightblue" stroke-width="2" />
      <use href="#path2" id="path" stroke-dasharray="75 100" stroke-dashoffset="0"
        stroke="navy" stroke-width="4" />
      <text style="font-family:sans-serif;font-size:8px;font-weight:bold;"
        transform="translate(0 -5)">
        <tspan id="text">75%</tspan>
        <animateMotion id="motion" rotate="0" begin="0s" dur="100ms"
          end="75ms" keyPoints=".75;.75" keyTimes="0;1" fill="freeze">
          <mpath href="#path2"/>
        </animateMotion>
      </text>
    </svg>

    Here I have made an example with <foreignObject>. To make it move I had to create a parent element (<g>) because I'm not able to do an animation on the <foreignObject> itself.

    var path = document.getElementById('path');
    var text = document.getElementById('text');
    var motion = document.getElementById('motion');
    
    document.forms.rangeform.range.addEventListener('change', e => {
      path.style.strokeDasharray = `${e.target.value} 100`;
      text.textContent = `${e.target.value}%`;
      motion.attributes['keyPoints'].value = `${e.target.value/100};${e.target.value/100}`;
      motion.attributes['end'].value = `${e.target.value}ms`;
    });
    span {
      font-size: 8px;
      font-family: sans-serif;
      font-weight: bold;
    }
    <form name="rangeform">
      <input type="range" name="range" min="0" max="100" value="75">
    </form>
    <svg viewBox="0 0 120 80" width="400" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <path id="path2" d="M 10 50 C 20,50 25,30 50,30 S 80,50 80,50"
          pathLength="100" stroke-linecap="round" fill="none" />
      </defs>
      <use href="#path2" stroke-dasharray="none" stroke="lightblue" stroke-width="2" />
      <use href="#path2" id="path" stroke-dasharray="75 100" stroke-dashoffset="0"
        stroke="navy" stroke-width="4" />
      <g>
        <foreignObject width="30" height="20" transform="translate(0 -20)">
          <span id="text" xmlns="http://www.w3.org/1999/xhtml">75%</span>
        </foreignObject>  
        <animateMotion id="motion" rotate="0" begin="0s" dur="100ms"
          end="75ms" keyPoints=".75;.75" keyTimes="0;1" fill="freeze">
          <mpath href="#path2"/>
        </animateMotion>
      </g>
    </svg>