Don Rhummy Don Rhummy - 3 days ago 5
CSS Question

Why does animationend/webkitAnimationEnd event have a delay before it's called?

When i listen to

animationend
, it is fired after a delay. You can see the animation complete (the
div
is moved into the correct spot), yet the event doesn't fire for another 300-500ms.

JSFiddle: https://jsfiddle.net/9q0scpa0/4/

Why is this?

HTML:

<div class="page" id="page1" onclick="restart()">
Page 1<br/><br/>Page 1<br/><br/>Page 1<br/><br/>Page 1<br/><br/>
Page 1<br/><br/>Page 1<br/><br/>Page 1<br/><br/>
Page 1<br/><br/>Page 1<br/><br/>Page 1<br/><br/>
Page 1<br/><br/>Page 1<br/><br/>Page 1<br/><br/>
</div>
<div class="page" id="page2" onclick="restart()">
Page 2
</div>


CSS:

*
{
margin: 0em;
padding: 0em;
}

html
{
height: 100%;
width: 100%;
}

body
{
height: 100%;
width: 100%;
overflow: hidden;
}

.page
{
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
background-color: red;
}

@keyframes moveNext
{
0% {
transform: translate(0em,100%);
-webkit-transform: translate(0em,0%);
-moz-transform: translate(0em,0%);
}

100% {
transform: translate(0em,-100%);
-webkit-transform: translate(0em,-100%);
-moz-transform: translate(0em,-100%);
}
}

@keyframes movePrevious
{
0% {
transform: translate(0em,-100%);
-webkit-transform: translate(0em,-100%);
-moz-transform: translate(0em,-100%);
display: block;
}

100% {
transform: translate(0em,0%);
-webkit-transform: translate(0em,0%);
-moz-transform: translate(0em,0%);
}
}

#page1.leave
{
z-index: 0;
animation-name: moveNext;
-webkit-animation-delay: 200ms;
animation-delay: 200ms;
animation-duration: 800ms;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}

#page2.enter
{
background-color: blue;
z-index: 1;
animation-name: moveNext;
animation-duration: 750ms;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}

#page1.enter
{
z-index: 0;
animation-name: movePrevious;
animation-duration: 800ms;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}

#page2.leave
{
background-color: blue;
z-index: 1;
animation-name: movePrevious;
-webkit-animation-delay: 200ms;
animation-delay: 200ms;
animation-duration: 750ms;
animation-iteration-count: 1;
animation-timing-function: ease-in-out;
animation-fill-mode: forwards;
}


JavaScript:

var isMoveNext = true;
var page1 = document.getElementById( "page1" );
var page2 = document.getElementById( "page2" );

window.restart = function()
{
//Moving next
if ( isMoveNext )
{
page1.className = "page leave";
page2.className = "page enter";
isMoveNext = false;
}

else
{
page1.className = "page enter";
page2.className = "page leave";
isMoveNext = true;
}
}

page1.addEventListener( "animationend", function(event) { console.log( "page 1" ); }, false );
page2.addEventListener( "animationend", function(event) { console.log( "page 2" ); }, false );
page1.addEventListener( "webkitAnimationEnd", function(event) { console.log( "page 1" ); }, false );
page2.addEventListener( "webkitAnimationEnd", function(event) { console.log( "page 2" ); }, false );
page1.addEventListener( "MSAnimationEnd", function(event) { console.log( "page 1" ); }, false );
page2.addEventListener( "MSAnimationEnd", function(event) { console.log( "page 2" ); }, false );

Answer

It's due to the easing. Even if the animation looks close to done, it's still moving very small amounts until it hits some threshold which snaps it to the end value. This threshold is intentionally kept very small to prevent visible snapping when easing out.

If you switch the easing to linear the animationend event should fire as soon as the animation visually reaches the end.

Comments