Rolling countdown leaps back to start instead of doing a continous roll
Unanswered
mx posted this in #help-forum
mxOP
Hello, I am trying to do a rolling countdown where the digit "slides down" once a second is reached. My current implementation is fine, but the problem is that it leaps back (9 -> 0). I am not really sure how to make "be in a loop" where it doesn't leap back. Here is my JSX implementation of the Roller.js component:
And my countdown component:
import React, { useState, useEffect } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group';
import './Roller.css';
const Roller = ({ value, charList = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], transition = 0.8 }) => {
const [isRollStart, setIsRollStart] = useState(false);
useEffect(() => {
setTimeout(() => {
setIsRollStart(true);
}, 200);
}, [transition]);
const getIndex = (t, idx) => {
if (!charList.includes(t)) {
if (!t.trim()) return "NULL" + idx;
return String(t) + String(idx);
}
return idx.toString();
};
const findCharIndex = (t, isOriginal = false) => {
let idx = charList.indexOf(t);
if (idx === -1 && !isOriginal) return 0;
return idx;
};
return (
<TransitionGroup className="roller">
{String(value).split("").map((t, idx) => (
t !== '\n'
? (
<CSSTransition key={getIndex(t, idx)} timeout={transition * 1000 + 200} classNames="roller-item">
<div className="roller__wrapper">
{t !== '\n' && (
<ul
className="roller__char rollerBlock"
style={{
top: `${isRollStart ? findCharIndex(t) * -100 : 1}%`,
height: `${charList.length * 100}%`,
transition: `${transition}s`
}}
>
{(findCharIndex(t, true) !== -1 ? charList : [t]).map(char => (
<li
key={char}
className={`roller__char__item ${t === char ? 'copyable' : ''}`}
style={{ opacity: char === ' ' ? 0 : 1 }}
>
{char === " " ? "-" : char}
</li>
))}
</ul>
)}
</div>
</CSSTransition>
) : <br key={idx} />
))}
</TransitionGroup>
);
}
export default Roller;And my countdown component:
import React, { useEffect, useState } from 'react';
import moment from 'moment';
import Roller from './Roller';
const Countdown = ({ targetDate }) => {
const [timeLeft, setTimeLeft] = useState({
days: 0,
hours: 0,
minutes: 0,
seconds: 0
});
useEffect(() => {
if (!moment.isMoment(targetDate)) {
console.error("Invalid targetDate provided to Countdown.");
return;
}
const interval = setInterval(() => {
const now = moment();
const duration = moment.duration(targetDate.diff(now));
setTimeLeft({
days: duration.days(),
hours: duration.hours(),
minutes: duration.minutes(),
seconds: duration.seconds(),
});
}, 1000);
return () => clearInterval(interval);
}, [targetDate]);
const timeElements = ['days', 'hours', 'minutes', 'seconds'];
return (
<div style={{display: 'inline-flex'}}>
{timeElements.map((element, idx) => {
const paddedTime = String(timeLeft[element]).padStart(2, '0').split('');
return (
<React.Fragment key={element}>
{paddedTime.map((char, charIdx) => (
<Roller key={charIdx} value={char} />
))}
{idx !== timeElements.length - 1 && <span>:</span>}
</React.Fragment>
);
})}
</div>
);
};
export default Countdown;1 Reply
mxOP
The bug lays down in this: (top & transition). I think the easiest solution would be to store the currentDigit and nextDigit, and when the animation moves to nextDigit after the animation is complete set transition time to 0, top to 0% and update nextDigit. Could anyone help me achieve this implementation?