DEV Community

Sergej Skrjanec
Sergej Skrjanec

Posted on • Edited on

How to Implement Page Scroll Animations with Animista

In this tutorial, we will explore how we can add a bit of flair to a website by animating elements onto the page based on their viewport visibility. We will do so with the help of Animista - a CSS animations on-demand library and Scrollama - a lightweight JavaScript library for scrollytelling based on Intersection Observer API.

Prerequisites:

  • Basic knowledge of HTML
  • Basic knowledge of CSS/SCSS
  • Basic knowledge of JavaScript
  • Basic knowledge of terminal (node, npm)

We won't be focusing on the design aspect, so go ahead and download this free Agency bootstrap theme. It looks nice, it is responsive and will make for an excellent starting point. The project runs on Gulp so make sure Node.js is installed on your development machine. It is already set up with Sass and Browsersync which will refresh our browser in the background whenever we make any changes in the code.

Once you have downloaded the theme, fire up the terminal, navigate to the project root folder, install the dependencies and run the project by running the following commands in the terminal:

$~ npm install
$~ npm start
Enter fullscreen mode Exit fullscreen mode

If everything went well you should be looking at the theme on your machine's default browser. Next, go ahead and open the theme folder in your editor of choice (e.g. VS Code).

Structuring the Content

Before we proceed, let's stop for a moment and think about our page HTML structure.

We will be grouping all animated elements by the animation used. Basically, we will use some kind of a wrapper element where we can define the animation that is going to be applied to its children. Let's give this wrapper element the animista class.

Furthermore, we will be animating only some of the children elements, so we need another CSS class for this purpose. We can use that same class for controlling the animation delay as well. In our case, we will be using anim-delay-{number} class. By doing so, we gain a certain degree of control over animation delay values in a simple, declarative manner.

Finally, we will be triggering our animations by adding the appear class to animista wrapper. If this sounds confusing โ€” don't worry, it will make more sense later on.

Elements Structure - Animated elements are wrapped inside the 'animista' element

Elements Structure - Animated elements are wrapped inside the 'animista' element

Laying the groundwork

Let's begin by creating animista.scss file inside the scss folder. We will write all of our animation related CSS code here. We want all animated elements to be initially hidden. This is where the CSS attribute selector comes in handy, as it allows us to target all elements that have class attribute value containing anim-delay-. Let's write our SCSS code like this:

// Our wrapper element
.animista {

  // Hide all animated elements
  [class*="anim-delay-"] {
    opacity: 0;
  }

}
Enter fullscreen mode Exit fullscreen mode

Next, open up the agency.scss file in the scss folder and import the animista.scss file that we have just created:

// ...

// Animista
@import "animista.scss";
Enter fullscreen mode Exit fullscreen mode

Time to test if everything works. Let's assign our newly created classes to an element on our page. Open the main index.html file in the project root folder and change the Services section heading and subheading like so:

...
<!-- The following layer is the animista wrapper -->
<div class="col-lg-12 text-center animista">
  <!-- This is the animated element -->
  <h2 class="section-heading text-uppercase anim-delay-0">Services</h2>
  <!-- This is the animated element too, only with a delay -->
  <h3 class="section-subheading text-muted anim-delay-1">Lorem ipsum dolor sit amet consectetur.</h3>
</div>
...
Enter fullscreen mode Exit fullscreen mode

Excellent, the heading and subheading are now both hidden. Let's try and animate those two elements with simple slide-in animation. Head on over to Animista entrances category and select the 'fade-in-bottom' animation. You can tweak the animation options in the options panel. Great thing about Animista is that you can grab only the code for the animation you actually plan on using. So once you are happy, click the generate code button at the top right of the main stage (the round button with the curly braces icon). We don't need the auto-prefixed option so you can turn that off.

Animista - Generate Code Button

Animista - Generate Code Button

Animista - Autoprefixer Toggle and Copy Keyframes Buttons

Animista - Autoprefixer Toggle and Copy Keyframes Buttons

Copy the animation keyframes code and paste it into animista.scss file at the end. You can copy the animation class code too, but we will modify it slightly.

// Our wrapper element
.animista {

  // Hide all animated elements
  [class*="anim-delay-"] {
    opacity: 0;
  }

  // Appearing animations
  &.appear {

    // Fade in bottom animation
    &.fade-in-bottom [class*="anim-delay-"]  {
      animation-name: fade-in-bottom 0.6s cubic-bezier(0.230, 1.000, 0.320, 1.000) both;
    }

  }

}

/* ----------------------------------------------
 * Generated by Animista on 2020-1-29 20:13:59
 * Licensed under FreeBSD License.
 * See http://animista.net/license for more info. 
 * w: http://animista.net, t: @cssanimista
 * ---------------------------------------------- */

/**
 * ----------------------------------------
 * animation fade-in-bottom
 * ----------------------------------------
 */
@keyframes fade-in-bottom {
  0% {
    transform: translateY(50px);
    opacity: 0;
  }
  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

Enter fullscreen mode Exit fullscreen mode

Connecting the Pieces

We now have everything set up. Next thing we need to do is include the Scrollama library. Add the following code at the end of the index.html, right before the closing </body> element, after all other <script> tags.

...
<body>
  ...

  <!-- Scrollama and intersection observer polyfill -->
  <script src="https://unpkg.com/intersection-observer@0.5.1/intersection-observer.js"></script>
  <script src="https://unpkg.com/scrollama@2.1.4/build/scrollama.js"></script>

  <!-- Our page scroll animation code -->
  <script src="js/scroll-anim.js"></script>

</body>
...
Enter fullscreen mode Exit fullscreen mode

Note that we don't yet have the scroll-anim.jsfile, so let's create it now in the project's js folder and add the following code:

var scroller = scrollama();

scroller
  .setup({
    step: '.animista',
    offset: 0.85
  })
  .onStepEnter(function(response) {
    response.element.classList.add('appear');
  });

window.addEventListener('resize', scroller.resize);
Enter fullscreen mode Exit fullscreen mode

Basically, we are telling Scrollama to add the appear class to all animista wrapper elements when they scroll into the viewport. The default offset is 0.5 which means the trigger is set to the center of the viewport. We are going to move the threshold further down to 0.85 so that animations get triggered a bit earlier.

Finally, we need to tell our animista wrapper element to use the newly created animation, like so:

<div class="col-lg-12 text-center animista fade-in-bottom">
  ...
</div>
Enter fullscreen mode Exit fullscreen mode

Creating Staggered Animations

And now if we scroll down a bit, we see that the heading and subheading both slide in onto the page. However they do so at the same time and, as discussed earlier, we would like to have some control over animation-delay values. While at it, we can also modify our code so we have some default animation properties and are able to over-ride them should we decide to.

.animista {

  // Hide all animated elements
  // ...

  // Appearing animations
  &.appear {

    // Default animation options
    [class*="anim-delay-"]{
      animation-duration: 0.75s;
      animation-timing-function: cubic-bezier(0.230, 1.000, 0.320, 1.000);
      animation-fill-mode: both;
    }

    // Fade in bottom animation
    &.fade-in-bottom [class*="anim-delay-"]  {
      animation-name: fade-in-bottom;
    }

    // Let's add animation delays so we can
    // create staggered animation effect
    .anim-delay-1 { animation-delay: 0.1s; }
    .anim-delay-2 { animation-delay: 0.2s; }
    .anim-delay-3 { animation-delay: 0.3s; }
    .anim-delay-4 { animation-delay: 0.4s; }
    .anim-delay-5 { animation-delay: 0.5s; }
    .anim-delay-6 { animation-delay: 0.6s; }
    .anim-delay-7 { animation-delay: 0.7s; }

  }

}
Enter fullscreen mode Exit fullscreen mode

We are almost there, let's try adding more animated elements inside the Services section. Note how we are only adding animista and anim-delay-{number} classes to create a nice staggered animation effect.

...
<!-- Services -->
<section class="page-section" id="services">
  <div class="container">
    <div class="row">
      <!-- The following layer will is the animista wrapper -->
      <div class="col-lg-12 text-center animista fade-in-bottom">
        <!-- This is the animated element -->
        <h2 class="section-heading text-uppercase anim-delay-0">Services</h2>
        <!-- This is the animated element too, only with a delay -->
        <h3 class="section-subheading text-muted anim-delay-1">Lorem ipsum dolor sit amet consectetur.</h3>
      </div>
    </div>
    <div class="row text-center">
      <div class="col-md-4 animista fade-in-bottom">
        <span class="fa-stack fa-4x anim-delay-0">
          <i class="fas fa-circle fa-stack-2x text-primary"></i>
          <i class="fas fa-shopping-cart fa-stack-1x fa-inverse"></i>
        </span>
        <h4 class="service-heading anim-delay-1">E-Commerce</h4>
        <p class="text-muted anim-delay-2">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
      </div>
      <div class="col-md-4 animista fade-in-bottom">
        <span class="fa-stack fa-4x anim-delay-2">
          <i class="fas fa-circle fa-stack-2x text-primary"></i>
          <i class="fas fa-laptop fa-stack-1x fa-inverse"></i>
        </span>
        <h4 class="service-heading anim-delay-3">Responsive Design</h4>
        <p class="text-muted anim-delay-4">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
      </div>
      <div class="col-md-4 animista fade-in-bottom">
        <span class="fa-stack fa-4x anim-delay-4">
          <i class="fas fa-circle fa-stack-2x text-primary"></i>
          <i class="fas fa-lock fa-stack-1x fa-inverse"></i>
        </span>
        <h4 class="service-heading anim-delay-5">Web Security</h4>
        <p class="text-muted anim-delay-6">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Minima maxime quam architecto quo inventore harum ex magni, dicta impedit.</p>
      </div>
    </div>
  </div>
</section>
...
Enter fullscreen mode Exit fullscreen mode

Perfect, this looks much better! Now, how about we try using different animation for portfolio items. Head on over to Animista again, grab only the keyframes code for the 'tilt-in-br' animation and paste it at the end of animista.scss.

// ...

/* ----------------------------------------------
 * Generated by Animista on 2020-1-29 21:14:0
 * Licensed under FreeBSD License.
 * See http://animista.net/license for more info. 
 * w: http://animista.net, t: @cssanimista
 * ---------------------------------------------- */

/**
 * ----------------------------------------
 * animation tilt-in-br
 * ----------------------------------------
 */
@keyframes tilt-in-br {
  0% {
    transform: rotateY(-35deg) rotateX(-20deg) translate(250px, 250px) skew(12deg, 15deg);
    opacity: 0;
  }
  100% {
    transform: rotateY(0) rotateX(0deg) translate(0, 0) skew(0deg, 0deg);
    opacity: 1;
  }
}

Enter fullscreen mode Exit fullscreen mode

Next, add the tilt-in-br class after the fade-in-bottom and before the delay definitions:

.animista {
  // ...

  // Appearing animations
  &.appear {

    // Default animation options
    // ...

    // Slide in blurred animation
    // ...

    // Tilt in BR animation
    &.tilt-in-br [class*="anim-delay-"] {
      animation-name: tilt-in-br;
    }

    // Let's add animation delays so we can
    // create staggered animation effect
    // ...

  }
}
Enter fullscreen mode Exit fullscreen mode

We also need to update the Portfolio Grid section in our index.html:

...
<!-- Portfolio Grid -->
<section class="bg-light page-section" id="portfolio">
  <div class="container">
    <div class="row">
      <div class="col-lg-12 text-center animista tilt-in-br">
        <h2 class="section-heading text-uppercase anim-delay-0">Portfolio</h2>
        <h3 class="section-subheading text-muted anim-delay-1">Lorem ipsum dolor sit amet consectetur.</h3>
      </div>
    </div>
    <div class="row animista tilt-in-br">
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-0">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal1">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/01-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Threads</h4>
          <p class="text-muted">Illustration</p>
        </div>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-1">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal2">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/02-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Explore</h4>
          <p class="text-muted">Graphic Design</p>
        </div>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-2">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal3">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/03-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Finish</h4>
          <p class="text-muted">Identity</p>
        </div>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-3">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal4">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/04-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Lines</h4>
          <p class="text-muted">Branding</p>
        </div>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-4">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal5">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/05-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Southwest</h4>
          <p class="text-muted">Website Design</p>
        </div>
      </div>
      <div class="col-md-4 col-sm-6 portfolio-item anim-delay-5">
        <a class="portfolio-link" data-toggle="modal" href="#portfolioModal6">
          <div class="portfolio-hover">
            <div class="portfolio-hover-content">
              <i class="fas fa-plus fa-3x"></i>
            </div>
          </div>
          <img class="img-fluid" src="img/portfolio/06-thumbnail.jpg" alt="">
        </a>
        <div class="portfolio-caption">
          <h4>Window</h4>
          <p class="text-muted">Photography</p>
        </div>
      </div>
    </div>
  </div>
</section>
...
Enter fullscreen mode Exit fullscreen mode

Cool! Let's add another round of animations. This time we will get the keyframes code for the 'flip-in-hor-top' animation and add it at the end of the animista.scss.

// ...

/* ----------------------------------------------
 * Generated by Animista on 2020-1-29 21:26:34
 * Licensed under FreeBSD License.
 * See http://animista.net/license for more info. 
 * w: http://animista.net, t: @cssanimista
 * ---------------------------------------------- */

/**
 * ----------------------------------------
 * animation flip-in-hor-top
 * ----------------------------------------
 */
@keyframes flip-in-hor-top {
  0% {
    transform: rotateX(-80deg);
    opacity: 0;
  }
  100% {
    transform: rotateX(0);
    opacity: 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

Add the animation class code too:

.animista {
  // ...

  // Appearing animations
  &.appear {

    // Default animation options
    // ...

    // Slide in blurred animation
    // ...

    // Tilt in BR animation
    // ...

    // Flip in horizontal top animation
    &.flip-in-hor-top [class*="anim-delay-"] {
      animation-name: flip-in-hor-top;
    }

    // Let's add animation delays so we can
    // create staggered animation effect
    // ...

  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, update the About section in our index.html file (simply add our CSS classes where needed):

...
<!-- About -->
<section class="page-section" id="about">
  <div class="container">
    <div class="row">
      <div class="col-lg-12 text-center animista fade-in-bottom">
        <h2 class="section-heading text-uppercase anim-delay-0">About</h2>
        <h3 class="section-subheading text-muted anim-delay-1">Lorem ipsum dolor sit amet consectetur.</h3>
      </div>
    </div>
    <div class="row">
      <div class="col-lg-12">
        <ul class="timeline">
          <li class="animista flip-in-hor-top">
            <div class="timeline-image anim-delay-0">
              <img class="rounded-circle img-fluid" src="img/about/1.jpg" alt="">
            </div>
            <div class="timeline-panel anim-delay-1">
              <div class="timeline-heading">
                <h4>2009-2011</h4>
                <h4 class="subheading">Our Humble Beginnings</h4>
              </div>
              <div class="timeline-body">
                <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p>
              </div>
            </div>
          </li>
          <li class="timeline-inverted animista flip-in-hor-top">
            <div class="timeline-image anim-delay-0">
              <img class="rounded-circle img-fluid" src="img/about/2.jpg" alt="">
            </div>
            <div class="timeline-panel anim-delay-1">
              <div class="timeline-heading">
                <h4>March 2011</h4>
                <h4 class="subheading">An Agency is Born</h4>
              </div>
              <div class="timeline-body">
                <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p>
              </div>
            </div>
          </li>
          <li class="animista flip-in-hor-top">
            <div class="timeline-image anim-delay-0">
              <img class="rounded-circle img-fluid" src="img/about/3.jpg" alt="">
            </div>
            <div class="timeline-panel anim-delay-1">
              <div class="timeline-heading">
                <h4>December 2012</h4>
                <h4 class="subheading">Transition to Full Service</h4>
              </div>
              <div class="timeline-body">
                <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p>
              </div>
            </div>
          </li>
          <li class="timeline-inverted animista flip-in-hor-top">
            <div class="timeline-image anim-delay-0">
              <img class="rounded-circle img-fluid" src="img/about/4.jpg" alt="">
            </div>
            <div class="timeline-panel anim-delay-1">
              <div class="timeline-heading">
                <h4>July 2014</h4>
                <h4 class="subheading">Phase Two Expansion</h4>
              </div>
              <div class="timeline-body">
                <p class="text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt ut voluptatum eius sapiente, totam reiciendis temporibus qui quibusdam, recusandae sit vero unde, sed, incidunt et ea quo dolore laudantium consectetur!</p>
              </div>
            </div>
          </li>
          <li class="timeline-inverted animista flip-in-hor-top">
            <div class="timeline-image anim-delay-0">
              <h4>Be Part
                <br>Of Our
                <br>Story!</h4>
            </div>
          </li>
        </ul>
      </div>
    </div>
  </div>
</section>
...
Enter fullscreen mode Exit fullscreen mode

From this point on, you can add just about any animation from the Animista entrances category without much effort. The JS code stays the same, only the HTML and the CSS code need updating.

Let's demonstrate this by adding a final round of animations to the team section. This time however, let's reuse the 'fade-in-bottom' animation that we already have and only update the Team section in our index.html like so:

...
<!-- Team -->
<section class="bg-light page-section" id="team">
  <div class="container">
    <div class="row">
      <div class="col-lg-12 text-center animista fade-in-bottom">
        <h2 class="section-heading text-uppercase anim-delay-0">Our Amazing Team</h2>
        <h3 class="section-subheading text-muted anim-delay-1">Lorem ipsum dolor sit amet consectetur.</h3>
      </div>
    </div>
    <div class="row">
      <div class="col-sm-4">
        <div class="team-member animista fade-in-bottom">
          <img class="mx-auto rounded-circle anim-delay-0" src="img/team/1.jpg" alt="">
          <h4 class="anim-delay-1">Kay Garland</h4 >
          <p class="text-muted anim-delay-2">Lead Designer</p>
          <ul class="list-inline social-buttons">
            <li class="list-inline-item anim-delay-3">
              <a href="#">
                <i class="fab fa-twitter"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-4">
              <a href="#">
                <i class="fab fa-facebook-f"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-5">
              <a href="#">
                <i class="fab fa-linkedin-in"></i>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <div class="col-sm-4">
        <div class="team-member animista fade-in-bottom">
          <img class="mx-auto rounded-circle anim-delay-1" src="img/team/2.jpg" alt="">
          <h4 class="anim-delay-2">Larry Parker</h4>
          <p class="text-muted anim-delay-3">Lead Marketer</p>
          <ul class="list-inline social-buttons">
            <li class="list-inline-item anim-delay-4">
              <a href="#">
                <i class="fab fa-twitter"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-5">
              <a href="#">
                <i class="fab fa-facebook-f"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-6">
              <a href="#">
                <i class="fab fa-linkedin-in"></i>
              </a>
            </li>
          </ul>
        </div>
      </div>
      <div class="col-sm-4">
        <div class="team-member animista fade-in-bottom">
          <img class="mx-auto rounded-circle anim-delay-2" src="img/team/3.jpg" alt="">
          <h4 class="anim-delay-3">Diana Pertersen</h4>
          <p class="text-muted anim-delay-4">Lead Developer</p>
          <ul class="list-inline social-buttons">
            <li class="list-inline-item anim-delay-5">
              <a href="#">
                <i class="fab fa-twitter"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-6">
              <a href="#">
                <i class="fab fa-facebook-f"></i>
              </a>
            </li>
            <li class="list-inline-item anim-delay-7">
              <a href="#">
                <i class="fab fa-linkedin-in"></i>
              </a>
            </li>
          </ul>
        </div>
      </div>
    </div>
    <div class="row animista fade-in-bottom">
      <div class="col-lg-8 mx-auto text-center anim-delay-0">
        <p class="large text-muted">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut eaque, laboriosam veritatis, quos non quis ad perspiciatis, totam corporis ea, alias ut unde.</p>
      </div>
    </div>
  </div>
</section>
...
Enter fullscreen mode Exit fullscreen mode

Closing Thoughts

Congratulations, you have made it to the end of the tutorial! Hopefully it gives you an idea on how to liven up your website with neat scroll-based animation effects. A bit of CSS, HTML, and very little JS knowledge gives you lots of creative possibilities. You can try experimenting with different entrance animations from Animista. Careful though, once you get the hang of it, it is fairly easy to overdo it. As ever โ€” use your own judgment.

Resources

Further Reading

If you are just diving into CSS animation, here are a few places that may help you gain better insight and learn basics:

Top comments (0)