When doing front-end UI effects, animating elements based on their scrolling position is a very popular design, often using third-party plugins or libraries. In this tutorial, I’ll teach you how to do it using pure JavaScript and CSS.

Here’s a preview of the implementation:

We use CSS for the animations and JavaScript for the styles needed to trigger them. Let’s start by creating the layout.

Create a layout

We create the page layout using HTML, and then assign a generic class name to the elements that we want to animate, which the JavaScript uses to locate those elements. Here we specify js-scroll as the class name for the element that needs to be animated according to the scroll, and the HTML code is as follows:

<section class="scroll-container">
  <div class="scroll-element js-scroll"></div>
  <div class="scroll-caption">This animation fades in from the top.</div>
</section>
Copy the code

Add CSS styles

Let’s start with a simple fade animation:

.js-scroll {
  opacity: 0;
  transition: opacity 500ms;
}

.js-scroll.scrolled {
  opacity: 1;
}
Copy the code

All jS-Scroll elements on the page will be hidden with an opacity of 0. When scrolling to the element area, add the.scrolled class name to it to make it visible.

Manipulating elements with JavaScript

With layout and style in hand, we now need to write a JavaScript function that assigns class names to elements as they scroll into the view.

Let’s break down the logic briefly:

  1. Get all on the pagejs-scrollThe element
  2. Make these elements fade out by default
  3. Checks if the element is in the window
  4. Allocates if the element is inside the windowscrolledThe name of the class

Get the target element

Get all js – scroll elements on a page, use the document. The querySelectorAll (a) :

const scrollElements = document.querySelectorAll('.js-scroll')
Copy the code

All target elements fade out by default

Iterate over these elements to fade them all out:

scrollElements.forEach((el) = > {
  el.style.opacity = 0
})
Copy the code

Checks if the element is in the window

We can check if an element is in the user window by determining whether the distance between the element and the top of the page is less than the height of the visible part of the page.

In JavaScript, we use the getBoundingClientRect().top method to get the distance between the element and the top of the page, Using the window. The innerHeight or document. The documentElement. ClientHeight to obtain the height of the window.

We will create an elementInView function using the above logic:

const elementInView = (el) = > {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <= (window.innerHeight || document.documentElement.clientHeight)
  )
}
Copy the code

We can modify this function to detect whether the element scrolls x pixels to the page, or to detect the percentage of page scrolling.

const elementInView = (el, scrollOffset = 0) = > {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <=
    (window.innerHeight || document.documentElement.clientHeight) - scrollOffset
  )
}
Copy the code

In this case, this function returns true if the element has been scrolled to the page by the number of scrolloffSets. Let’s change the parameter scrollOffset to a percentage:

const elementInView = (el, percentageScroll = 100) = > {
  const elementTop = el.getBoundingClientRect().top

  return (
    elementTop <=
    (window.innerHeight || document.documentElement.clientHeight) *
      (percentageScroll / 100))}Copy the code

This section can define the logic according to its own specific needs.

Note: You can use the Intersection Observer API[2] to achieve the same effect, but it does not support IE.

Add the class name to the element

Now that we can detect if the element has been rolled to the page, we need to define a function to handle the display of the element — in this case we display the element by assigning the scrolled class name.

const displayScrollElement = (element) = > {
  element.classList.add('scrolled')}Copy the code

Then, we’ll combine our previous logic with the displayScrollElement function and call that function on all jS-Scroll elements using the forEach method.

const handleScrollAnimation = () = > {
  scrollElements.forEach((el) = > {
    if (elementInView(el, 100)) {
      displayScrollElement(el)
    }
  })
}
Copy the code

In addition, to reset an element to its default state when it is no longer in the view, we can define a hideScrollElement to do this:

const hideScrollElement = (element) = > {
  element.classList.remove("scrolled");
};

const handleScrollAnimation = () = > {
  scrollElements.forEach((el) = > {
    if (elementInView(el, 100)) {
      displayScrollElement(el);
    } else{ hideScrollElement(el); }}Copy the code

Finally, we’ll pass the above method to the window’s scroll event listener so that it runs every time the user scrolls.

window.addEventListener('scroll'.() = > {
  handleScrollAnimation()
})
Copy the code

We have implemented all the functions of scrolling animation.

Improve the sample

Please go back to the beginning of the article to see the renderings. As you can see, these elements appear in different animations. This is done by assigning different CSS animations to class names. The HTML for this example looks like this:

<section class="scroll-container">
  <div class="scroll-element js-scroll fade-in"></div>
  <div class="scroll-caption">Fade in dynamic effect</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll fade-in-bottom"></div>
  <div class="scroll-caption">Cut into the top dynamic effect</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll slide-left"></div>
  <div class="scroll-caption">Cut in the action effect from the left</div>
</section>
<section class="scroll-container">
  <div class="scroll-element js-scroll slide-right"></div>
  <div class="scroll-caption">Cut in the action effect from the right</div>
</section>
Copy the code

Here we assign different CSS class names to the elements with different dynamic effects. Here is the CSS code for these classes:

.scrolled.fade-in {
  animation: fade-in 1s ease-in-out both;
}

.scrolled.fade-in-bottom {
  animation: fade-in-bottom 1s ease-in-out both;
}

.scrolled.slide-left {
  animation: slide-in-left 1s ease-in-out both;
}

.scrolled.slide-right {
  animation: slide-in-right 1s ease-in-out both;
}

@keyframes slide-in-left {
  0% {
    transform: translateX(-100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1; }}@keyframes slide-in-right {
  0% {
    transform: translateX(100px);
    opacity: 0;
  }
  100% {
    transform: translateX(0);
    opacity: 1; }}@keyframes fade-in-bottom {
  0% {
    transform: translateY(50px);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1; }}@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1; }}Copy the code

Although we added different animation elements, we didn’t need to change the JavaScript code because the logic remained the same. This means we can add any number of different animations to the page without having to write new functions.

Use throttle to improve performance

Whenever we bind a function in the scroll listener, that function is called every time the user scrolls the page. Scrolling a 500px page results in a function being called at least 50 times. If we try to include a lot of elements on the page, it will cause our page to slow down significantly.

You can reduce the number of calls by using the “Throttle Function.” A throttling function is a higher-order function that calls the passed function only once in a specified time interval.

It’s especially useful for scroll events, because we don’t need to detect every pixel the user scrolls. For example, if we have a throttling function with a timer of 100ms, this function will be called only once for every 100ms scroll by the user.

The throttling function can be implemented in JavaScript like this:

let throttleTimer = false

const throttle = (callback, time) = > {
  if (throttleTimer) return

  // Mark this so that the function will not be executed twice
  throttleTimer = true

  setTimeout(() = > {
    At the specified time, the incoming callback function is called
    callback()
    throttleTimer = false
  }, time)
}
Copy the code

Then we can modify the Scroll event listener on the window object:

window.addEventListener('scroll'.() = > {
  throttle(handleScrollAnimation, 250)})Copy the code

Now our handleScrollAnimation function is called every 250ms as the user scrolls:

To finish.

Reference:

  1. Developer.mozilla.org/en-US/docs/…
  2. Developer.mozilla.org/en-US/docs/…