Home > Software engineering >  How to animate text with css based on event listener scroll
How to animate text with css based on event listener scroll

Time:12-30

I want to make a text animation like this website: https://www.viraly.com.br/ They have 3 keyframes for two types of animation Left to Right and Right to Left RTL start with 164vh LTR start with -200vh

When those texts are visible the animation starts base on scroll

You can check the final result here: https://hartech.vercel.app/ But I want the same text animation as this website https://www.viraly.com.br/

Can someone provide me some tutorial or help me with the code to achieve that?

This is what I have tried on NextJs component:

export function HartechAnimated() {
  if (typeof window !== "undefined") {

    const animateElementRTL = document.querySelectorAll('.animateRTL');
    const animateElementLTR = document.querySelectorAll('.animateLTR');

    function isScrolledIntoView(elementsToBeAnimated) {
      elementsToBeAnimated.forEach((element) => {
        const rect = element.getBoundingClientRect();
        const elemTop = rect.top;
        const elemBottom = rect.bottom;

        // Only completely visible elements return true:
        const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);

        if (isVisible && element.classList.contains('animateRTL')) {
          element.classList.add('resetXRTL')
        } else {
          element.classList.remove('resetXRTL')
        }

        if (isVisible && element.classList.contains('animateLTR')) {
          element.classList.add('resetXLTR')
        } else {
          element.classList.remove('resetXLTR')
        }
      });
    }

    window.addEventListener('scroll', () => {

      isScrolledIntoView(animateElementRTL);
      isScrolledIntoView(animateElementLTR);

    }, false);
  }

  return (
    <div className="font-britanica-black-expanded overflow-hidden text-6xl text-white py-20 md:text-black md:text-9xl md:py-36">
      <b className="block animateLTR" id="teste">HARTECH REPARO</b>
      <b className="block hartech-stroke animateRTL">DE PLACAS DE VÍDEO</b>
      <b className="block animateLTR">HARTECH REPARO</b>
    </div >
  );
}

CSS

.animateRTL {
  transition: transform 2.2s ease-in-out;
  transform: translateX(164vh);
}

.animateLTR {
  transition: transform 2s ease-in-out;
  transform: translateX(-200vh);
}

.resetXRTL{
  transform: translateX(0);
}

.resetXLTR{
  transform: translateX(0);
}

CodePudding user response:

Honestly rather than writing all that code to check if the element is in the viewport, you'd be better served using an IntersectionObserver (much less painful). https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API


const animateElementRTL = Array.from(document.querySelectorAll('.animateRTL'));
const animateElementLTR = Array.from(document.querySelectorAll('.animateLTR'));


const animateOnEntry = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio === 0) {
      // completely out of viewport, stop animation
      if (entry.target.classList.contains('animateRTL')) {
        entry.target.classList.remove('resetXRTL');      
      } else {
        entry.target.classList.remove('resetXLTR');
      }
    } else if (entry.intersectionRatio === 1) {
      // fully visible in viewport, start animation
      if (entry.target.classList.contains('animateRTL')) {
        entry.target.classList.add('resetXRTL');      
      } else {
        entry.target.classList.add('resetXLTR');
      }
    }
  });
}


const rtlObservers = animateElementRTL.map((elem) => {
  const observer = new IntersectionObserver(animateOnEntry, {
    root: null,
    rootMargin: "0px",
    threshold: [0, 1]
  });
  observer.observe(elem);
  return observer;
});

const ltrObservers = animateElementLTR.map((elem) => {
  const observer = new IntersectionObserver(animateOnEntry, {
    root: null,
    rootMargin: "0px",
    threshold: [0, 1]
  });
  observer.observe(elem);
  return observer;
});

CodePudding user response:

Thanks for the answer however I still not having the same animation as this website: https://www.viraly.com.br/ They have like a keyframe based on scroll. The animation that I have is fixed, there's only one animation.

Check this: https://i.imgur.com/ImsImbk.mp4

This is your code fixed:

const animateElementRTL = Array.from(document.querySelectorAll('.animateRTL'));
    const animateElementLTR = Array.from(document.querySelectorAll('.animateLTR'));

    const animateOnEntry = (entries, observer) => {
      entries.forEach((entry) => {
        if (entry.intersectionRatio === 0) {
          // completely out of viewport, stop animation
          if (entry.target.classList.contains('animateRTL')) {
            entry.target.classList.remove('resetXRTL');
          }

          if (entry.target.classList.contains('animateLTR')) {
            entry.target.classList.remove('resetXLTR');
          }
        } else {
          // fully visible in viewport, start animation
          if (entry.target.classList.contains('animateRTL')) {
            entry.target.classList.add('resetXRTL');
          }

          if (entry.target.classList.contains('animateLTR')) {
            entry.target.classList.add('resetXLTR');
          }
        }
      });
    }

    const rtlObservers = animateElementRTL.map((elem) => {
      const observer = new IntersectionObserver(animateOnEntry, {
        root: null,
        rootMargin: "0px",
        threshold: [0, 1]
      });
      observer.observe(elem);
      return observer;
    });

    const ltrObservers = animateElementLTR.map((elem) => {
      const observer = new IntersectionObserver(animateOnEntry, {
        root: null,
        rootMargin: "0px",
        threshold: [0, 1]
      });
      observer.observe(elem);
      return observer;
    });
  }
  • Related