I am trying to create a blinking countdown timer, starting it at 5 seconds, and disappearing when it gets to 0.
CSS:
.blinky {
transition: 1s opacity;
-moz-transition: 1s opacity;
-webkit-transition: 1s opacity;
}
HTML:
<div id="countdown" >
JS:
const cdStart = 5;
countdown.innerHTML = cdStart;
countdown.style.opacity = 0;
for (var i = cdStart - 1; i > 0; i--) {
setTimeout(
(x) => {
countdownTime.innerHTML = x;
countdown.classList.remove('blinky');
countdown.style.opacity = 1;
countdown.classList.add('blinky');
countdown.style.opacity = 0;
},
1000 * (cdStart - i),
i
);
}
What I want this to do is to show 5, fade out, 4, fade out, 3, fade out, 2, fade out, 1, fade out. When a new number is shown on the timer, I want it to show up instantly, and not fade back in. For that reason, I remove the "blinky" class before I set the opacity to 1, then add it back in before setting opacity to 0.
Unfortunately, this doesn't seem to work - 5 shows up and fades out, and then nothing else shows up. If I remove the manipulation of countdown's style (and just set the innerHTML in the for loop), I see that the timer displays properly (counts down from 3 to 1), so that's working.
I thought, maybe the browser is having trouble removing a class and then immediately adding it back in, so I separated those events by a bit:
CSS:
.blinky {
transition: .9s opacity;
-moz-transition: .9s opacity;
-webkit-transition: .9s opacity;
}
JS:
const cdStart = 5;
countdownTime.innerHTML = cdStart;
countdown.style.opacity = 0;
for (var i = cdStart - 1; i > 0; i--) {
setTimeout(
(x) => {
countdownTime.innerHTML = x;
countdown.classList.remove('blinky');
countdown.style.opacity = '';
},
1000 * (cdStart - i),
i
);
setTimeout(() => {
countdown.classList.add('blinky');
countdown.style.opacity = 0;
}, 1000 * (cdStart - i) 100);
}
This one was closer - I saw 5, fade out, then nothing for a bit, and 1 came in and then faded out.
Is there a more reliable way to get the desired behavior here?
CodePudding user response:
With CSS Animations you can create the fade-out for every second that is changed in the counter.
Add an animation with 5 iterations and listen for the animationiteration
and animationend
events. These events are fired for every time the animation plays and restarts, and for when the animation is finished.
Change the count and update the textContent
of the countdown in both event handlers.
const countdown = document.querySelector('#countdown');
let count = 5;
function updateCount() {
count--;
countdown.textContent = count;
}
countdown.textContent = count;
countdown.classList.add('blinky');
countdown.addEventListener('animationiteration', updateCount);
countdown.addEventListener('animationend', updateCount);
@keyframes blink-out {
0%, 25% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.blinky {
font-size: 48px;
animation: blink-out 1s ease-out forwards 5;
}
<div id="countdown"></div>
CodePudding user response:
Hope I understood correctly :
let clock = document.getElementById('seconds');
let secondsRemaining = 5;
clock.innerText = secondsRemaining;
clock.classList.add('animation');
const myInterval = setInterval(()=>{
secondsRemaining--;
clock.innerText = secondsRemaining;
if(!secondsRemaining)
stopAnimation();
},1000)
function stopAnimation(){
clock.classList.remove('animation');
clearInterval(myInterval);
}
.clock{
position:absolute;
top:50%;
left:50%;
transform:translate(-50%,-50%);
}
.animation{
animation:1s fade-out ease;
animation-iteration-count:infinite;
animation-fill-mode:forwards;
}
#seconds{
font-size:10rem;
}
@keyframes fade-out{
from{opacity:1}
99%{opacity:0}
to{opacity:1}
}
<body>
<div >
<span id="seconds"></span>
</div>
</body>
CodePudding user response:
Here is an example using async functions and css transition to control fading.
const wait = ms => new Promise(r => setTimeout(r, ms));
const $ = str => document.querySelector(str);
const btn = $("button");
const number = $(".number");
const DELAY_TIME = 1000;
async function changeNumber(newVal) {
number.classList.add("fade");
await wait(DELAY_TIME);
number.textContent = newVal;
number.classList.remove("fade");
await wait(DELAY_TIME);
}
async function countFrom(n) {
const STARTING_VALUE = n;
number.textContent = STARTING_VALUE;
await wait(DELAY_TIME);
for (let counterVal = STARTING_VALUE - 1; counterVal >= 0; counterVal--) {
await changeNumber(counterVal);
}
}
btn.addEventListener("click", async () => {
btn.disabled = true;
await countFrom(5);
btn.disabled = false;
});
.timer {
display: grid;
place-items: center;
width: 100px;
height: 150px;
border-radius: 20px;
border: 5px solid lightgreen;
}
.number {
font-size: 5rem;
font-family: monospace;
transition: opacity 1s;
}
.number.fade {
opacity: 0;
}
<div >
<span ></span>
</div>
<br />
<button>count from 5!</button>