Text animation on scroll

I’ve stumbled accross this cool text animation and I want to use it on multiple texts. But then I noticed that the effect applies only to one text. I changed the ids for each text, but still it works just for one. Any ideas what should I change in the code so it would work on multiple texts?

var textPath = document.querySelector('#text-path');

var textContainer = document.querySelector('#text-container');

var path = document.querySelector( textPath.getAttribute('href') );

var pathLength = path.getTotalLength();
console.log(pathLength);

function updateTextPathOffset(offset){
  textPath.setAttribute('startOffset', offset); 
}

updateTextPathOffset(pathLength);

function onScroll(){
  requestAnimationFrame(function(){
    var rect = textContainer.getBoundingClientRect();
    var scrollPercent = rect.y / window.innerHeight;
    console.log(scrollPercent);
    updateTextPathOffset( scrollPercent * 2 * pathLength );
  });
}

window.addEventListener('scroll',onScroll);

You can find the code here

https://codepen.io/team/keyframers/pen/NWKyNqK

Thanks

Answer

I created two 2 text blocks and then used a for loop & iterate over the animation of keyframes. Here are some snippets:

  • Create multiple id's for all elements – better to use numeric values
  • Use a common function and attach the similar event handler for those using the id's inside an iterator
  • Create an array of functions and attach those to the window.addEventListener("scroll", func);

You can check here: https://codepen.io/imtantri/pen/NWdoQpg

let allFuncs = [];

function updateTextPathOffset(textPath, offset) {
   textPath.setAttribute("startOffset", offset);
}

for (let i = 1; i < 3; i++) {
  let textPath = document.querySelector("#text-path-" + i.toString());
  let textContainer = document.querySelector("#text-container-" + i.toString());
  let pathSelector = textPath.getAttribute("href");
  let path = document.querySelector(pathSelector);
  let pathLength = path.getTotalLength();

  updateTextPathOffset(textPath, pathLength);

  allFuncs.push(() => {
    requestAnimationFrame(function () {
      var rect = textContainer.getBoundingClientRect();
      var scrollPercent = rect.y / window.innerHeight;
      updateTextPathOffset(textPath, scrollPercent * 2 * pathLength);
    });
  });
}

for (let func of allFuncs) {
  window.addEventListener("scroll", func);
}
<p><strong>Enjoy the scroll 👇</strong></p>

<p>Inspired by <a href="https://www.nytimes.com/interactive/2019/07/22/us/politics/elizabeth-warren-selfies.html">this article</a>. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent bibendum, lorem vel tincidunt imperdiet, nibh elit laoreet felis, a bibendum nisl tortor non orci. Donec pretium fermentum felis, quis aliquet est rutrum ut. Integer quis massa ut lacus viverra pharetra in eu lacus. Aliquam tempus odio adipiscing diam pellentesque rhoncus. Curabitur a bibendum est. Mauris vehicula cursus risus id luctus. Curabitur accumsan venenatis nibh, non egestas ipsum vulputate ac. Vivamus consectetur dolor sit amet enim aliquet eu scelerisque ipsum hendrerit. Donec lobortis suscipit vestibulum. Nullam luctus pellentesque risus in ullamcorper. Nam neque nunc, mattis vitae ornare ut, feugiat a erat. Ut tempus iaculis augue vel pellentesque.</p>

<svg id="text-container-1" viewBox="0 0 1000 200" xmlns="http://www.w3.org/2000/svg">
  <path id="text-curve-1" d="M0 100s269.931 86.612 520 0c250.069-86.612 480 0 480 0" fill="none"/>
  
  <text y="40" font-size="32">
    <textPath id="text-path-1" href="#text-curve-1" startOffset="200">
      * Keyframers 1 really know animation.
    </textPath>
  </text>
</svg>

<svg id="text-container-2" viewBox="0 0 1000 200" xmlns="http://www.w3.org/2000/svg">
  <path id="text-curve-2" d="M0 100s269.931 86.612 520 0c250.069-86.612 480 0 480 0" fill="none"/>
  
  <text y="40" font-size="32">
    <textPath id="text-path-2" href="#text-curve-2" startOffset="200">
      * Keyframers 2 really know animation.
    </textPath>
  </text>
</svg>

<p>Inspired by <a href="https://www.nytimes.com/interactive/2019/07/22/us/politics/elizabeth-warren-selfies.html">this article</a>. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent bibendum, lorem vel tincidunt imperdiet, nibh elit laoreet felis, a bibendum nisl tortor non orci. Donec pretium fermentum felis, quis aliquet est rutrum ut. Integer quis massa ut lacus viverra pharetra in eu lacus. Aliquam tempus odio adipiscing diam pellentesque rhoncus. Curabitur a bibendum est. Mauris vehicula cursus risus id luctus. Curabitur accumsan venenatis nibh, non egestas ipsum vulputate ac. Vivamus consectetur dolor sit amet enim aliquet eu scelerisque ipsum hendrerit. Donec lobortis suscipit vestibulum. Nullam luctus pellentesque risus in ullamcorper. Nam neque nunc, mattis vitae ornare ut, feugiat a erat. Ut tempus iaculis augue vel pellentesque. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent bibendum, lorem vel tincidunt imperdiet, nibh elit laoreet felis, a bibendum nisl tortor non orci. Donec pretium fermentum felis, quis aliquet est rutrum ut. Integer quis massa ut lacus viverra pharetra in eu lacus. Aliquam tempus odio adipiscing diam pellentesque rhoncus. Curabitur a bibendum est. Mauris vehicula cursus risus id luctus. Curabitur accumsan venenatis nibh, non egestas ipsum vulputate ac. Vivamus consectetur dolor sit amet enim aliquet eu scelerisque ipsum hendrerit. Donec lobortis suscipit vestibulum. Nullam luctus pellentesque risus in ullamcorper. Nam neque nunc, mattis vitae ornare ut, feugiat a erat. Ut tempus iaculis augue vel pellentesque.</p>

Update

If you want text to be in reverse, you can introduce a custom attribute for svg element like: data-offsetchange="" as shown for the two text:

<textPath id="text-path-1" href="#text-curve-1" startOffset="200" data-offsetchange="2">
  * Keyframers 1 really know animation.
</textPath>

<textPath id="text-path-2" href="#text-curve-2" startOffset="200"  data-offsetchange="-2">
  * Keyframers 2 really know animation.
</textPath>

And use it in your function for multiplier:

let offsetChanger =  textPath.dataset.offsetchange;

...
updateTextPathOffset(textPath, scrollPercent * offsetChanger * pathLength);