I’m trying to create a countdown timer in React using Framer Motion, where the digits change with a flip effect. I want the top half of the card to “fall” or “flip” down when the digit changes, creating a smooth animation effect. However, I’m having trouble getting the flip animation to work correctly. Here’s the code I have so far:
import React, { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
const Countdown = () => {
const initialCount = 2 * 24 * 60 * 60
const [count, setCount] = useState(initialCount)
// useEffect para decrementar a contagem a cada segundo
useEffect(() => {
if (count > 0) {
const timer = setTimeout(() => setCount(count - 1), 1000)
return () => clearTimeout(timer)
}
}, [count])
// Função para formatar o tempo em dias, horas, minutos e segundos
const formatTime = (seconds: number) => {
const days = Math.floor(seconds / (24 * 60 * 60))
const hours = Math.floor((seconds % (24 * 60 * 60)) / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
return { days, hours, minutes, secs }
}
const { days, hours, minutes, secs } = formatTime(count)
const FlipDigit = ({ digit }: { digit: string }) => {
return (
<div className='relative w-20 h-32 bg-gray-800 text-white rounded-lg shadow-lg overflow-hidden'>
<div className='absolute inset-0 flex flex-col'>
<div className='h-1/2 w-full overflow-hidden'>
<AnimatePresence mode='popLayout'>
<motion.div
key={`top-${digit}`}
initial={{ rotateX: 0 }}
animate={{ rotateX: 0 }}
exit={{ rotateX: 90 }}
transition={{ duration: 0.3, ease: 'easeInOut' }}
style={{
transformOrigin: 'bottom',
backfaceVisibility: 'hidden',
boxShadow: '0 10px 20px rgba(0, 0, 0, 0.5)',
}}
className='absolute inset-0 flex items-center justify-center bg-gray-700'
>
<span className='text-6xl'>{digit}</span>
</motion.div>
</AnimatePresence>
</div>
<div className='h-1/2 w-full overflow-hidden'>
<div className='absolute inset-0 flex items-center justify-center bg-gray-700'>
<span className='text-6xl'>{digit}</span>
</div>
</div>
</div>
<div className='absolute top-1/2 w-full h-[1px] bg-gray-600'></div>
</div>
)
}
// Componente para a unidade de tempo (dias, horas, etc.)
const FlipUnit = ({ value, label }: { value: number; label: string }) => {
const paddedValue = String(value).padStart(2, '0')
return (
<div className='flex flex-col items-center'>
<FlipDigit digit={paddedValue} />
<div className='mt-2 text-xl text-gray-400'>{label}</div>
</div>
)
}
return (
<div className='flex justify-center items-center h-screen bg-gray-900'>
<div className='flex space-x-8'>
<FlipUnit value={days} label='DAYS' />
<FlipUnit value={hours} label='HOURS' />
<FlipUnit value={minutes} label='MINUTES' />
<FlipUnit value={secs} label='SECONDS' />
</div>
</div>
)
}
export default Countdown