Learn how to create text animations using CSS triggered by a scroll event listener

I'm looking to create a text animation similar to the one on this website:

They have 3 keyframes for two types of animations: Left to Right and Right to Left.

The RTL animation starts at 164vh, while LTR starts at -200vh.

These text animations only trigger when the texts become visible during scrolling.

You can see my attempt at replicating this animation here:

However, I am still struggling to achieve the same effect as seen on . Can anyone provide me with a tutorial or help me with the necessary code?

This is what I've implemented in my NextJs component:

insert your modified JavaScript code here...

CSS

insert your modified CSS code here...

Answer №1

Thank you for your response, but I am still unable to achieve the same animation effect as seen on this website: . They seem to have a keyframe animation based on scroll movement.

You can see my current animation here:

Here is the revised version of your code:

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;
    });
  }

Answer №2

If you're tired of writing lengthy code to check if an element is in the viewport, consider using an IntersectionObserver instead (much easier). 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) {
      // 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;
});

Edit: To create a 'stepped' CSS animation like what you're seeing, use the `threshold` array and `rootMargin` property in the `IntersectionObserver` options object.

For instance, you can step the animation based on the element's visibility percentage on the screen by:

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

This will trigger an intersection event, allowing you to apply different CSS classes based on the `intersectionRatio` values at 0, 0.2, 0.5, 0.8, 1.

You could also adjust the starting point of these events beyond the viewport edge by setting a root margin (e.g., a root margin of `2em, 0, 2em, 0` would shift the thresholds calculation starting from 2em (32px) inward from the top and bottom viewport edges).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Interactive Timekeeping Tool with AngularJS Alarm Feature

I have successfully implemented the functionality to display the system time and date. Additionally, I have set up textboxes for users to input the time they want to set as their first alarm. My current goal is to trigger a particular action once the syst ...

Issue with Bootstrap checkbox buttons not rendering properly

I'm attempting to utilize the checkbox button feature outlined on the bootstrap webpage here in the "checkbox" subsection. I copied and pasted the html code (displayed below) from that page into a jsfiddle, and checkboxes are unexpectedly appearing in ...

Efficient utilization of the bootstrap form-group for optimal responsiveness

I've encountered an issue with the form-group class in Bootstrap. You can view the problem on this link. After analyzing the problem, I found that when viewing the code on a small or medium-sized device, the space between "Statut" and "Etat" appears ...

Encountered an error while attempting to insert a new user into a MySQL database using the Node, Express, and MySQL package

Currently in the process of creating a CRUD API using NodeJS. My main goal at the moment is to execute a POST request to add a new user to the database. The issue I am facing is that although I can successfully add a new user to the database, my server cra ...

Passing a JSON file name as an argument to a command line in Node.js

When I run app.js using the command node app.js, it will execute const inputData = require('./input.json'); Now, my question is - can I pass the file name as an argument to const inputData = require('./file.json'); directly from the co ...

A practical guide to troubleshooting typescript errors when exporting a map component

I encountered a typescript error when trying to export the map component with GoogleApiWrapper. It works fine when not wrapped in GoogleApiWrapper, but all my attempts to resolve the issue have failed. I would appreciate it if someone could review the code ...

jQuery fails to fetch information

I am currently working with a straightforward script as shown below: $(function() { var url = theme_directory + '/func/api.php'; $.get( url, function(data) { alert("Data Loaded: " + data); }); }); Here is the code for api ...

Discovering the unique identifier of an item in Node.js is simple with these steps

I have a delete API which requires the item's unique ID to be passed in for deletion. How can I capture the ID of the item being deleted and send it to the API? Here is the API: app.post("/delete_product", function (req, res) { var data = { ...

Is it possible to adjust the size of a carousel image without compromising its quality?

In the carousel currently displayed on my website, there are three images that are enclosed within the following HTML tag: Despite exploring various online solutions, I have not been able to resolve the issue. Adjusting the height and width settings has o ...

When trying to return a string value from array.find(), I encountered an error stating that Objects cannot be used as a React child

The code snippet below shows that console.log(el.distance) is equal to a string. Despite this, whenever I attempt to return el.distance, React throws the error: Error: Objects are not valid as a React child (found: object with keys {item, distance}). If y ...

Maintaining the integrity of a list within a for loop

I have a challenge where I need to display 3 elements in cards on each row from a list of elements. The issue with my current code is that it only displays the first two elements and then the loop stops. Here is the code snippet using ReactJS and Materia ...

Node.js: Extract the object's name and value that are sent from the frontend

I'm in the process of creating a microservice using nodejs. The request is returning the following JSON. { "distCd": "abcd", "distName": "parentLife Distributor (TOD)", "stateCd": "", "subdistInd": false, "maindistInd": true ...

JavaScript encountered a problem when trying to call the inline function

I created a function called controls that adds HTML elements dynamically into the specified div. function controls() { var block = $('#controls'); block.html(''); block.append('<button type="button" onClick="f ...

Steps to retrieve a value from a promise function

My current challenge involves a function that verifies whether a device is online. Take a look at the code snippet below. const ping = require('ping'); export function checkDeviceStatus(device) { try { const hosts = [device]; let resul ...

What could be the reason that my submenus are not appearing as expected?

I'm currently working on refining the navigation bar design for a website, but I've encountered a problem with the submenu functionality. It appears that only one submenu is visible while the others remain hidden. For example, when navigating to ...

Tips for combining Nuxt with Vue Cli 3

section: My current setup involves utilizing Nuxt.js to set up my Vue application. I'm curious if it's feasible to incorporate Nuxt into a Vue Cli 3 project in order to leverage the advantages of both platforms. Surprisingly, there doesn't ...

Activate the class using Vue with the v-for directive

I'm curious about the v-for functionality. Why is it necessary to use this.activeClass = {...this.activeClass} to update the component? I noticed that the component does not update after this line of code. if (this.activeClass[index]) { ...

Trigger .gif on hover using ng-repeat in AngularJS

Many solutions to this problem involve using jQuery, such as the following examples: Stop a gif animation onload, on mouseover start the activation and Animating a gif on hover. However, I'm interested in achieving the same functionality using Angular ...

Transferring data from one function to another function

I'm currently working on a role-based access application where I've integrated user roles into JWT tokens. Upon decoding the token, I retrieve information such as id and role. I have implemented a function that checks for the token within the x-a ...

Implementing dynamic class bindings with Vue.js using v-for

I am currently using a v-for loop in Vue.js to create a list of items populated with data from an API. Here is an example of the items array: items: [ { foo: 'something', number: 60 }, { foo: 'anything', ...