4

When my page loads, the content in the viewport is displayed (content not in view get's a hidden class on the container) and does an animation (flipInX). Then when the user scrolls down the page the hidden class is removed from the container and the content fade's (using fadeIn()) onto the page and is supposed to then do the animation.

My problem is that it's fading (fadeIn()) onto the page, doing the animation, and then doing the fadeIn() again.

EDIT: I found on some browsers (Firefox and Chrome) it does an extra "bounce" at the end, instead of fading in the second time (Safari)

JSFiddle

HTML

<body class="homepage">
  <main id="main" class="main" role="main" tabindex="-1">
    <div class="main-3">
      <article class="post flipInX animated" data-delay="2">
        <h1 class="h-1">Service 1</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
      <article class="post flipInX animated" data-delay="4">
        <h1 class="h-1">Service 2</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
      <article class="post flipInX animated" data-delay="6">
        <h1 class="h-1">Service 3</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
      <article class="post flipInX animated" data-delay="8">
        <h1 class="h-1">Service 4</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
      <article class="post flipInX animated" data-delay="10">
        <h1 class="h-1">Service 5</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
      <article class="post flipInX animated" data-delay="12">
        <h1 class="h-1">Service 6</h1>
        <div class="entry">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vestibulum, lectus id sollicitudin mattis, quam tortor lobortis orci, sed iaculis mi nulla non ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec non felis mollis, congue neque eu, rhoncus libero. Nunc ut mattis ante.</div>
      </article>
    </div>
  </main>
</body>

CSS

* {
  border: 0;
  margin: 0;
  padding: 0;
  -webkit-appearance: none;
  -webkit-border-radius: 0;
}
*,
*:before,
*:after {
  box-sizing: border-box;
}
.hidden {
  visibility: hidden;
}
.main {
  position: relative;
  display: inline-block;
  height: auto;
  width: 98%;
  margin: 40px auto 0 auto;
  text-align: left;
}
.main.minify {
  margin-top: 150px;
}
.main-3 {
  min-height: 400px;
  font-size: 16px;
  line-height: 24px;
}
[class|=h] {
  margin-bottom: 42px;
  font-size: 30px;
  font-weight: normal;
  line-height: 1;
}
.animated {
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  -webkit-animation-fill-mode: both;
  animation-fill-mode: both
}
.animated.bounceIn,
.animated.bounceOut,
.animated.flipOutX,
.animated.flipOutY {
  -webkit-animation-duration: .75s;
  animation-duration: .75s
}
@-webkit-keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0
  }
  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1
  }
  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg)
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px)
  }
}
@keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0
  }
  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1
  }
  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg)
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px)
  }
}
.flipInX {
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipInX;
  animation-name: flipInX
}
.homepage .main-3 {
  width: 80%;
  margin: 0 auto;
  text-align: center;
}
.homepage .post {
  display: inline-block;
  width: 49%;
  margin-bottom: 40px;
  vertical-align: top;
  -webkit-text-stroke: 0.5px;
}
.homepage .post .h-1 {
  font-size: 26px;
  line-height: 110%;
  margin: 14px 0 10px;
  text-align: center;
}
.homepage .post .entry {
  padding: 0 10%
}

JS

function isScrolledIntoView(elem) {
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();

  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

  return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
$(document).ready(function() {
  $('.post').each(function(index) {
    var delay = $(this).attr('data-delay');
    if (typeof delay !== typeof undefined && delay !== false) {
      $(this).css('animation', 'flipInX 2s .' + delay + 's');
      $(this).css('-webkit-animation', 'flipInX 2s .' + delay + 's');
    }
  });
  $('.post').not('.post:first').each(function() {
    if (!isScrolledIntoView($(this))) {
      $(this).addClass('hidden');
    }
  });
  $(document).on('scroll', function() {
    $('.post.hidden').each(function() {
      if (isScrolledIntoView($(this))) {
        $(this).removeClass('hidden').css({
          'display': 'none'
        }).fadeIn();
      }
    });
  });
});
Draven
  • 1,417
  • 2
  • 15
  • 42

2 Answers2

3

The visual result you describe (fade in, then animate) can be accomplished better by using just keyframe animation without relying on jQuery's fadeIn.

I changed a bunch of things and came up with a pretty natural animation. Although this may not be what you expect, you can use it as a basis to get closer to the result you want (while using a bit less code).

This is what I improved/changed:

  • Replaced the function that detects visible elements with a more reliable method. (See below for references)
  • Used css animation-state property and a bit of javascript to control the initial state and playback of every animation
  • Delays are now assigned to previously hidden elements on every scroll.
  • For the fade in part I leveraged existing keyframes in flipInX. There you can tweak the opacity value for better results
  • Moved some animation parameters out of the javascript to the CSS. (e.g animation-duration)
  • When the flipInX animation ends, the element stays on the last frame by using animation-fill-mode: forwards.

Sources consulted:

You can see the result in this fiddle: https://jsfiddle.net/wazaraki/czwrgqdq/

New code with comments (updated):

$(function() {
  /** Reccomended technique for detecting visible elements 
      take from https://stackoverflow.com/a/7557433/4405615 
      Removed width detection since we only need the heights */
  function isScrolledIntoView (el) {

    //special bonus for those using jQuery
    if (typeof jQuery === "function" && el instanceof jQuery) {
        el = el[0];
    }

    var rect = el.getBoundingClientRect();

    return (
        rect.top >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) /*or $(window).height() */
    );
}
/* Not longer needed as we now assign delays dinamically on scroll (see below) */
/*  $('.post').each(function(index) {
    var delay = $(this).data('delay');
    if (typeof delay !== typeof undefined && delay !== false) {
      //$(this).css('animation-delay', delay + 's');
      //$(this).css('-webkit-animation-delay', delay + 's');
    }
  });*/
  
  var delay = 0;
  $('.post').each(function() {
    var post = $(this);
    if (isScrolledIntoView(post)) {
      /* start animation for visible elements 
         see the CSS for more info */
      post.css({
          '-webkit-animation-delay': delay + 's',
         'animation-delay': delay + 's',
         'webkit-animation-play-state': 'running',
         'animation-play-state': 'running'
         });
      delay += 0.2;
    } else {
      post.addClass('hidden');
    }
  });
  $(document).on('scroll', function() {
    /** every round starts with 0s delay so following scrolls 
        can start animating immediately */
    var delay = 0;
    $('.post.hidden').each(function() {
      if (isScrolledIntoView(this)) {
        $(this).removeClass('hidden').css({
          '-webkit-animation-delay': delay + 's',
         'animation-delay': delay + 's',
         'webkit-animation-play-state': 'running',
         'animation-play-state': 'running'
         });
        delay += 0.2;
      }
    });
  });
});
* {
  border: 0;
  margin: 0;
  padding: 0;
  -webkit-appearance: none;
  -webkit-border-radius: 0;
}

*,
*:before,
*:after {
  box-sizing: border-box;
}

/** this class doesn't really do anything, just used it for
    marking invisible elements. You could use data
    attributes instead, for example */
.hidden {}

.main {
  position: relative;
  display: inline-block;
  height: auto;
  width: 98%;
  margin: 40px auto 0 auto;
  text-align: left;
}

.main-3 {
  min-height: 400px;
  font-size: 16px;
  line-height: 24px;
}

[class|=h] {
  margin-bottom: 42px;
  font-size: 30px;
  font-weight: normal;
  line-height: 1;
}

.animated {
  -webkit-animation-duration: 1s;
  animation-duration: 1s;
  /** once the animation finishes we stay on the last
      keyframe */
  -webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;
}

@-webkit-keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }
  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in
  }
  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1;
  }
  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
    opacity: 1;
  }
}

@keyframes flipInX {
  0% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
    opacity: 0;
  }
  40% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
    -webkit-animation-timing-function: ease-in;
    animation-timing-function: ease-in;
  }
  60% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
    opacity: 1;
  }
  80% {
    -webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
    transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
  }
  100% {
    -webkit-transform: perspective(400px);
    transform: perspective(400px);
    opacity: 1;
  }
}

.flipInX {
  -webkit-backface-visibility: visible !important;
  backface-visibility: visible !important;
  -webkit-animation-name: flipInX;
  animation-name: flipInX;
  -webkit-animation-duration: 2s;
  animation-duration: 2s;
  /* this is a nice way to control animation playback */
  -webkit-animation-play-state: paused;
  animation-play-state: paused;
}

.homepage .main-3 {
  width: 80%;
  margin: 0 auto;
  text-align: center;
}

.homepage .post {
  display: inline-block;
  width: 49%;
  margin-bottom: 40px;
  vertical-align: top;
  -webkit-text-stroke: 0.5px;
  opacity: 0;
}

.homepage .post .h-1 {
  font-size: 26px;
  line-height: 110%;
  margin: 14px 0 10px;
  text-align: center;
}

.homepage .post .entry {
  padding: 0 10%
}
Community
  • 1
  • 1
Wazaraki
  • 454
  • 2
  • 7
  • Wow, nice answer! Seems to work great except there is no delay on the `.post` elements that are in view when the page loads (on Safari). – Draven Mar 03 '16 at 22:13
  • I added delay also for the items initially visible. On top of that, some `-webkit-` prefixes for Safari support were missing, so I added them. Please see my updated anwser and fiddle. It should hopefully work better now. – Wazaraki Mar 04 '16 at 03:56
-1

The flipInX animation is also animating the opacity of the element between 0% and 60%. Try removing the references to opacity in the animation.