counter roll back ready

This commit is contained in:
Kakabay 2024-12-24 18:33:55 +05:00
parent 17d59d8b94
commit 95abb52d81
1 changed files with 59 additions and 21 deletions

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { motion } from 'framer-motion'; import { motion } from 'framer-motion';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
interface RollingCounterProps { interface RollingCounterProps {
numberString: string; numberString: string;
@ -55,6 +55,7 @@ const RollingDigit = ({
isStopped, isStopped,
showHyphen, showHyphen,
totalDigits, totalDigits,
isRollingBack,
}: { }: {
targetValue: number; targetValue: number;
index: number; index: number;
@ -62,6 +63,7 @@ const RollingDigit = ({
isStopped: boolean; isStopped: boolean;
showHyphen: boolean; showHyphen: boolean;
totalDigits: number; totalDigits: number;
isRollingBack: boolean;
}) => { }) => {
const numbers = useMemo(() => getNumbers(targetValue), [targetValue]); const numbers = useMemo(() => getNumbers(targetValue), [targetValue]);
@ -71,11 +73,13 @@ const RollingDigit = ({
<motion.div <motion.div
initial={{ y: INITIAL_OFFSET }} initial={{ y: INITIAL_OFFSET }}
animate={{ animate={{
y: -((ROLLS * 10 + targetValue + 1) * DIGIT_HEIGHT) + INITIAL_OFFSET, y: isRollingBack
? INITIAL_OFFSET
: -(numbers.length - EXTRA_NUMBERS_AFTER - 3) * DIGIT_HEIGHT + INITIAL_OFFSET,
}} }}
transition={{ transition={{
duration: 2, duration: isRollingBack ? 2 : 2,
delay: (totalDigits - 1 - index) * 0.2, delay: isRollingBack ? index * 0.2 : (totalDigits - 1 - index) * 0.2,
ease: 'easeInOut', ease: 'easeInOut',
}} }}
onAnimationComplete={onAnimationComplete} onAnimationComplete={onAnimationComplete}
@ -84,7 +88,7 @@ const RollingDigit = ({
<div <div
key={`${index}-${i}`} key={`${index}-${i}`}
className={`h-[${DIGIT_HEIGHT}px] w-[77px] flex items-center justify-center numeric-display-1 transition-colors duration-500 ${ className={`h-[${DIGIT_HEIGHT}px] w-[77px] flex items-center justify-center numeric-display-1 transition-colors duration-500 ${
isStopped && num === targetValue ? 'text-white' : 'text-[#B0B1CD]' isStopped && !isRollingBack && num === targetValue ? 'text-white' : 'text-[#B0B1CD]'
}`}> }`}>
{num} {num}
</div> </div>
@ -98,18 +102,19 @@ const RollingDigit = ({
const RollingCounter: React.FC<RollingCounterProps> = ({ numberString }) => { const RollingCounter: React.FC<RollingCounterProps> = ({ numberString }) => {
const [isStopped, setIsStopped] = useState<boolean[]>([]); const [isStopped, setIsStopped] = useState<boolean[]>([]);
const [isRollingBack, setIsRollingBack] = useState(false);
const [isTransitioning, setIsTransitioning] = useState(false);
const [currentNumbers, setCurrentNumbers] = useState<number[]>([]);
const prevNumberStringRef = useRef(numberString);
const { numbers, isInitialLoading } = useMemo(() => { const { numbers, isInitialLoading } = useMemo(() => {
if (!numberString) { if (!numberString) {
return { numbers: [], isInitialLoading: true }; return { numbers: [], isInitialLoading: true };
} }
const parsed = numberString const parsed = numberString.split('-').flatMap((pair) => {
.replace(/-/g, '') return pair.split('').map((char) => parseInt(char, 10));
.split('') });
.map((char) => parseInt(char, 10));
setIsStopped(new Array(parsed.length).fill(false));
return { return {
numbers: parsed, numbers: parsed,
@ -117,13 +122,45 @@ const RollingCounter: React.FC<RollingCounterProps> = ({ numberString }) => {
}; };
}, [numberString]); }, [numberString]);
const handleAnimationComplete = useCallback((index: number) => { // Initialize currentNumbers
setIsStopped((prev) => { useEffect(() => {
const newState = [...prev]; if (!currentNumbers.length && numbers.length) {
newState[index] = true; setCurrentNumbers(numbers);
return newState; }
}); }, [numbers, currentNumbers.length]);
}, []);
// Handle number changes
useEffect(() => {
if (prevNumberStringRef.current !== numberString && !isRollingBack) {
setIsTransitioning(true);
setIsRollingBack(true);
setIsStopped(new Array(numbers.length).fill(false));
setTimeout(() => {
setIsRollingBack(false);
setCurrentNumbers(numbers);
prevNumberStringRef.current = numberString;
}, 2000);
}
}, [numberString, numbers, isRollingBack]);
const handleAnimationComplete = useCallback(
(index: number) => {
if (!isRollingBack) {
setIsStopped((prev) => {
const newState = [...prev];
newState[index] = true;
if (newState.every((stopped) => stopped)) {
setIsTransitioning(false);
}
return newState;
});
}
},
[isRollingBack],
);
if (isInitialLoading) { if (isInitialLoading) {
return ( return (
@ -135,15 +172,16 @@ const RollingCounter: React.FC<RollingCounterProps> = ({ numberString }) => {
return ( return (
<div className="flex items-center justify-center bg-lightPrimary text-white py-4 px-6 rounded-full"> <div className="flex items-center justify-center bg-lightPrimary text-white py-4 px-6 rounded-full">
{numbers.map((num, index) => ( {currentNumbers.map((num, index) => (
<RollingDigit <RollingDigit
key={`${index}-${num}`} key={`${index}`} // Simplified key to prevent re-renders
targetValue={num} targetValue={num}
index={index} index={index}
onAnimationComplete={() => handleAnimationComplete(index)} onAnimationComplete={() => handleAnimationComplete(index)}
isStopped={isStopped[index]} isStopped={isStopped[index] && !isTransitioning}
showHyphen={(index + 1) % 2 === 0 && index !== numbers.length - 1} showHyphen={(index + 1) % 2 === 0 && index !== numbers.length - 1}
totalDigits={numbers.length} totalDigits={numbers.length}
isRollingBack={isRollingBack}
/> />
))} ))}
</div> </div>