CSS Animations End Together Even Though They Are Started at Different Times

css animations end together even though they are started at different times

Simply because all the animation have already started at the same time. Using visibility:hidden will not prevent the animation to start and make it start later when the element is visible. The same thing will happen with opacity for example:

const boxes = document.querySelectorAll(".box");boxes.forEach(box => {  setTimeout(() => box.classList.remove("hidden"), Math.random() * 5000);});
.container {  width: 100px;  height: 100px;  margin-bottom: 20px;}
.box { width: 100%; height: 100%;}
.box.hidden { opacity: 0.1;}
.box { animation: growIn 5s; animation-timing-function: cubic-bezier(.46, .13, .99, .83); transition: all .2s cubic-bezier(0.215, 0.61, 0.355, 1);}
.container:first-child .box { background-color: green;}
.container:nth-child(2) .box { background-color: orange;}
.container:nth-child(3) .box { background-color: red;}
@keyframes growIn { from { transform: scale(0); } to { transform: scale(1); }}
<div class="container">  <div class="box hidden"></div></div><div class="container">  <div class="box hidden"></div></div><div class="container">  <div class="box hidden"></div></div>

How to synchronize CSS animations that were started/restarted at different times

For this purpose document.getAnimations() is a useful method. It returns an array of all animation objects currently in effect whose target elements are descendants of the document. More information: https://developer.mozilla.org/en-US/docs/Web/API/Document/getAnimations

Items of document.getAnimations() include currentTime property, that can use for syncing animations. More information: https://drafts.csswg.org/web-animations/#dom-documentorshadowroot-getanimations

toggleFx function was modified and now it has three parts.

In the first part, search document.getAnimations() and find currentTime attribute where animationName is one of pulse_active or pulse_inverted; then store them in the realted variables (i.e. pulseActiveStart and pulseInvertedStart).

In the 2nd part, className of spanTarget changed as you told.

In the 3rd part, pulseActiveStart and pulseInvertedStart applied to currentTime attribute of related animation (reverse 1st part).

function toggleFx(spanID) {

let spanTarget = document.getElementById(spanID);

let pulseActiveStart;
let pulseInvertedStart;
let anims = document.getAnimations()
for(let i = 0; i < anims.length; i++) {
if(anims[i].animationName == "pulse_active") pulseActiveStart = anims[i].currentTime;
else if(anims[i].animationName == "pulse_inverted") pulseInvertedStart = anims[i].currentTime;
}



if(spanTarget.classList.contains('pulse_invert')) {
spanTarget.classList.remove('pulse_invert');
spanTarget.classList.remove('pulse');
} else if(spanTarget.classList.contains('pulse')) {
spanTarget.classList.add('pulse_invert');
spanTarget.classList.remove('pulse');
} else {
spanTarget.classList.add('pulse');
}

anims = document.getAnimations()
for(let i = 0; i < anims.length; i++) {
if(anims[i].animationName == "pulse_active") {
if(pulseActiveStart) anims[i].currentTime = pulseActiveStart;
} else if(anims[i].animationName == "pulse_inverted") {
if(pulseInvertedStart) anims[i].currentTime = pulseInvertedStart;
}
}
}
div {
display: flex;
flex-flow: column;
}

span.pulse {
color: #f00;
animation: pulse_active 1.5s ease-in infinite;
}

span.pulse_invert {
color: #00f;
animation: pulse_inverted 3s ease-in infinite;
}

@keyframes pulse_active {
0% { opacity: 0; }
50% { opacity: 0.66; }
100% { opacity: 0; }
}

@keyframes pulse_inverted {
0% { opacity: 1; }
50% { opacity: 0.33; }
100% { opacity: 1; }
}
<div>
<span id="spA" onclick="toggleFx('spA')">A</span>
<span id="spB" onclick="toggleFx('spB')">B</span>
<span id="spC" onclick="toggleFx('spC')">C</span>
<span id="spD" onclick="toggleFx('spD')">D</span>
</div>

CSS Transition after animation ends

I have forked your project and adapted it so it works. You can find it here.

What I have changed is the following:

I give the white square a start position of top: 150px and let it, on hover of div, get a top: 0. The span gets a transition: top .5s and with that it goes to top: 0; on hover and back to top: 150px; when the mouse leaves.

I have removed the translateY(-60px); from the animation, because that would move it even more up when the animation would start.

Here's your new CSS:

div {
width: 200px;
height: 200px;
margin: 40px auto;
background-color: #b00;
position: relative;

&:hover {
span {
top: 0px;
animation: rotate 1s infinite .5s alternate;
animation-direction: alternate;
}
}
}

span {
position: absolute;
width: 20px;
height: 20px;
background-color: #fff;
bottom: 10px;
left: 0;
right: 0;
top: 150px;
margin: auto;
transition: top .5s;
}

@keyframes rotate {
from {
transform: rotate(0);
}
to {
transform: rotate(-90deg);
}
}

Edit: The problem is that an animation is time-based and not action-based, which means that as soon as you trigger an animation, a timer starts running and it will run through all the keyframes until the set time has passed. Hover-in and hover-out have no effect, except that the timer can be stopped prematurely, but the animation will not continue (or reversed, which you wanted) after that. transition is action-based, which means it gets triggered every time an action (for example :hover) is happening. On :hover, this means it takes .5s to go to top:0 and when the hover ends, it takes .5s to got to top:150px.

I hope the above addition makes sense :)

As you can see, I also cleaned up a bit in your animation-name: etc., since it can be combined into one line.

Chaining Multiple CSS Animations

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>

Combination of animation and transition not working properly

This is sort of a known behavior with Chrome. Firefox does seem to be able to handle the removal of animation smoothly with transition but Chrome doesn't do so. I had seen this behavior happen earlier also in this thread.

Why does removal of an animation not work with transition in Chrome?

While I cannot provide a 100% fool-proof explanation of why this happens, we can decode it to some extent based on this HTML5Rocks article about Accelerated rendering in Chrome and this one about GPU accelerated compositing in Chrome.

What seems to happen is that the element gets its own rendering layer because it has explicit position property set on it. When a layer (or part of it) gets invalidated due to animation, Chrome only repaints that layer which is affected by the change. When you open the Chrome Developer Console, switch on "Show Paint Rects" option, you would see that when the animation is happening Chrome only paints the actual element that is getting animated.

However, at the start and end of animation a whole page repaint is happening which puts the element back into its original position immediately and thus overriding the transition behavior.

$('button').click(function(){  $('div').toggleClass('clicked');});
div{  background-color: #ccc;  height: 100px;  width: 100px;  transition-property: top, left;  transition-duration: 1s;  transition-timing-function: linear;  position: relative;  top: 0;  left: 0;}.clicked{  animation-name: clicked;  animation-duration: 1s;  animation-timing-function: linear;  animation-fill-mode: forwards;}@keyframes clicked{  0% {top: 0; left: 0;}  100% {top: 100px; left: 100px;}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button type="button">Click Me!</button><div></div>

Css3 animations with same durations end at different times when triggered by the same event

Change this style:

@-webkit-keyframes button_stretch {
from{width: 50px;}
to{max-width: 377px;}
}

To:

@-webkit-keyframes button_stretch {
from{width: 50px;}
to{width: 377px;}
}

I'd like to tell you why, but I don't really know. ;) I think max-width causes the button to respond more to the other elements which are also resizing, which is why it is lagging. By specifying width, you just force it to be that size, no concessions.

Updated Fiddle



Related Topics



Leave a reply



Submit