26

I am trying to chain together 2 CSS animations, see pen: http://codepen.io/jdesilvio/pen/pjwvyO

I have followed the syntax provided in other answers to similar questions, but they don't seem to work correctly:

animation-name: falling, laydown;
animation-duration: 2000ms, 1000ms;
animation-timing-function: ease-in, ease-out;
animation-iteration-count: 1, 1;

It is playing the second animation, then the first and finally the second one again. How can I get it to play the first, then second?

Here is the full code:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

html, body {
  height: 100%;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

@keyframes falling {
  0% {
    transform: translate3d(0, -400px, 0);
  }
  100% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(30deg)
      rotateY(0deg)
      rotateZ(60deg);
  }
}

@keyframes laydown {
  0% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(30deg)
      rotateY(0deg)
      rotateZ(60deg);
  }
  100% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(70deg)
      rotateY(0deg)
      rotateZ(80deg);
  }
}

#falling-card-parent {
  height: 150px;
  width: 100px;
  margin: auto;
  perspective: 1000px;
}

#falling-card {
  height: 150px;
  width: 100px;
  background-color: black;
  margin: auto;
  transform:
    translate3d(0, 40%, 0)
    rotateX(70deg)
    rotateY(0deg)
    rotateZ(80deg);
  animation-name:
    falling, laydown;
  animation-duration:
    2000ms, 1000ms;
  animation-timing-function:
    ease-in, ease-out;
  animation-iteration-count:
    1, 1;
}

</style>
<html>
  <body>
    <div id="falling-card-parent">
      <div id="falling-card"></div>
    </div>
  </body>
</html>

2 Answers 2

36

The problem is actually not with the order of the animations but because of how multiple animations on pme element works. When multiple animations are added on an element, they start at the same time by default.

Because of this, both the laydown and falling animations start at the same time but the laydown animation actually completes within 1000ms from the start but the first animation (which is falling) doesn't complete till 2000ms.

The W3C spec about animations also say the following about multiple animations accessing the same property during animation:

If multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.

In the code provided in question, both animations are trying to modify the transform property and the second animation is the closest to the end. So while the second animation is still running (which is, for the first 1000ms) the transform changes are applied as specified in the second animation. During this time the first animation is still running but it has no effect because its values are overwritten. In the 2nd 1000ms (when the second animation has already completed but 1st is still executing), the transforms are applied as directed by the first animation. This is why it looks as if the second animation is running before the first animation and then the first.


To fix this problem, the execution of the second animation should be put on hold (or delayed) until the time the first animation is complete. This can be done by adding a animation-delay (that is equal to the animation-duration of the first animation) for the second animation.

animation-name: falling, laydown;
animation-duration: 2000ms, 1000ms;
animation-delay: 0ms, 2000ms; /* add this */
animation-timing-function: ease-in, ease-out;
animation-iteration-count: 1, 1;

html,
body {
  height: 100%;
}
body {
  display: flex;
  align-items: center;
  justify-content: center;
}
@keyframes falling {
  0% {
    transform: translate3d(0, -400px, 0);
  }
  100% {
    transform: translate3d(0, 40%, 0) rotateX(30deg) rotateY(0deg) rotateZ(60deg);
  }
}
@keyframes laydown {
  0% {
    transform: translate3d(0, 40%, 0) rotateX(30deg) rotateY(0deg) rotateZ(60deg);
  }
  100% {
    transform: translate3d(0, 40%, 0) rotateX(70deg) rotateY(0deg) rotateZ(80deg);
  }
}
#falling-card-parent {
  height: 150px;
  width: 100px;
  margin: auto;
  perspective: 1000px;
}
#falling-card {
  height: 150px;
  width: 100px;
  background-color: black;
  margin: auto;
  transform: translate3d(0, 40%, 0) rotateX(70deg) rotateY(0deg) rotateZ(80deg);
  animation-name: falling, laydown;
  animation-duration: 2000ms, 1000ms;
  animation-delay: 0ms, 2000ms;
  animation-timing-function: ease-in, ease-out;
  animation-iteration-count: 1, 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div id="falling-card-parent">
  <div id="falling-card"></div>
</div>

Sign up to request clarification or add additional context in comments.

3 Comments

Doesn't work well with infinite animations.
IMHO, You can use 1-liner shorthand here, and is probably less confusing since all the properties for one element are together. First number is duration, second number delay. e.g.: animation: myFirstAnim 1s linear, mySecondAnim 1s ease 1s;. See MDN animation.
@MichaelScofield - It could work if your animations act on different elements. For example - if this were falling, pulsating text, you could wrap the text in a div, make the div fall while the text element inside it pulsates infinitely. Or, in this case, OP could have done the same thing with making the falling-card-parent div fall and then reshape the falling-card after the delay.
2

html, body {
  height: 100%;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

@keyframes falling {
  0% {
    transform: translate3d(0, -400px, 0);
  }
  100% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(30deg)
      rotateY(0deg)
      rotateZ(60deg);
  }
}

@keyframes laydown {
  0% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(30deg)
      rotateY(0deg)
      rotateZ(60deg);
  }
  100% {
    transform:
      translate3d(0, 40%, 0)
      rotateX(70deg)
      rotateY(0deg)
      rotateZ(80deg);
  }
}

#falling-card-parent {
  height: 150px;
  width: 100px;
  margin: auto;
  perspective: 1000px;
}

#falling-card {
  height: 150px;
  width: 100px;
  background-color: black;
  margin: auto;
  transform:
    translate3d(0, 40%, 0)
    rotateX(70deg)
    rotateY(0deg)
    rotateZ(80deg);
  animation-name:
    laydown, falling;
  animation-duration:
    2000ms, 1000ms;
  animation-timing-function:
    ease-in, ease-out;
  animation-iteration-count:
    1, 1;
}
    <div id="falling-card-parent">
      <div id="falling-card"></div>
    </div>

Just reverse this:

 animation-name:
    falling, laydown;

to

 animation-name:
    laydown, falling;

Here: http://jsfiddle.net/hfxopjjy/

2 Comments

@jdesilvio: Let me know if this helped you.
While switching the order does work for this case, that is not the ideal approach to be used and would end up confusing future editors of the code also. Ideal approach is to make the second animation wait till the time it takes for the first one to complete.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.