Skip to content

Commit 08463b2

Browse files
committed
FEAT: #224 메인 페이지 애니메이션 롤백
1 parent 4c7e04b commit 08463b2

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

react-app/package-lock.json

Lines changed: 45 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

react-app/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@tanstack/react-query": "^5.74.11",
1616
"axios": "^1.8.4",
1717
"dayjs": "^1.11.13",
18+
"framer-motion": "^12.12.1",
1819
"react": "^18.2.0",
1920
"react-canvas-confetti": "^2.0.7",
2021
"react-card-flip": "^1.2.3",
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { motion, useAnimation } from 'framer-motion';
2+
import { useEffect } from 'react';
3+
4+
interface FloatItemProps {
5+
src: string;
6+
size?: string;
7+
duration?: number;
8+
interval?: number;
9+
}
10+
11+
// 랜덤 이동
12+
const getRandomPosition = () => ({
13+
x: Math.floor(Math.random() * 100 - 50),
14+
y: Math.floor(Math.random() * 100 - 50),
15+
});
16+
17+
const FloatingItem = ({ src, size = 'w-20', duration = 6, interval = 8000 }: FloatItemProps) => {
18+
const controls = useAnimation();
19+
20+
useEffect(() => {
21+
const animateRandomly = () => {
22+
const { x, y } = getRandomPosition();
23+
controls.start({
24+
x: `${x}vw`,
25+
y: `${y}vh`,
26+
transition: {
27+
duration,
28+
ease: 'easeInOut',
29+
},
30+
});
31+
};
32+
33+
animateRandomly(); // 첫 애니메이션
34+
const intervalId = setInterval(animateRandomly, interval);
35+
36+
return () => clearInterval(intervalId);
37+
}, [controls, duration, interval]);
38+
39+
return (
40+
<motion.img
41+
src={src}
42+
className={`absolute ${size} opacity-80 pointer-events-none`}
43+
style={{
44+
left: '50%',
45+
top: '50%',
46+
transform: 'translate(-50%, -50%)', // 중앙에서 시작
47+
}}
48+
animate={controls}
49+
alt="floating item"
50+
/>
51+
);
52+
};
53+
const FloatingBackground = () => {
54+
return (
55+
<div className="fixed inset-0 -z-1000 overflow-hidden pointer-events-none">
56+
<FloatingItem
57+
src="https://noticon-static.tammolo.com/dgggcrkxq/image/upload/v1741777507/noticon/dihnlmrn8aopedon41ur.gif"
58+
size="w-20"
59+
duration={5}
60+
interval={7000}
61+
/>
62+
<FloatingItem
63+
src="https://noticon-static.tammolo.com/dgggcrkxq/image/upload/v1683523256/noticon/kyp3frg9corycovxokx6.gif"
64+
size="w-16"
65+
duration={6}
66+
interval={9000}
67+
/>
68+
<FloatingItem
69+
src="https://noticon-static.tammolo.com/dgggcrkxq/image/upload/v1686670279/noticon/gvasp3qtaz16spmwoorl.gif"
70+
size="w-24"
71+
duration={7}
72+
interval={10000}
73+
/>
74+
</div>
75+
);
76+
};
77+
78+
export default FloatingBackground;

react-app/src/pages/Main.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Button from '@/components/Button/Button';
22
import { useCallback, useEffect, useState } from 'react';
33
import { useNavigate } from 'react-router-dom';
4+
import FloatingBackground from '@/components/FloatingBackground/FloatingBackground';
45

56
const Main = () => {
67
const [restTime, setRestTime] = useState(10);
@@ -43,6 +44,7 @@ const Main = () => {
4344

4445
return (
4546
<div className="flex flex-col justify-center items-center h-full px-4 text-center relative">
47+
<FloatingBackground />
4648
<div className="flex flex-col justify-center gap-6">
4749
<p className="text-base sm:text-lg md:text-xl text-gray-600 font-pb">
4850
{restTime > 0 ? '투표 결과 발표까지 남은 시간' : '투표 집계가 완료되었습니다.'}

0 commit comments

Comments
 (0)