dragostis
dragostis

Reputation: 2662

Why does CSS animation have no duration?

I have a few animations that should go from one state to the other. Going from state empty to state bit works fine and so does from bit to large, but when I try to use the reverse animations which I add as classes, the animations have no duration. Here's a snippet of my code:

@keyframes empty-bit {
  0%   {width: 0%;}
  100% {width: 4px;}
}
@keyframes bit-large {
  0%   {width: 4px;}
  100% {width: 100%;}
}
.timeline-empty-bit {
  animation-name: empty-bit;
  animation-fill-mode: forwards;
  animation-duration: 200ms;
  animation-timing-function: ease-in-out;
  animation-delay: 200ms;
}
.timeline-bit-large {
  animation-name: bit-large;
  animation-fill-mode: forwards;
  animation-duration: 200ms;
  animation-timing-function: ease-in-out;
}
.timeline-large-bit {
  animation-name: bit-large;
  animation-fill-mode: forwards;
  animation-duration: 200ms;
  animation-timing-function: ease-in-out;
  animation-direction: reverse;
  animation-delay: 200ms;
}
.timeline-bit-empty {
  animation-name: empty-bit;
  animation-fill-mode: forwards;
  animation-duration: 200ms;
  animation-timing-function: ease-in-out;
  animation-direction: reverse;
}

Here's a jsfiddle: https://jsfiddle.net/9tmm46fz/ Use arrows (left, right) to see the animation.

Upvotes: 3

Views: 203

Answers (1)

Ajedi32
Ajedi32

Reputation: 48378

The animation does have a duration. The problem is the animation already finished playing, and changing the class won't play the animation from the beginning unless the animation-name property changes. So one way to solve this would be to create separate animations for the bit-empty and large-bit classes:

.timeline-gauge {
    height: 20px;
}
.timeline-gauge-container {
    display: inline-block;
    height: 100%;
}
.timeline-gauge-part {
    display: block;
    height: 100%;
    width: 0%;
}
@keyframes empty-bit {
    0%   {width: 0%;}
    100% {width: 4px;}
}
@keyframes bit-empty {
    0%   {width: 0%;}
    100% {width: 4px;}
}
@keyframes bit-large {
    0%   {width: 4px;}
    100% {width: 100%;}
}
@keyframes large-bit {
    0%   {width: 4px;}
    100% {width: 100%;}
}
.timeline-empty-bit {
    animation-name: empty-bit;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-delay: 200ms;
}
.timeline-bit-large {
    animation-name: bit-large;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
}
.timeline-large-bit {
    animation-name: large-bit;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-direction: reverse;
    animation-delay: 200ms;
}
.timeline-bit-empty {
    animation-name: bit-empty;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-direction: reverse;
}

var containers = document.getElementsByClassName("timeline-gauge-container");
var length = containers.length;

for (var i = 0; i < length; i++) {
    containers[i].style.width = containers[i].getAttribute("percentage");
}

var parts = document.getElementsByClassName("timeline-gauge-part");
var length = parts.length;

for (var i = 0; i < length; i++) {
    parts[i].style.backgroundColor = parts[i].getAttribute("color");
}

var index = 0;

parts[0].classList.add("timeline-empty-bit");

function removeAnimation(part) {
    part.classList.remove("timeline-empty-bit");
    part.classList.remove("timeline-bit-large");
    part.classList.remove("timeline-large-bit");
    part.classList.remove("timeline-bit-empty");
}

document.onkeydown = function(event) {
    if (event.keyCode == 37 && index > 0) {
        removeAnimation(parts[index - 1]);
        parts[index - 1].classList.add("timeline-large-bit");
        
        if (index != length) {
            removeAnimation(parts[index]);
            parts[index].classList.add("timeline-bit-empty");
        }
        
        index--;
    }
    
    if (event.keyCode == 39 && index < length) {
        removeAnimation(parts[index]);
        parts[index].classList.add("timeline-bit-large");
        
        if (index != length - 1) {
            removeAnimation(parts[index + 1]);
            parts[index + 1].classList.add("timeline-empty-bit");
        }
        
        index++;
    }
};
.timeline-gauge {
    height: 20px;
}
.timeline-gauge-container {
    display: inline-block;
    height: 100%;
}
.timeline-gauge-part {
    display: block;
    height: 100%;
    width: 0%;
}
@keyframes empty-bit {
    0%   {width: 0%;}
    100% {width: 4px;}
}
@keyframes bit-empty {
    0%   {width: 0%;}
    100% {width: 4px;}
}
@keyframes bit-large {
    0%   {width: 4px;}
    100% {width: 100%;}
}
@keyframes large-bit {
    0%   {width: 4px;}
    100% {width: 100%;}
}
.timeline-empty-bit {
    animation-name: empty-bit;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-delay: 200ms;
}
.timeline-bit-large {
    animation-name: bit-large;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
}
.timeline-large-bit {
    animation-name: large-bit;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-direction: reverse;
    animation-delay: 200ms;
}
.timeline-bit-empty {
    animation-name: bit-empty;
    animation-fill-mode: both;
    animation-duration: 200ms;
    animation-timing-function: ease-in-out;
    animation-direction: reverse;
}
<div class="timeline-container">
    <div class="timeline-gauge">
        <div class="timeline-gauge-container" percentage="5%"><span class="timeline-gauge-part" color="#4f5f6b"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#237487"></span></div><div class="timeline-gauge-container" percentage="10%"><span class="timeline-gauge-part" color="#00b188"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#008e6c"></span></div><div class="timeline-gauge-container" percentage="25%"><span class="timeline-gauge-part" color="#99ca34"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#9059a2"></span></div>
    </div>
</div>

View JSFiddle

A better solution though would be to not use animations at all. In my opinion, transitions are a much better tool for the job in this case:

.timeline-gauge {
    height: 20px;
}
.timeline-gauge-container {
    display: inline-block;
    height: 100%;
}
.timeline-gauge-part {
    display: block;
    height: 100%;
    width: 0%;

    transition: width 200ms ease-in-out;
}

.timeline-empty-bit {
    width: 4px;
    transition-delay: 200ms;
}
.timeline-bit-large {
    width: 100%;
}
.timeline-large-bit {
    width: 4px;
    transition-delay: 200ms;
}
.timeline-bit-empty {
    width: 0%;
}

var containers = document.getElementsByClassName("timeline-gauge-container");
var length = containers.length;

for (var i = 0; i < length; i++) {
    containers[i].style.width = containers[i].getAttribute("percentage");
}

var parts = document.getElementsByClassName("timeline-gauge-part");
var length = parts.length;

for (var i = 0; i < length; i++) {
    parts[i].style.backgroundColor = parts[i].getAttribute("color");
}

var index = 0;

parts[0].classList.add("timeline-empty-bit");

function removeAnimation(part) {
    part.classList.remove("timeline-empty-bit");
    part.classList.remove("timeline-bit-large");
    part.classList.remove("timeline-large-bit");
    part.classList.remove("timeline-bit-empty");
}

document.onkeydown = function(event) {
    if (event.keyCode == 37 && index > 0) {
        removeAnimation(parts[index - 1]);
        parts[index - 1].classList.add("timeline-large-bit");
        
        if (index != length) {
            removeAnimation(parts[index]);
            parts[index].classList.add("timeline-bit-empty");
        }
        
        index--;
    }
    
    if (event.keyCode == 39 && index < length) {
        removeAnimation(parts[index]);
        parts[index].classList.add("timeline-bit-large");
        
        if (index != length - 1) {
            removeAnimation(parts[index + 1]);
            parts[index + 1].classList.add("timeline-empty-bit");
        }
        
        index++;
    }
};
.timeline-gauge {
    height: 20px;
}
.timeline-gauge-container {
    display: inline-block;
    height: 100%;
}
.timeline-gauge-part {
    display: block;
    height: 100%;
    width: 0%;
    
    transition: width 200ms ease-in-out;
}

.timeline-empty-bit {
    width: 4px;
    transition-delay: 200ms;
}
.timeline-bit-large {
    width: 100%;
}
.timeline-large-bit {
    width: 4px;
    transition-delay: 200ms;
}
.timeline-bit-empty {
    width: 0%;
}
<div class="timeline-container">
    <div class="timeline-gauge">
        <div class="timeline-gauge-container" percentage="5%"><span class="timeline-gauge-part" color="#4f5f6b"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#237487"></span></div><div class="timeline-gauge-container" percentage="10%"><span class="timeline-gauge-part" color="#00b188"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#008e6c"></span></div><div class="timeline-gauge-container" percentage="25%"><span class="timeline-gauge-part" color="#99ca34"></span></div><div class="timeline-gauge-container" percentage="20%"><span class="timeline-gauge-part" color="#9059a2"></span></div>
    </div>
</div>

View JSFiddle

Upvotes: 2

Related Questions