Skip to content

Commit e210db5

Browse files
committed
feat: replace Framer Motion with CSS animations and optimize media handling
1 parent cd75fc9 commit e210db5

File tree

10 files changed

+148
-160
lines changed

10 files changed

+148
-160
lines changed

website/app/global.css

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,59 @@
147147
to { opacity: 1; transform: translateY(0); }
148148
}
149149

150+
@keyframes node-enter {
151+
from { opacity: 0; transform: scale(0.8); }
152+
to { opacity: 1; transform: scale(1); }
153+
}
154+
155+
@keyframes connection-enter {
156+
from { opacity: 0; }
157+
to { opacity: 1; }
158+
}
159+
160+
@keyframes dash {
161+
to { stroke-dashoffset: -100; }
162+
}
163+
164+
@keyframes spin-slow {
165+
to { transform: rotate(360deg); }
166+
}
167+
168+
@keyframes progress {
169+
0%, 100% { width: 0%; }
170+
50% { width: 100%; }
171+
}
172+
173+
@keyframes progress-slow {
174+
0%, 100% { width: 0%; }
175+
50% { width: 100%; }
176+
}
177+
178+
@keyframes bar-pulse {
179+
0%, 100% { height: 30%; }
180+
50% { height: 80%; }
181+
}
182+
183+
@keyframes ripple {
184+
0% { transform: scale(0.5); opacity: 0.6; }
185+
100% { transform: scale(2.2); opacity: 0; }
186+
}
187+
188+
@keyframes float-orb {
189+
0%, 100% {
190+
transform: translate(-50%, -50%) translate(0px, 0px) scale(1);
191+
}
192+
25% {
193+
transform: translate(-50%, -50%) translate(50px, -40px) scale(1.1);
194+
}
195+
50% {
196+
transform: translate(-50%, -50%) translate(-30px, 30px) scale(0.9);
197+
}
198+
75% {
199+
transform: translate(-50%, -50%) translate(20px, -20px) scale(1.05);
200+
}
201+
}
202+
150203
@layer utilities {
151204
.animate-marquee {
152205
animation: marquee 40s linear infinite;
@@ -170,5 +223,35 @@
170223
.animate-float-up {
171224
animation: float-up 0.6s ease-out forwards;
172225
}
226+
.animate-node-enter {
227+
animation: node-enter 0.5s ease-out forwards;
228+
animation-fill-mode: both;
229+
}
230+
.animate-connection-enter {
231+
animation: connection-enter 0.3s ease-out forwards;
232+
animation-fill-mode: both;
233+
}
234+
.animate-dash {
235+
animation: dash 3s linear infinite;
236+
}
237+
.animate-spin-slow {
238+
animation: spin-slow 3s linear infinite;
239+
}
240+
.animate-progress {
241+
animation: progress 2s ease-in-out infinite;
242+
}
243+
.animate-progress-slow {
244+
animation: progress-slow 3s ease-in-out infinite;
245+
}
246+
.animate-bar-pulse {
247+
animation: bar-pulse 1.5s ease-in-out infinite;
248+
animation-fill-mode: both;
249+
}
250+
.animate-ripple {
251+
animation: ripple 2.5s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
252+
}
253+
.animate-float-orb {
254+
animation: float-orb 20s ease-in-out infinite;
255+
}
173256
}
174257

website/components/floating-orbs.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { motion } from 'motion/react';
43
import { useMemo } from 'react';
54
import { useIsMobile, useReducedMotion } from '@/hooks/use-mobile';
65

@@ -48,26 +47,16 @@ export function FloatingOrbs() {
4847
return (
4948
<div className="absolute inset-0 overflow-hidden pointer-events-none">
5049
{orbs.map((orb) => (
51-
<motion.div
50+
<div
5251
key={orb.id}
53-
className={`absolute rounded-full blur-3xl ${orb.color}`}
52+
className={`absolute rounded-full blur-3xl ${orb.color} animate-float-orb`}
5453
style={{
5554
width: orb.size,
5655
height: orb.size,
5756
left: `${orb.x}%`,
5857
top: `${orb.y}%`,
59-
transform: 'translate(-50%, -50%)',
60-
}}
61-
animate={{
62-
x: [0, 50, -30, 20, 0],
63-
y: [0, -40, 30, -20, 0],
64-
scale: [1, 1.1, 0.9, 1.05, 1],
65-
}}
66-
transition={{
67-
duration: orb.duration,
68-
delay: orb.delay,
69-
repeat: Infinity,
70-
ease: 'easeInOut',
58+
animationDuration: `${orb.duration}s`,
59+
animationDelay: `${orb.delay}s`,
7160
}}
7261
/>
7362
))}

website/components/hero-visual.tsx

Lines changed: 33 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { motion, AnimatePresence } from 'motion/react';
43
import { clsx } from 'clsx';
54
import { twMerge } from 'tailwind-merge';
65
import React from 'react';
@@ -93,53 +92,44 @@ export function HeroVisual({ nodes, connections }: HeroVisualProps) {
9392
<stop offset="100%" stopColor="#a855f7" stopOpacity="0.8" />
9493
</linearGradient>
9594
</defs>
96-
<AnimatePresence>
97-
{connections.map((conn) => (
98-
<PathWithAnimation
99-
key={conn.id}
100-
d={getPathForConnection(conn, nodes)}
101-
delay={conn.delay || 0}
102-
color={conn.color}
103-
/>
104-
))}
105-
</AnimatePresence>
95+
{connections.map((conn, i) => (
96+
<ConnectionPath
97+
key={conn.id}
98+
d={getPathForConnection(conn, nodes)}
99+
delay={i * 0.2}
100+
color={conn.color}
101+
/>
102+
))}
106103
</svg>
107104

108105
{/* Nodes */}
109-
<AnimatePresence>
110-
{nodes.map((node, i) => (
111-
<StaticNode key={node.id} data={node} delay={0.2 + (i * 0.1)} />
112-
))}
113-
</AnimatePresence>
106+
{nodes.map((node, i) => (
107+
<StaticNode key={node.id} data={node} delay={i * 0.1} />
108+
))}
114109
</div>
115110
</div>
116111
</div>
117112
);
118113
}
119114

120-
// Static node - no hover interactions
115+
// Static node with CSS animations
121116
function StaticNode({ data, delay }: { data: NodeData; delay: number }) {
122117
const { x, y, width, height, type, title } = data;
123118

124119
return (
125-
<motion.div
126-
initial={{ opacity: 0, scale: 0.8 }}
127-
animate={{ opacity: 1, scale: 1 }}
128-
exit={{ opacity: 0, scale: 0.8 }}
129-
transition={{ delay, duration: 0.5, type: "spring" }}
120+
<div
130121
style={{
131122
position: 'absolute',
132123
left: x,
133124
top: y,
134125
width,
135126
height,
127+
animationDelay: `${delay}s`,
136128
}}
137-
className="font-mono"
129+
className="font-mono animate-node-enter"
138130
>
139131
{/* Shadow layer */}
140-
<div
141-
className="absolute inset-4 rounded-xl bg-blue-500/10 dark:bg-black/40 blur-xl opacity-40"
142-
/>
132+
<div className="absolute inset-4 rounded-xl bg-blue-500/10 dark:bg-black/40 blur-xl opacity-40" />
143133

144134
{/* Node card */}
145135
<div
@@ -175,7 +165,7 @@ function StaticNode({ data, delay }: { data: NodeData; delay: number }) {
175165
{/* Glass highlight */}
176166
<div className="absolute inset-0 rounded-xl bg-gradient-to-br from-white/20 to-transparent opacity-30 pointer-events-none" />
177167
</div>
178-
</motion.div>
168+
</div>
179169
);
180170
}
181171

@@ -203,19 +193,11 @@ function NodeContent({ type }: { type: NodeType }) {
203193
case 'process': return (
204194
<div className="flex items-center gap-2 h-full">
205195
<div className="w-8 h-8 rounded-lg bg-blue-600/10 dark:bg-blue-500/10 border-2 border-blue-600/20 dark:border-blue-500/20 flex items-center justify-center">
206-
<motion.div
207-
animate={{ rotate: 360 }}
208-
transition={{ duration: 3, repeat: Infinity, ease: "linear" }}
209-
className="w-4 h-4 rounded-full border-2 border-blue-600/40 dark:border-blue-500/40 border-t-blue-700 dark:border-t-blue-400"
210-
/>
196+
<div className="w-4 h-4 rounded-full border-2 border-blue-600/40 dark:border-blue-500/40 border-t-blue-700 dark:border-t-blue-400 animate-spin-slow" />
211197
</div>
212198
<div className="space-y-1 flex-1">
213199
<div className="h-1.5 w-full bg-slate-300/40 dark:bg-white/5 rounded-full overflow-hidden">
214-
<motion.div
215-
animate={{ width: ['0%', '100%'] }}
216-
transition={{ duration: 2, repeat: Infinity }}
217-
className="h-full bg-blue-600 dark:bg-blue-500/40"
218-
/>
200+
<div className="h-full bg-blue-600 dark:bg-blue-500/40 animate-progress" />
219201
</div>
220202
</div>
221203
</div>
@@ -230,11 +212,10 @@ function NodeContent({ type }: { type: NodeType }) {
230212
case 'output': return (
231213
<div className="flex items-end gap-1 h-full pb-1">
232214
{[30, 60, 45, 80].map((h, i) => (
233-
<motion.div
215+
<div
234216
key={i}
235-
animate={{ height: [`${h/2}%`, `${h}%`, `${h/2}%`] }}
236-
transition={{ duration: 1.5, delay: i * 0.1, repeat: Infinity }}
237-
className="flex-1 bg-purple-600/40 dark:bg-white/10 rounded-t-[1px] border-t-2 border-purple-600 dark:border-white/20"
217+
style={{ animationDelay: `${i * 0.1}s` }}
218+
className="flex-1 bg-purple-600/40 dark:bg-white/10 rounded-t-[1px] border-t-2 border-purple-600 dark:border-white/20 animate-bar-pulse"
238219
/>
239220
))}
240221
</div>
@@ -253,25 +234,19 @@ function NodeContent({ type }: { type: NodeType }) {
253234
<div className="h-1 w-1 rounded-full bg-indigo-600/60 dark:bg-white/10" />
254235
<div className="h-1 w-1 rounded-full bg-indigo-600/30 dark:bg-white/5" />
255236
</div>
256-
<div className="h-1.5 w-full bg-indigo-600/10 dark:bg-white/5 rounded-full border border-indigo-600/20 dark:border-white/5">
257-
<motion.div
258-
animate={{ width: ['0%', '100%'] }}
259-
transition={{ duration: 3, repeat: Infinity }}
260-
className="h-full bg-indigo-600/60 dark:bg-indigo-400/40 rounded-full"
261-
/>
237+
<div className="h-1.5 w-full bg-indigo-600/10 dark:bg-white/5 rounded-full border border-indigo-600/20 dark:border-white/5 overflow-hidden">
238+
<div className="h-full bg-indigo-600/60 dark:bg-indigo-400/40 rounded-full animate-progress-slow" />
262239
</div>
263240
</div>
264241
);
265242
case 'broadcast': return (
266243
<div className="flex items-center justify-center h-full pt-1">
267244
<div className="relative flex items-center justify-center w-12 h-12">
268-
{[0, 0.6, 1.2].map((d, i) => (
269-
<motion.div
245+
{[0, 1, 2].map((i) => (
246+
<div
270247
key={i}
271-
initial={{ scale: 0.5, opacity: 0 }}
272-
animate={{ scale: 2.2, opacity: 0 }}
273-
transition={{ duration: 2.5, repeat: Infinity, ease: [0.165, 0.84, 0.44, 1], delay: d }}
274-
className="absolute inset-0 rounded-full border-2 border-pink-600/40 dark:border-white/20"
248+
style={{ animationDelay: `${i * 0.6}s` }}
249+
className="absolute inset-0 rounded-full border-2 border-pink-600/40 dark:border-white/20 animate-ripple"
275250
/>
276251
))}
277252
<div className="relative w-3.5 h-3.5 bg-pink-600 dark:bg-pink-400 rounded-full shadow-[0_0_15px_rgba(236,72,153,0.6)] dark:shadow-[0_0_15px_rgba(236,72,153,0.4)]" />
@@ -281,29 +256,18 @@ function NodeContent({ type }: { type: NodeType }) {
281256
}
282257
}
283258

284-
function PathWithAnimation({ d, delay, color }: { d: string, delay: number, color?: string }) {
259+
function ConnectionPath({ d, delay, color }: { d: string; delay: number; color?: string }) {
285260
return (
286-
<motion.g initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.3 }}>
261+
<g style={{ animationDelay: `${delay}s` }} className="animate-connection-enter">
287262
<path d={d} stroke={color || "url(#gradient-line)"} strokeWidth="3" fill="none" className="opacity-20" />
288-
<motion.path
263+
<path
289264
d={d}
290265
stroke={color || "url(#gradient-line)"}
291266
strokeWidth="3"
292267
fill="none"
293268
strokeDasharray="8 8"
294-
initial={{ strokeDashoffset: 100, opacity: 0 }}
295-
animate={{ strokeDashoffset: 0, opacity: 1 }}
296-
transition={{ delay, duration: 3, ease: "linear", repeat: Infinity }}
297-
/>
298-
<motion.circle
299-
r="4"
300-
fill={color || "#3b82f6"}
301-
initial={{ offsetDistance: "0%", opacity: 0 }}
302-
animate={{ offsetDistance: "100%", opacity: [0, 1, 1, 0] }}
303-
style={{ offsetPath: `path('${d}')` }}
304-
transition={{ duration: 3, repeat: Infinity, ease: "easeInOut", delay }}
305-
className="shadow-[0_0_15px_currentColor]"
269+
className="animate-dash"
306270
/>
307-
</motion.g>
271+
</g>
308272
);
309273
}

0 commit comments

Comments
 (0)