From 9d4178f1de1eff2d03aa00421c1c7c8b1bcf9c11 Mon Sep 17 00:00:00 2001 From: Kakabay <2kakabayashyrberdyew@gmail.com> Date: Fri, 20 Dec 2024 18:23:26 +0500 Subject: [PATCH] rolling counter added --- .../lottery/RollingCounter/RollingCounter.tsx | 215 ++++++++++++++---- .../RollingCounter/RollingCounterWorking.tsx | 143 ++++++++++++ 2 files changed, 310 insertions(+), 48 deletions(-) create mode 100644 components/lottery/RollingCounter/RollingCounterWorking.tsx diff --git a/components/lottery/RollingCounter/RollingCounter.tsx b/components/lottery/RollingCounter/RollingCounter.tsx index 79b5e24..afab738 100644 --- a/components/lottery/RollingCounter/RollingCounter.tsx +++ b/components/lottery/RollingCounter/RollingCounter.tsx @@ -1,67 +1,186 @@ 'use client'; import { motion } from 'framer-motion'; -import { useEffect, useState } from 'react'; +import { useCallback, useMemo, useState, useEffect } from 'react'; interface RollingCounterProps { - numberString: string; // A 10-character string, e.g., "05-12-34-56-78" + numberString: string; } +const ROLLS = 2; +const DIGIT_HEIGHT = 104; +const INITIAL_OFFSET = 38; +const EXTRA_NUMBERS_AFTER = 5; +const EXTRA_NUMBERS_BEFORE = 2; + +const getNumbers = (targetValue: number, previousValue?: number) => { + const numbers = []; + + if (previousValue === undefined) { + // Initial load + for (let i = 0; i < ROLLS; i++) { + for (let n = 0; n < 10; n++) { + numbers.push(n); + } + } + + // Add sequence before target + for (let n = EXTRA_NUMBERS_BEFORE; n > 0; n--) { + numbers.push((targetValue - n + 10) % 10); + } + + numbers.push(targetValue); + + // Add extra numbers after target + for (let n = 1; n <= EXTRA_NUMBERS_AFTER; n++) { + numbers.push((targetValue + n) % 10); + } + } else { + // Keep the previous sequence + for (let i = 0; i < ROLLS; i++) { + for (let n = 0; n < 10; n++) { + numbers.push(n); + } + } + + // Add sequence before previous value + for (let n = EXTRA_NUMBERS_BEFORE; n > 0; n--) { + numbers.push((previousValue - n + 10) % 10); + } + + numbers.push(previousValue); + + // Add complete rolls between previous and target + for (let i = 0; i < ROLLS; i++) { + for (let n = 0; n < 10; n++) { + numbers.push(n); + } + } + + // Add sequence before target + for (let n = EXTRA_NUMBERS_BEFORE; n > 0; n--) { + numbers.push((targetValue - n + 10) % 10); + } + + numbers.push(targetValue); + + // Add extra numbers after target + for (let n = 1; n <= EXTRA_NUMBERS_AFTER; n++) { + numbers.push((targetValue + n) % 10); + } + } + + return numbers; +}; + +const RollingDigit = ({ + targetValue, + index, + onAnimationComplete, + isStopped, + showHyphen, + previousValue, +}: { + targetValue: number; + index: number; + onAnimationComplete: () => void; + isStopped: boolean; + showHyphen: boolean; + previousValue?: number; +}) => { + const numbers = useMemo( + () => getNumbers(targetValue, previousValue), + [targetValue, previousValue], + ); + + return ( +
+
+ + {numbers.map((num, i) => ( +
+ {num} +
+ ))} +
+
+ {showHyphen &&
} +
+ ); +}; + const RollingCounter: React.FC = ({ numberString }) => { - const [rollingValues, setRollingValues] = useState([]); - const [isStopped, setIsStopped] = useState([]); // Track stopped numbers + const [isInitialLoading, setIsInitialLoading] = useState(true); + const [isStopped, setIsStopped] = useState([]); + const [previousNumbers, setPreviousNumbers] = useState([]); + const [isNewNumber, setIsNewNumber] = useState(false); useEffect(() => { - // Parse input string into an array of numbers, removing hyphens - const targetNumbers = numberString + if (!isInitialLoading) { + setPreviousNumbers(numbers); + setIsStopped(new Array(numberString.replace(/-/g, '').length).fill(false)); + setIsNewNumber(true); + } + }, [numberString, isInitialLoading]); + + const numbers = useMemo(() => { + if (!numberString) return []; + + const parsed = numberString .replace(/-/g, '') .split('') .map((char) => parseInt(char, 10)); - setRollingValues(targetNumbers); - setIsStopped(new Array(targetNumbers.length).fill(false)); - }, [numberString]); - const getNumbers = () => Array.from({ length: 10 }, (_, i) => i); + if (isInitialLoading) { + setIsStopped(new Array(parsed.length).fill(false)); + setIsInitialLoading(false); + } + + return parsed; + }, [numberString, isInitialLoading]); + + const handleAnimationComplete = useCallback((index: number) => { + setIsStopped((prev) => { + const newState = [...prev]; + newState[index] = true; + return newState; + }); + }, []); + + if (isInitialLoading) { + return ( +
+ Loading... +
+ ); + } return (
- {rollingValues.map((targetValue, index) => ( -
- {/* Container to display numbers */} -
- { - // Mark this number as stopped - setIsStopped((prev) => { - const newStatus = [...prev]; - newStatus[index] = true; - return newStatus; - }); - }} - className="absolute top-1/2 -translate-y-1/2 flex flex-col -mt-[52px]"> - {getNumbers().map((num) => ( -
- {num} -
- ))} -
-
- {/* Add a hyphen every two digits */} - {(index + 1) % 2 === 0 && index !== rollingValues.length - 1 && ( -
- )} -
+ {numbers.map((num, index) => ( + handleAnimationComplete(index)} + isStopped={isStopped[index]} + showHyphen={(index + 1) % 2 === 0 && index !== numbers.length - 1} + /> ))}
); diff --git a/components/lottery/RollingCounter/RollingCounterWorking.tsx b/components/lottery/RollingCounter/RollingCounterWorking.tsx new file mode 100644 index 0000000..4f0bf74 --- /dev/null +++ b/components/lottery/RollingCounter/RollingCounterWorking.tsx @@ -0,0 +1,143 @@ +'use client'; +import { motion } from 'framer-motion'; +import { useCallback, useMemo, useState } from 'react'; + +interface RollingCounterWorkingProps { + numberString: string; +} + +// Move constants outside component +const ROLLS = 5; +const DIGIT_HEIGHT = 104; +const INITIAL_OFFSET = 38; +const EXTRA_NUMBERS_AFTER = 5; +const EXTRA_NUMBERS_BEFORE = 2; + +// Memoize number generation function +const getNumbers = (targetValue: number) => { + const numbers = []; + + // Add complete rolls + for (let i = 0; i < ROLLS; i++) { + for (let n = 0; n < 10; n++) { + numbers.push(n); + } + } + + // Add sequence before target + for (let n = EXTRA_NUMBERS_BEFORE; n > 0; n--) { + numbers.push((targetValue - n + 10) % 10); + } + + // Add target + numbers.push(targetValue); + + // Add extra numbers after target + for (let n = 1; n <= EXTRA_NUMBERS_AFTER; n++) { + numbers.push((targetValue + n) % 10); + } + + return numbers; +}; + +const RollingDigit = ({ + targetValue, + index, + onAnimationComplete, + isStopped, + showHyphen, +}: { + targetValue: number; + index: number; + onAnimationComplete: () => void; + isStopped: boolean; + showHyphen: boolean; +}) => { + const numbers = useMemo(() => getNumbers(targetValue), [targetValue]); + + const targetIndex = ROLLS * 10 + EXTRA_NUMBERS_BEFORE; + + return ( +
+
+ + {numbers.map((num, i) => ( +
+ {num} +
+ ))} +
+
+ {showHyphen &&
} +
+ ); +}; + +const RollingCounterWorking: React.FC = ({ numberString }) => { + const [isInitialLoading, setIsInitialLoading] = useState(true); + const [isStopped, setIsStopped] = useState([]); + + const numbers = useMemo(() => { + if (!numberString) return []; + + const parsed = numberString + .replace(/-/g, '') + .split('') + .map((char) => parseInt(char, 10)); + + if (isInitialLoading) { + setIsStopped(new Array(parsed.length).fill(false)); + setIsInitialLoading(false); + } + + return parsed; + }, [numberString, isInitialLoading]); + + const handleAnimationComplete = useCallback((index: number) => { + setIsStopped((prev) => { + const newState = [...prev]; + newState[index] = true; + return newState; + }); + }, []); + + if (isInitialLoading) { + return ( +
+ Loading... +
+ ); + } + + return ( +
+ {numbers.map((num, index) => ( + handleAnimationComplete(index)} + isStopped={isStopped[index]} + showHyphen={(index + 1) % 2 === 0 && index !== numbers.length - 1} + /> + ))} +
+ ); +}; + +export default RollingCounterWorking;