dark mode

CSS animations triggered when elements are visible on-screen

There are great ideas, and examples on the internet for CSS animations, but not so many explain how to make loading page elements appear smoothly and not before they are visible/scrolled.

It’s amazing how the following simple tricks can turn a page from boring and dull to a champion.

The goal is to create a way to implement or test CSS Animations on pages by people that are not developers, such as UX designers and even regular users.

In our case we are using CSS library Animate.css which will add a bunch of predefined animations like "Fade In," "Slide In," etc., but you are free to use others or do your own using @keyframes. We will need jQuery for simplicity, but everything can be done with pure JavaScript too.

Adding the Animate.css gives you a way to trigger animations with adding two HTML classes without having CSS knowledge – “animated” and “animation name.” The first class triggers the animation and the second - the animation type. The unfortunate issue here is that they all start as soon as the page finish loading. So, we are at the point of having a great and powerful tool, but somehow yet unpractical.

We need the option to control the animation's timing (delay) easily and to execute it only when the element is visible on the screen.

How to control animations and delays easily

To do that instead of adding classes like in the Animate.css library we will add custom attributes, for example:

Instead of:

<p class="”fadeIn" animated”></p>

We will add:

<p animation-delay="250" animation-type="fadeIn"></p>

Now, with these custom attributes, we have the needed control, and anyone with basic HTML knowledge can use and be creative.

How to use custom attributes for configuration

First, we will add styles. In our case, I wrote LESS, but it can be easily converted to pure CSS or even to Sass. After that, we will add JavaScript functionality where needed.

The LESS code

// Animations delay
@animation-ms: 0, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 700, 800, 900, 1000, 1100, 1200, 1300,
  1400, 1500, 1600, 1700, 1800, 1900, 2000;

// Adding the delays from the array in CSS selectors
.nvs-animation-delay(@i: 1) when (@i =< length(@animation-ms)) {
  @ms: extract(@animation-ms, @i);
  @selector: ~'[nvs-animation-delay="@{ms}"]';

  @{selector} {
    animation-delay: ~'@{ms}ms';
  }

  .nvs-animation-delay(@i + 1);
}

// Initializing CSS selectors with predefined delay
.nvs-animation-delay();

// Hide animated elements when not on screen
@media (min-width: 768px) {
  [nvs-animation-type] {
    visibility: hidden;
  }
}

// Remove animations on small tablets and mobile devices
@media (max-width: 768px) {
  [nvs-animation-type] {
    animation: none;
    transform: none;
    transition-property: none;
  }
}

// Show elements when on screen
.animated {
  visibility: visible;
}

I’ve decided that it’s pointless to use JavaScript for the delay functionality as no one will need perfection of a millisecond.

As a result, I decided to create an array of time steps @animation-ms. First, in every 50ms and then in 100ms until 2000ms (2 seconds). The idea is that the designer will create animations with a delay in some patterns and won’t need countless variations, but still be flexible if there is a need.

After that, we need to add styles to all animation-delay attributes. In our case, we use a loop to iterate through the @animation-ms array. If you've never used CSS, pre-processor, this can be a little bit overwhelming, but if you convert to CSS, you will see that we are doing it just to minimize redundancy and it’s not black magic.

To create the actual CSS, we need to initialize the loop.

The styles below hide the elements before the beginning of the animation and disable the animation for small devices.

The JavaScript code

///<reference path="../bower_components/jquery/dist/jquery.js" />
(function($, window, document, undefined) {
  'use strict';
  var animationObject;

  function addAnimation() {
    animationObject.each(function(index, element) {
      var $currentElement = $(element),
        animationType = $currentElement.attr('animation-type');

      if (onScreen($currentElement)) {
        $currentElement.addClass('animated ' + animationType);
      }
    });
  }

  // takes jQuery(element) a.k.a. $('element')
  function onScreen(element) {
    // window bottom edge
    var windowBottomEdge = $(window).scrollTop() + $(window).height();

    // element top edge
    var elementTopEdge = element.offset().top;
    var offset = 100;

    // if element is between window's top and bottom edges
    return elementTopEdge + offset <= windowBottomEdge;
  }

  $(window).load(function() {
    animationObject = $('[animation-type]');
    addAnimation();
  });

  $(window).on('scroll', function(e) {
    addAnimation();
  });
})(jQuery, window, document);

We need to write the JavaScript code that finds animation-type’s values and adds them to classes when the element is visible on the screen.

The function addAnimation() adds the needed classes, while onScreen() checks if the element is on the screen.

Conclusion

Adding animations will breathe life into every page. Everything can be changed or extend to fit your needs. Just make sure not to overuse it, because it is considered bad practice.

Happy animating!

Related articles

© 2021 All rights reserved.