Trigger a CSS keyframe animation via scroll

23,484

Solution 1

Add a class to element that has the animation on it when the user scrolls down.

Pseudocode

on window scroll {
  if (scroll pos > x) {
    element.addclass("animateMe");
  }
}

Demo

http://jsbin.com/zuqexigepe/edit?html,output

If you want the animation to happen every time the user scrolls past a certain point, you can simply change the scroll event to remove the class as well, like so:

$(window).scroll(function () {
  if($(window).scrollTop() > 0) {
    element.addClass("animateMe");
  }
  else {
    element.removeClass("animateMe");
  }
});

Solution 2

The posted (and accepted) answer is technically correct, but nowadays there are more performance friendly ways to achieve this. If you want to trigger animations on more elements than one, watching these multiple elements can result in bad performance. See for example this question. disclaimer: This question also has my answer, I will only copy my own work in this anwser:

To solve this in a modern, performant way it's best to use Intersection Observer (IO) for this.

With IO you can watch one (or multiple) elements and react once they come into view or if they intersect each other.

To use IO you first have to first set the options for it, then define which element(s) to watch and last to define what exactly happens once the IO triggers.

Example (Taken from here), with one minimal modification: The author removed the IO even if the animation didn't happen yet. I moved the unobserve call inside the check if element is visible.

const SELECTOR = '.watched';
const ANIMATE_CLASS_NAME = 'animated';

const animate = element => (
  element.classList.add(ANIMATE_CLASS_NAME)
);

const isAnimated = element => (
  element.classList.contains(ANIMATE_CLASS_NAME)
);

const intersectionObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    
    // when element's is in viewport,
    // animate it!
    if (entry.intersectionRatio > 0) {
      animate(entry.target);
      // remove observer after animation
      observer.unobserve(entry.target);
    }
  });
});

// get only these elements,
// which are not animated yet
const elements = [].filter.call(
  document.querySelectorAll(SELECTOR),
  element => !isAnimated(element, ANIMATE_CLASS_NAME)
);
//console.log(elements);

// start observing your elements
elements.forEach((element) => intersectionObserver.observe(element));
.h100 {
height: 100vh;
}

.watched {
 opacity: 0;
 transition: opacity .5s;
}

.watched.animated {
opacity: 1;
}
<div class="h100">
scroll down
</div>
<div class="watched">
I'm watched
</div>
<div class="h100">
Another element, keep scrolling
</div>
<div class="watched">
I'm also watched
</div>
Share:
23,484
daveycroqet
Author by

daveycroqet

Updated on July 09, 2022

Comments

  • daveycroqet
    daveycroqet almost 2 years

    Say I have an element with an animation:

    #element {
      animation: Fade 3s linear 1s forwards;
    }
    
    @keyframes Fade {
      /* do stuff */
    }
    

    How can I trigger this animation only when the user scrolls down? Vanilla JS, jQuery, ScrollMagic, GSAP/TweenMax, however you want to do it.

    Would adding the animation property itself trigger the effect? So, a user scrolls to a certain point/element, and then I apply something like: $('#element').css('animation', 'Fade 3s linear 1s forwards');?