Reputation: 7735
I want to animate a div
so that:
width
changes from 100% to 0%.background-color
should change from green to orange.background-color
should change from orange to red.When I start the animation, the color of my div
changes immediately to orange. The change is not animated, the color just goes from 100% green to 100% orange.
orange-to-red
animation works correctly.I start the animation by adding the class timer-animation
to div#timerBar
. I'm not sure if this is the preferred way to start the animation.
At 5 seconds, the animationstart
event fires for green-to-orange
(as coded), but the div
does not change color as it is already orange.
I'm new to CSS animations, but I have been coding for 30 years.
let timerBar = document.getElementById('timerBar');
let animationNameList = document.getElementById('animation-names');
const startAnimation = function() {
timerBar.classList.remove('pause');
timerBar.classList.toggle('timer-animation');
animationNameList.replaceChildren();
};
const pauseAnimation = function() {
timerBar.classList.toggle('pause');
};
timerBar.addEventListener('animationstart', (e) => {
let li = document.createElement('li');
li.innerText = e.animationName;
animationNameList.appendChild(li);
});
document
.getElementById('startButton')
.addEventListener('click',(startAnimation));
document
.getElementById('pauseButton')
.addEventListener('click',(pauseAnimation));
#timerBar {
background-color: #41b883;
height: 20px;
width: 100%;
}
.timer-animation {
animation-timing-function: linear;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: both;
animation-name: shrink-width,green-to-orange,orange-to-red;
animation-delay:0ms,5000ms,7500ms;
animation-duration:10000ms,2000ms,2000ms;
}
.pause {
animation-play-state: paused;
}
@keyframes shrink-width {
from {
width: 100%;
}
to {
width: 0%;
}
}
@keyframes green-to-orange {
from {
background-color: #41b883;
}
to {
background-color: #ffa500;
}
}
@keyframes orange-to-red {
from {
background-color: #ffa500;
}
to {
background-color: #ff0000;
}
}
<div id="timerBar"></div>
<br/>
<button id="startButton">Start / Reset</button>
<button id="pauseButton">Pause</button>
<br />
<h4>When an animationstart event fires, its name will appear in this list.</h4>
<ul id="animation-names"></ul>
Upvotes: 0
Views: 142
Reputation: 7735
Thanks for the suggestions everyone. They were helpful, but did not quite meet my requirements. I want the period of the color transitions to only be 2 seconds, rather than fading for the entire 10 seconds of the animation.
I ended up solving this in a sort of hacky mix of animations and transitions.
_dummy_
, and watched for it inside the animationstart
event.div
's classList
to switch it to the next color.transition: background-color 2000ms
to the div
.The three changes together make it work to meet my requirements.
I still don't understand why my original method of using just animations didn't work. I'd like to read some documentation to explain precisely what why my syntax is wrong.
let timerBar = document.getElementById('timerBar');
let animationNames = document.getElementById('animation-names');
let colors = ['green','orange','red'];
let colorIndex = 0;
const getNextColor = function() {
let color = colors[colorIndex++];
if(colorIndex == colors.length)
colorIndex = 0;
return color;
};
const changeColor = function() {
colors.forEach(o=>timerBar.classList.remove(o));
timerBar.classList.add(getNextColor());
};
const startAnimation = function() {
colorIndex = 0;
changeColor();
timerBar.classList.remove('pause');
timerBar.classList.toggle('timer-animation');
animationNames.replaceChildren();
};
const pauseAnimation = function() {
timerBar.classList.toggle('pause');
};
timerBar.addEventListener('animationstart', (e) => {
if(e.animationName.includes('_dummy_')) {
changeColor();
}
let li = document.createElement('li');
li.innerText = e.animationName;
animationNames.appendChild(li);
});
document
.getElementById('startButton')
.addEventListener('click',(startAnimation));
document
.getElementById('pauseButton')
.addEventListener('click',(pauseAnimation));
#timerBar {
height: 20px;
width: 100%;
background-size: 0 50%;
transition: background-color 2000ms;
}
.green {
background-color: #41b883;
}
.orange {
background-color: #ffa500;
}
.red {
background-color: #ff0000;
}
.timer-animation {
animation-timing-function: linear;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: both;
animation-name: shrink-width, _dummy_, _dummy_;
animation-duration: 10000ms,0ms,0ms;
animation-delay: 0ms, 5000ms, 7500ms;
}
.pause {
animation-play-state: paused;
}
@keyframes shrink-width {
from {
width: 100%;
}
to {
width: 0%;
}
}
@keyframes _dummy_ {}
<div id="timerBar" class="green"></div>
<br/>
<button id="startButton">Start / Reset</button>
<button id="pauseButton">Pause</button>
<br />
<h4>When an animation starts, its name will appear in this list.</h4>
<ul id="animation-names"></ul>
Upvotes: 0
Reputation: 175
You don't need to use multiple animation keyframes. Use a single keyframe. At 0% it will change stay green, at 50% it will gradually change from green to orange, at 75% it will change from orange to red and will stay red till 100%.
You don't need to use animation delay and set animation duration 10000ms for both keyframes.
let timerBar = document.getElementById('timerBar');
let animationNameList = document.getElementById('animation-names');
const startAnimation = function() {
timerBar.classList.remove('pause');
timerBar.classList.toggle('timer-animation');
animationNameList.replaceChildren();
};
const pauseAnimation = function() {
timerBar.classList.toggle('pause');
};
timerBar.addEventListener('animationstart', (e) => {
let li = document.createElement('li');
li.innerText = e.animationName;
animationNameList.appendChild(li);
});
document
.getElementById('startButton')
.addEventListener('click',(startAnimation));
document
.getElementById('pauseButton')
.addEventListener('click',(pauseAnimation));
#timerBar {
background-color: #41b883;
height: 20px;
width: 100%;
}
.timer-animation {
animation-timing-function: linear;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: both;
animation-name: shrink-width, green-to-orange-to-red;
animation-duration:10000ms, 10000ms;
}
.pause {
animation-play-state: paused;
}
@keyframes shrink-width {
from {
width: 100%;
}
to {
width: 0%;
}
}
@keyframes green-to-orange-to-red {
0%{
background-color: #41b883;
}
50%{
background-color: #ffa500;
}
75%{
background-color: #ff0000;
}
100%{
background-color: #ff0000;
}
}
<div id="timerBar"></div>
<br/>
<button id="startButton">Start / Reset</button>
<button id="pauseButton">Pause</button>
<br />
<h4>When an animationstart event fires, its name will appear in this list.</h4>
<ul id="animation-names"></ul>
Upvotes: 0
Reputation: 2096
The problem is in your CSS code. If we see the CSS code we can see that you are using two same animation keyframes green-to-orange
& orange-to-red
in the same class. Though you have set an animation-delay still it neglects the green-to-orange
animation and takes the initial color from the orange-to-red
keyframe. So the best option will be to use a single keyframe green-to-orange-to-red
instead of two separate keyframes green-to-orange
& orange-to-red
. And in the single animation green-to-orange-to-red
you have to use x%
instead of the from
& to
. You can check the following code, I've updated the code's CSS & I hope it will help you with your question.
let timerBar = document.getElementById('timerBar');
let animationNameList = document.getElementById('animation-names');
const startAnimation = function() {
timerBar.classList.remove('pause');
timerBar.classList.toggle('timer-animation');
animationNameList.replaceChildren();
};
const pauseAnimation = function() {
timerBar.classList.toggle('pause');
};
timerBar.addEventListener('animationstart', (e) => {
let li = document.createElement('li');
li.innerText = e.animationName;
animationNameList.appendChild(li);
});
document
.getElementById('startButton')
.addEventListener('click',(startAnimation));
document
.getElementById('pauseButton')
.addEventListener('click',(pauseAnimation));
#timerBar {
background-color: #41b883;
height: 20px;
width: 100%;
}
.timer-animation {
animation-timing-function: linear;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: both;
animation-name: shrink-width,green-to-orange-to-red;
animation-delay:0ms,0ms;
animation-duration:10000ms,10000ms;
}
.pause {
animation-play-state: paused;
}
@keyframes shrink-width {
from {
width: 100%;
}
to {
width: 0%;
}
}
@keyframes green-to-orange-to-red {
0% {
background-color: #41b883;
}
50% {
background-color: #ffa500;
}
100% {
background-color: #ff0000;
}
}
<div id="timerBar"></div>
<br/>
<button id="startButton">Start / Reset</button>
<button id="pauseButton">Pause</button>
<br />
<h4>When an animationstart event fires, its name will appear in this list.</h4>
<ul id="animation-names"></ul>
Upvotes: 1