Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend the final state of the first animation for translated element

I know this is asked multiple times here, but I am asking for a different approach for these kind of animations.

Problem:

The 2nd box has multiple animations, trying to create the same effect as 1st one. It seems like the transform property is getting overwritten (as it should)*. I am trying to extend the 1st animation (of 2nd box) with the properties of 2nd animation. Tried to use animation-fill-mode: forwards but with no success. Maybe I am missing something basic. Is it possible with vanilla CSS?

*Reference

The ‘animation-name’ property defines a list of animations that apply. If multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.


Requirement:

Separate move2-right and move2-down key-frames rules but work on the same element, preserving the 1st animation transformation. If there is an alternate approach to this kind of animation, please guide me through it.


Current output:

.animation-1,
.animation-2 {
  width: 200px;
  height: 200px;
  display: inline-block;
  background: white;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.movable-1,
.movable-2 {
  background: #41A186;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  text-align: center;
  vertical-align: middle;
  line-height: 50px;
}
.movable-1 {
  -webkit-animation-name: move1;
  animation-name: move1;
  -webkit-animation-duration: 4s;
  animation-duration: 4s;
  -webkit-animation-delay: 0s;
  animation-delay: 0s;
  -webkit-animation-fill-mode: forwards;
  animation-fill-mode: forwards;
}
.movable-2 {
  -webkit-animation-name: move2-right, move2-down;
  animation-name: move2-right, move2-down;
  -webkit-animation-duration: 2s, 2s;
  animation-duration: 2s, 2s;
  -webkit-animation-delay: 4s, 6s;
  animation-delay: 4s, 6s;
  -webkit-animation-fill-mode: forwards, forwards;
  animation-fill-mode: forwards, forwards;
}
@-webkit-keyframes move1 {
  0% {
    -webkit-transform: translateX(0px);
    transform: translateX(0px);
  }
  50% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
  100% {
    -webkit-transform: translateX(30px) translateY(50px);
    transform: translateX(30px) translateY(50px);
  }
}
@keyframes move1 {
  0% {
    -webkit-transform: translateX(0px);
    transform: translateX(0px);
  }
  50% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
  100% {
    -webkit-transform: translateX(30px) translateY(50px);
    transform: translateX(30px) translateY(50px);
  }
}
@-webkit-keyframes move2-right {
  0% {
    -webkit-transform: translateX(0px);
    transform: translateX(0px);
  }
  50% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
  100% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
}
@keyframes move2-right {
  0% {
    -webkit-transform: translateX(0px);
    transform: translateX(0px);
  }
  50% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
  100% {
    -webkit-transform: translateX(30px);
    transform: translateX(30px);
  }
}
@-webkit-keyframes move2-down {
  0% {
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
  }
  50% {
    -webkit-transform: translateY(50px);
    transform: translateY(50px);
  }
  100% {
    -webkit-transform: translateY(50px);
    transform: translateY(50px);
  }
}
@keyframes move2-down {
  0% {
    -webkit-transform: translateY(0px);
    transform: translateY(0px);
  }
  50% {
    -webkit-transform: translateY(50px);
    transform: translateY(50px);
  }
  100% {
    -webkit-transform: translateY(50px);
    transform: translateY(50px);
  }
}
<div class="animation-1">
  <div class="movable-1">1</div>
</div>
<div class="animation-2">
  <div class="movable-2">2</div>
</div>

Here is a fiddle you can play with: JSfiddle

like image 683
m4n0 Avatar asked Aug 26 '15 10:08

m4n0


People also ask

How do you end an animation in CSS?

CSS animations emit events, so you can use the animationend event to intervene when the animation ends. const element = document. getElementById("element-to-be-animated"); element. addEventListener("animationend", () => { // Set your final state here.

Which property can we use to bind the animation to an element?

The @keyframes Rule To get an animation to work, you must bind the animation to an element.

What is animation-play-state?

The animation-play-state property specifies whether the animation is running or paused. Note: Use this property in a JavaScript to pause an animation in the middle of a cycle.

What property specifies a delay for the start of an animation?

The animation-delay property specifies a delay for the start of an animation. The animation-delay value is defined in seconds (s) or milliseconds (ms).


1 Answers

As far as I am aware this is not possible with pure CSS because (as you have already indicated), when we add an extra transformation rule to an element which already has a transform, the whole transform gets reset because it overwrites and does not append to the existing transform.

With JS this might be possible to achieve but even there it is going to be tough because we have to do the following:

  • Handle the animationend event on completion of the first animation.
  • In the handler, get the translateX(...) in pixels.
  • Get the CSS keyframe rules of the next animation, modify them to take the translateX(...) as the first part of the transform stack.

Note: I assume that you have a case where there is absolutely no way for you to use the first method mentioned in the question.


An alternate method to achieve a similar effect would be to animate the margin or position of an element instead of using transform: translate(). One major downside to this approach is that this would not done at the GPU layer (unlike transform) and hence would be slower when multiple such animations happen at the same time (and could also be expensive).

Using Margins:

The below snippet achieves the effect by animating margin-left and margin-top properties.

.animation-1,
.animation-2,
.animation-3 {
  width: 200px;
  height: 200px;
  display: inline-block;
  background: white;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  vertical-align: middle;
}
.movable-1,
.movable-2,
.movable-3 {
  background: #41A186;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  text-align: center;
  vertical-align: middle;
  line-height: 50px;
}
.movable-1 {
  animation-name: move1;
  animation-duration: 4s;
  animation-delay: 0s;
  animation-fill-mode: forwards;
}
.movable-2 {
  animation-name: move2-right, move2-down;
  animation-duration: 2s, 2s;
  animation-delay: 4s, 6s;
  animation-fill-mode: forwards, forwards;
  animation-timing-function: linear;
}
.movable-3 {
  animation-name: move3-diagonal;
  animation-duration: 4s;
  animation-delay: 8s;
  animation-fill-mode: forwards;
  animation-timing-function: linear;
}
@keyframes move1 {
  0% {
    transform: translateX(0px);
  }
  50% {
    transform: translateX(30px);
  }
  100% {
    transform: translateX(30px) translateY(50px);
  }
}
@keyframes move2-right {
  0% {
    margin-left: 0px;
  }
  100% {
    margin-left: 30px;
  }
}
@keyframes move2-down {
  0% {
    margin-top: 0px;
  }
  100% {
    margin-top: 50px;
  }
}
@keyframes move3-diagonal
 {
  0% {
    margin-top: 0px;
    margin-left: 0px;
  }
  100% {
    margin-top: 50px;
    margin-left: 30px;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="animation-1">
  <div class="movable-1">1</div>
</div>
<div class="animation-2">
  <div class="movable-2">2</div>
</div>
<div class="animation-3">
  <div class="movable-3">3</div>
</div>

Using Position:

This snippet achieves the same effect by animation left and top properties. The child element has position: absolute.

.animation-1,
.animation-2,
.animation-3 {
  width: 200px;
  height: 200px;
  display: inline-block;
  background: white;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  position: relative;
}
.movable-1,
.movable-2,
.movable-3 {
  background: #41A186;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  text-align: center;
  vertical-align: middle;
  line-height: 50px;
  position: absolute;
}
.movable-1 {
  animation-name: move1;
  animation-duration: 4s;
  animation-delay: 0s;
  animation-fill-mode: forwards;
}
.movable-2 {
  animation-name: move2-right, move2-down;
  animation-duration: 2s, 2s;
  animation-delay: 4s, 6s;
  animation-fill-mode: forwards, forwards;
  animation-timing-function: linear;
}
.movable-3 {
  animation-name: move3-diagonal;
  animation-duration: 4s;
  animation-delay: 8s;
  animation-fill-mode: forwards;
  animation-timing-function: linear;
}
@keyframes move1 {
  0% {
    transform: translateX(0px);
  }
  50% {
    transform: translateX(30px);
  }
  100% {
    transform: translateX(30px) translateY(50px);
  }
}
@keyframes move2-right {
  0% {
    left: 0px;
  }
  100% {
    left: 30px;
  }
}
@keyframes move2-down {
  0% {
    top: 0px;
  }
  100% {
    top: 50px;
  }
}
@keyframes move3-diagonal {
  0% {
    top: 0px;
    left: 0px;
  }
  100% {
    top: 50px;
    left: 30px;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="animation-1">
  <div class="movable-1">1</div>
</div>
<div class="animation-2">
  <div class="movable-2">2</div>
</div>
<div class="animation-3">
  <div class="movable-3">3</div>
</div>

Note: As described in my answer here, you can of-course add a wrapper element and set the move-down animation to it. This would produce the same effect as the first one but I am not suggesting that approach because it is against the very point of your question (which in my opinion was - how to add multiple animations to the same element and make the second start from where the first ended).

.animation-1,
.animation-2 {
  width: 200px;
  height: 200px;
  display: inline-block;
  background: white;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
}
.movable-1,
.movable-2 {
  background: #41A186;
  width: 50px;
  height: 50px;
  box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
  text-align: center;
  vertical-align: middle;
  line-height: 50px;
}
.movable-1 {
  animation-name: move1;
  animation-duration: 4s;
  animation-delay: 0s;
  animation-fill-mode: forwards;
}
.movable-2 {
  animation-name: move2-right;
  animation-duration: 2s;
  animation-delay: 4s;
  animation-fill-mode: forwards;
}
.movable-2-wrapper {
  animation-name: move2-down;
  animation-duration: 2s;
  animation-delay: 6s;
  animation-fill-mode: forwards;
}
@keyframes move1 {
  0% {
    transform: translateX(0px);
  }
  50% {
    transform: translateX(30px);
  }
  100% {
    transform: translateX(30px) translateY(50px);
  }
}
@keyframes move2-right {
  0% {
    transform: translateX(0px);
  }
  50% {
    transform: translateX(30px);
  }
  100% {
    transform: translateX(30px);
  }
}
@keyframes move2-down {
  0% {
    transform: translateY(0px);
  }
  50% {
    transform: translateY(50px);
  }
  100% {
    transform: translateY(50px);
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="animation-1">
  <div class="movable-1">1</div>
</div>
<div class="animation-2">
  <div class='movable-2-wrapper'>
    <div class="movable-2">2</div>
  </div>
</div>
like image 145
Harry Avatar answered Oct 05 '22 20:10

Harry