last work on lottery/

This commit is contained in:
Kakabay 2025-01-09 19:53:20 +05:00
parent 28d98cd790
commit 687a7f0f81
5 changed files with 117 additions and 69 deletions

View File

@ -329,6 +329,11 @@ big {
.rolling-number { .rolling-number {
@apply text-[24px] px-1; @apply text-[24px] px-1;
} }
.slot-seperator {
content: url('/dash-md.svg');
@apply !mx-1;
}
} }
@media (max-width: 320px) { @media (max-width: 320px) {

View File

@ -1,5 +1,5 @@
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
interface AnimatedTextProps { interface AnimatedTextProps {
text: string; text: string;
@ -23,31 +23,38 @@ const AnimatedText = ({
const characters = text.split(''); const characters = text.split('');
return ( return (
<div className="overflow-hidden"> <motion.div className="overflow-hidden">
<motion.p <motion.p
className={cn(`flex `, className)} className={cn(`flex`, className)}
initial={{ translateY: initialY, opacity: 0 }} variants={{
animate={{ translateY: 0, opacity: 1 }} enter: {
exit={{ translateY: exitY, opacity: 0 }}> transition: { staggerChildren: 1, delayChildren: 0.5 },
{characters.map((character, i) => ( },
<motion.div exit: {
key={i} transition: { staggerChildren: 0.05, staggerDirection: -1 },
initial={{ translateY: initialY, opacity: 0 }} },
animate={{ translateY: 0, opacity: 1 }} }}>
exit={{ translateY: exitY, opacity: 0 }} <AnimatePresence>
transition={{ {characters.map((character, i) => (
duration, <motion.div
delay: characterDelay + (i / 2) * duration, key={i}
ease: 'easeOut', initial={{ translateY: '-100%', opacity: 0 }}
}} animate={{ translateY: 0, opacity: 1 }}
className={cn(`block `, characterClassName, { exit={{ translateY: '100%', opacity: 0 }}
'animate-dotsFlash': text === '...', transition={{
})}> duration,
{character} delay: characterDelay + (i / 5) * duration,
</motion.div> ease: 'easeOut',
))} }}
className={cn(`block`, characterClassName, {
'animate-dotsFlash': text === '...',
})}>
{character === ' ' ? '\u00A0' : character}
</motion.div>
))}
</AnimatePresence>
</motion.p> </motion.p>
</div> </motion.div>
); );
}; };

View File

@ -3,6 +3,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import ReactConfetti from 'react-confetti'; import ReactConfetti from 'react-confetti';
import { useWindowSize } from 'react-use'; import { useWindowSize } from 'react-use';
import { useMediaQuery } from 'usehooks-ts';
const Confetti = ({ const Confetti = ({
numberOfPieces = 200, numberOfPieces = 200,
@ -21,13 +22,15 @@ const Confetti = ({
'#FF3131', '#FF3131',
]; ];
const mobile = useMediaQuery('(max-width: 426px)');
return ( return (
<div className="fixed top-0 left-0 z-50"> <div className="fixed top-0 left-0 z-50">
<ReactConfetti <ReactConfetti
width={width} width={width}
height={height} height={height}
recycle={showConfetti} recycle={showConfetti}
numberOfPieces={numberOfPieces} numberOfPieces={mobile ? numberOfPieces / 3 : numberOfPieces}
tweenDuration={500} tweenDuration={500}
// run={true} // run={true}
colors={colors} colors={colors}

View File

@ -8,9 +8,10 @@ import { useWindowSize } from 'react-use';
import AnimatedText from '@/components/common/AnimatedText'; import AnimatedText from '@/components/common/AnimatedText';
import { useWebsocketLottery } from '@/hooks/useWebSocketLottery'; import { useWebsocketLottery } from '@/hooks/useWebSocketLottery';
import Confetti from '../common/Confetti'; import Confetti from '../common/Confetti';
import { AnimatePresence, motion } from 'framer-motion';
const WEBSOCKET_URL = 'wss://sms.turkmentv.gov.tm/ws/lottery?dst=0506'; const WEBSOCKET_URL = 'wss://sms.turkmentv.gov.tm/ws/lottery?dst=0506';
const SLOT_COUNTER_DURATION = 100000; const SLOT_COUNTER_DURATION = 30000;
const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => { const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) => {
const [winners, setWinners] = useState<LotteryWinnerDataSimplified[]>([]); const [winners, setWinners] = useState<LotteryWinnerDataSimplified[]>([]);
@ -39,7 +40,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
setWinners(simplifiedWinners); setWinners(simplifiedWinners);
setCurrentNumber(lotteryData.data.winners.at(-1)?.ticket || '00-00-00-00-00'); setCurrentNumber(lotteryData.data.winners.at(-1)?.ticket || '00-00-00-00-00');
setWinnerSelectingStatus('selected'); setWinnerSelectingStatus('selected');
setTopText('Ýeňiji'); setTopText(`${lotteryData.data.winners.at(-1)?.winner_no}-nji(y) ýeňiji`);
setBottomText(lotteryData.data.winners.at(-1)?.client || ''); setBottomText(lotteryData.data.winners.at(-1)?.client || '');
setIsConfettiActive(true); setIsConfettiActive(true);
} }
@ -60,9 +61,7 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
ticket: newWinner.ticket, ticket: newWinner.ticket,
}; };
setTimeout(() => { setIsConfettiActive(false);
setIsConfettiActive(false);
}, 1000);
setTopText(`${winnerData.winner_no}-nji(y) ýeňiji saýlanýar`); setTopText(`${winnerData.winner_no}-nji(y) ýeňiji saýlanýar`);
setBottomText('...'); setBottomText('...');
setWinnerSelectingStatus('is-selecting'); setWinnerSelectingStatus('is-selecting');
@ -70,12 +69,12 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
setCurrentNumber(winnerData.ticket); setCurrentNumber(winnerData.ticket);
setTimeout(() => { setTimeout(() => {
setTopText('Ýeniji'); setTopText(`${winnerData.winner_no}-nji(y) ýeňiji`);
setBottomText(winnerData.client); setBottomText(winnerData.client);
setWinnerSelectingStatus('selected'); setWinnerSelectingStatus('selected');
setIsConfettiActive(true); setIsConfettiActive(true);
setWinners((prev) => [...prev, winnerData]); setWinners((prev) => [...prev, winnerData]);
}, SLOT_COUNTER_DURATION + 500); }, SLOT_COUNTER_DURATION);
}); });
return () => { return () => {
@ -83,6 +82,25 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
}; };
}, [subscribeToMessages]); }, [subscribeToMessages]);
// useEffect(() => {
// setTimeout(() => {
// setIsConfettiActive(false);
// setTopText(`${1}-nji(y) ýeňiji saýlanýar`);
// setBottomText('...');
// setWinnerSelectingStatus('is-selecting');
// // setPendingWinner(winnerData);
// setCurrentNumber('55-44-33-22-11');
// setTimeout(() => {
// setTopText('Ýeniji');
// setBottomText('99361245555');
// setWinnerSelectingStatus('selected');
// setIsConfettiActive(true);
// // setWinners((prev) => [...prev, winnerData]);
// }, SLOT_COUNTER_DURATION);
// }, 10000);
// }, []);
return ( return (
<section> <section>
{wsStatus === 'error' && ( {wsStatus === 'error' && (
@ -95,47 +113,62 @@ const LotteryWinnersSection = ({ lotteryStatus }: { lotteryStatus: string }) =>
style={{ style={{
background: 'linear-gradient(180deg, #F0ECF4 0%, #E1E0FF 43.5%)', background: 'linear-gradient(180deg, #F0ECF4 0%, #E1E0FF 43.5%)',
}}> }}>
<div className="flex items-center justify-center w-full min-h-[240px]"> <AnimatePresence>
{winnerSelectingStatus === 'not-selected' ? ( <div className="flex items-center justify-center w-full sm:min-h-[240px] pt-6">
// <TextMaskReveal {winnerSelectingStatus === 'not-selected' ? (
// text={topText} // <TextMaskReveal
// className="text-center flex items-center justify-center text-[100px] leading-[108px] text-[#E65E19]" // text={topText}
// duration={0.5} // className="text-center flex items-center justify-center text-[100px] leading-[108px] text-[#E65E19]"
// /> // duration={0.5}
<AnimatedText // />
text={topText} <AnimatedText
className="text-center flex items-center justify-center text-[100px] leading-[108px] text-[#E65E19]" key={topText}
duration={0.4} text={topText}
/> className="text-center flex items-center justify-center sm:text-[100px] text-[48px] leading-[120%] text-[#E65E19]"
) : ( duration={0.4}
<div className="flex flex-col items-center justify-center"> />
{/* <TextMaskReveal ) : (
<motion.div
variants={{
enter: {
transition: { staggerChildren: 1, delayChildren: 0.5 },
},
exit: {
transition: { staggerChildren: 0.05, staggerDirection: -1 },
},
}}
className="flex flex-col items-center justify-center">
{/* <TextMaskReveal
text={topText} text={topText}
className="text-center text-[56px] leading-[64px] text-[#E65E19]" className="text-center text-[56px] leading-[64px] text-[#E65E19]"
duration={0.4} duration={0.4}
/> */} /> */}
<AnimatedText
text={topText}
className="text-center text-[56px] leading-[64px] text-[#E65E19]"
duration={0.4}
/>
{bottomText && (
// <TextMaskReveal
// text={bottomText}
// className="text-center text-[80px] leading-[88px] text-[#E65E19]"
// duration={0.4}
// startDelay={3}
// />
<AnimatedText <AnimatedText
text={bottomText} key={topText}
className="text-center text-[80px] leading-[88px] text-[#E65E19]" text={topText}
className="text-center sm:text-[56px] text-[24px] w-full leading-[120%] text-[#E65E19]"
duration={0.4} duration={0.4}
characterDelay={1.3}
/> />
)} {bottomText && (
</div> // <TextMaskReveal
)} // text={bottomText}
</div> // className="text-center text-[80px] leading-[88px] text-[#E65E19]"
// duration={0.4}
// startDelay={3}
// />
<AnimatedText
key={bottomText}
text={bottomText}
className="text-center sm:text-[80px] text-[32px] leading-[120%] text-[#E65E19]"
duration={0.4}
// characterDelay={2}
/>
)}
</motion.div>
)}
</div>
</AnimatePresence>
<div className="z-10"> <div className="z-10">
{currentNumber && <LotterySlotCounter numberString={currentNumber} />} {currentNumber && <LotterySlotCounter numberString={currentNumber} />}
</div> </div>

View File

@ -27,7 +27,7 @@ const LotterySlotCounter = ({ numberString }: LotterySlotCounterProps) => {
alt="roller-triangle" alt="roller-triangle"
width={24} width={24}
height={24} height={24}
className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-1/2 z-20" className="absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2/3 z-20"
/> />
) : ( ) : (
<Image <Image
@ -45,7 +45,7 @@ const LotterySlotCounter = ({ numberString }: LotterySlotCounterProps) => {
alt="roller-triangle" alt="roller-triangle"
width={24} width={24}
height={24} height={24}
className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 z-20 rotate-180" className="absolute right-0 top-1/2 -translate-y-1/2 translate-x-2/3 z-20 rotate-180"
/> />
) : ( ) : (
<Image <Image
@ -58,7 +58,7 @@ const LotterySlotCounter = ({ numberString }: LotterySlotCounterProps) => {
)} )}
<div <div
className="flex items-center h-fit md:max-w-[1132px] sm:max-w-[640px] max-w-[324px] w-full justify-center text-white md:py-4 md:px-6 rounded-full overflow-y-hidden overflow-x-visible relative border-4 border-lightPrimary" className="flex items-center h-fit md:max-w-[1132px] sm:max-w-[640px] max-w-[400px] w-full justify-center text-white md:py-4 md:px-6 rounded-full overflow-y-hidden overflow-x-visible relative border-4 border-lightPrimary"
style={{ style={{
background: background:
'linear-gradient(180deg, #454673 0%, #575992 10.5%, #575992 90%, #454673 100%)', 'linear-gradient(180deg, #454673 0%, #575992 10.5%, #575992 90%, #454673 100%)',