Skip to content

Commit a7a71ec

Browse files
authored
Merge pull request #7 from SupShadow/claude/optimize-android-01FydqeEo9ecgE5KUcgRxuHZ
Optimize everything for Android phones
2 parents 03a270c + 892cb18 commit a7a71ec

8 files changed

Lines changed: 881 additions & 94 deletions

components/ClickExplosion.tsx

Lines changed: 45 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useEffect, useState, useCallback, useRef } from "react";
44
import { motion, AnimatePresence } from "framer-motion";
5-
import { useIsMobile } from "@/hooks";
5+
import { usePerformanceOptimizations, useHapticFeedback } from "@/hooks";
66

77
interface Particle {
88
id: number;
@@ -40,10 +40,14 @@ export default function ClickExplosion() {
4040
const [explosions, setExplosions] = useState<Explosion[]>([]);
4141
const [mounted, setMounted] = useState(false);
4242
const [isWarmedUp, setIsWarmedUp] = useState(false);
43-
const isMobile = useIsMobile();
43+
const perf = usePerformanceOptimizations();
44+
const haptic = useHapticFeedback();
4445
// Track if we've done the initial "warm-up" animation
4546
const warmupDoneRef = useRef(false);
4647

48+
// Determine if we're in a low-performance mode
49+
const isLowPerformance = perf.level === "low" || perf.level === "ultra-low" || perf.level === "medium";
50+
4751
useEffect(() => {
4852
setMounted(true);
4953

@@ -60,28 +64,34 @@ export default function ClickExplosion() {
6064
}, []);
6165

6266
const createExplosion = useCallback((x: number, y: number) => {
63-
// Significantly fewer particles on mobile (6-8 vs 12-20)
64-
const particleCount = isMobile
65-
? 6 + Math.floor(Math.random() * 3) // 6-8 particles on mobile
66-
: 12 + Math.floor(Math.random() * 8); // 12-20 particles on desktop
67+
// Skip explosions entirely on ultra-low performance
68+
if (perf.level === "ultra-low") return;
69+
70+
// Trigger haptic feedback on click
71+
haptic.tap();
72+
73+
// Adjust particle count based on performance level
74+
const particleCount = isLowPerformance
75+
? 6 + Math.floor(Math.random() * 3) // 6-8 particles on low performance
76+
: 12 + Math.floor(Math.random() * 8); // 12-20 particles on high performance
6777

6878
const particles: Particle[] = [];
69-
const colors = isMobile ? PARTICLE_COLORS_MOBILE : PARTICLE_COLORS;
79+
const colors = isLowPerformance ? PARTICLE_COLORS_MOBILE : PARTICLE_COLORS;
7080

7181
for (let i = 0; i < particleCount; i++) {
7282
particles.push({
7383
id: i,
7484
x: 0,
7585
y: 0,
7686
angle: (Math.PI * 2 * i) / particleCount + (Math.random() - 0.5) * 0.5,
77-
// Shorter distance on mobile for snappier feel
78-
velocity: isMobile
79-
? 50 + Math.random() * 60 // 50-110px on mobile
80-
: 80 + Math.random() * 120, // 80-200px on desktop
81-
// Slightly smaller particles on mobile
82-
size: isMobile
83-
? 2 + Math.random() * 2 // 2-4px on mobile
84-
: 2 + Math.random() * 4, // 2-6px on desktop
87+
// Shorter distance on low performance for snappier feel
88+
velocity: isLowPerformance
89+
? 50 + Math.random() * 60 // 50-110px on low performance
90+
: 80 + Math.random() * 120, // 80-200px on high performance
91+
// Slightly smaller particles on low performance
92+
size: isLowPerformance
93+
? 2 + Math.random() * 2 // 2-4px on low performance
94+
: 2 + Math.random() * 4, // 2-6px on high performance
8595
color: colors[Math.floor(Math.random() * colors.length)],
8696
});
8797
}
@@ -95,11 +105,11 @@ export default function ClickExplosion() {
95105

96106
setExplosions(prev => [...prev, explosion]);
97107

98-
// Remove explosion after animation (shorter on mobile)
108+
// Remove explosion after animation (shorter on low performance)
99109
setTimeout(() => {
100110
setExplosions(prev => prev.filter(e => e.id !== explosion.id));
101-
}, isMobile ? 400 : 600);
102-
}, [isMobile]);
111+
}, isLowPerformance ? 400 : 600);
112+
}, [perf.level, isLowPerformance, haptic]);
103113

104114
useEffect(() => {
105115
if (!mounted) return;
@@ -141,29 +151,29 @@ export default function ClickExplosion() {
141151
willChange: "transform",
142152
}}
143153
>
144-
{/* Central flash - smaller on mobile */}
154+
{/* Central flash - smaller on low performance */}
145155
<motion.div
146156
className="absolute -translate-x-1/2 -translate-y-1/2 rounded-full"
147157
style={{
148158
willChange: "width, height, opacity",
149159
transform: "translateZ(0)",
150160
}}
151161
initial={{
152-
width: isMobile ? 8 : 10,
153-
height: isMobile ? 8 : 10,
162+
width: isLowPerformance ? 8 : 10,
163+
height: isLowPerformance ? 8 : 10,
154164
opacity: 1,
155165
background: "radial-gradient(circle, rgba(255,255,255,0.9) 0%, rgba(255,69,0,0.8) 50%, transparent 100%)"
156166
}}
157167
animate={{
158-
width: isMobile ? 25 : 40,
159-
height: isMobile ? 25 : 40,
168+
width: isLowPerformance ? 25 : 40,
169+
height: isLowPerformance ? 25 : 40,
160170
opacity: 0
161171
}}
162172
exit={{ opacity: 0 }}
163-
transition={{ duration: isMobile ? 0.2 : 0.3, ease: "easeOut" }}
173+
transition={{ duration: isLowPerformance ? 0.2 : 0.3, ease: "easeOut" }}
164174
/>
165175

166-
{/* Particles - no boxShadow on mobile for better performance */}
176+
{/* Particles - no boxShadow on low performance for better performance */}
167177
{explosion.particles.map(particle => (
168178
<motion.div
169179
key={particle.id}
@@ -172,8 +182,8 @@ export default function ClickExplosion() {
172182
width: particle.size,
173183
height: particle.size,
174184
backgroundColor: particle.color,
175-
// Skip expensive boxShadow on mobile
176-
boxShadow: isMobile ? undefined : `0 0 ${particle.size * 2}px ${particle.color}`,
185+
// Skip expensive boxShadow on low performance
186+
boxShadow: perf.enableBoxShadow ? `0 0 ${particle.size * 2}px ${particle.color}` : undefined,
177187
left: -particle.size / 2,
178188
top: -particle.size / 2,
179189
willChange: "transform, opacity",
@@ -194,28 +204,28 @@ export default function ClickExplosion() {
194204
}}
195205
exit={{ opacity: 0 }}
196206
transition={{
197-
// Shorter, more consistent duration on mobile
198-
duration: isMobile ? 0.25 : 0.4 + Math.random() * 0.2,
207+
// Shorter, more consistent duration on low performance
208+
duration: isLowPerformance ? 0.25 : 0.4 + Math.random() * 0.2,
199209
ease: [0.25, 0.46, 0.45, 0.94], // easeOutQuad
200210
}}
201211
/>
202212
))}
203213

204-
{/* Ring expand effect - smaller on mobile */}
214+
{/* Ring expand effect - smaller on low performance */}
205215
<motion.div
206216
className="absolute -translate-x-1/2 -translate-y-1/2 rounded-full border border-signal/50"
207217
style={{
208218
willChange: "width, height, opacity",
209219
transform: "translateZ(0)",
210220
}}
211-
initial={{ width: 0, height: 0, opacity: isMobile ? 0.6 : 0.8 }}
212-
animate={{ width: isMobile ? 40 : 60, height: isMobile ? 40 : 60, opacity: 0 }}
221+
initial={{ width: 0, height: 0, opacity: isLowPerformance ? 0.6 : 0.8 }}
222+
animate={{ width: isLowPerformance ? 40 : 60, height: isLowPerformance ? 40 : 60, opacity: 0 }}
213223
exit={{ opacity: 0 }}
214-
transition={{ duration: isMobile ? 0.25 : 0.4, ease: "easeOut" }}
224+
transition={{ duration: isLowPerformance ? 0.25 : 0.4, ease: "easeOut" }}
215225
/>
216226

217-
{/* Spark lines - skip on mobile for better performance */}
218-
{!isMobile && [0, 1, 2, 3].map(i => (
227+
{/* Spark lines - skip on low performance for better performance */}
228+
{perf.enableComplexAnimations && [0, 1, 2, 3].map(i => (
219229
<motion.div
220230
key={`spark-${i}`}
221231
className="absolute bg-signal/80"

0 commit comments

Comments
 (0)