Files

544 lines
14 KiB
TypeScript

import React from 'react';
import { Animated, Easing, View, StyleSheet } from 'react-native';
import Svg, { Path, Ellipse, Circle } from 'react-native-svg';
import { theme } from '../theme';
interface ZenCenterProps {
isPlaying: boolean;
}
const ZenFigure = () => (
<View style={styles.zenFigure}>
<Svg viewBox="0 0 200 210" style={styles.zenSvg}>
{/* Ground shadow */}
<Ellipse cx="100" cy="178" rx="44" ry="5.5" fill="rgba(232,184,109,0.08)" />
{/* Lower body - Full lotus base */}
<Ellipse cx="100" cy="158" rx="46" ry="14" fill="#c8973e" opacity={0.7} />
{/* Left knee */}
<Path
d="M 54 150 C 44 152, 36 158, 36 165 C 36 171, 42 175, 52 174 C 60 173, 68 167, 72 160 C 74 155, 70 149, 64 148 Z"
fill="#d4a045"
opacity={0.78}
/>
{/* Right knee */}
<Path
d="M 146 150 C 156 152, 164 158, 164 165 C 164 171, 158 175, 148 174 C 140 173, 132 167, 128 160 C 126 155, 130 149, 136 148 Z"
fill="#d4a045"
opacity={0.78}
/>
{/* Left foot */}
<Path
d="M 124 148 C 132 144, 142 145, 146 150 C 148 153, 145 157, 138 157 C 131 157, 124 153, 122 150 Z"
fill="#c8973e"
opacity={0.72}
/>
{/* Right foot */}
<Path
d="M 76 148 C 68 144, 58 145, 54 150 C 52 153, 55 157, 62 157 C 69 157, 76 153, 78 150 Z"
fill="#c8973e"
opacity={0.72}
/>
{/* Torso */}
<Path
d="M 72 152 C 70 140, 69 126, 70 114 C 71 102, 75 92, 82 85 C 88 79, 94 77, 100 76 C 106 77, 112 79, 118 85 C 125 92, 129 102, 130 114 C 131 126, 130 140, 128 152 C 120 157, 110 160, 100 160 C 90 160, 80 157, 72 152 Z"
fill="#e0a84e"
opacity={0.88}
/>
{/* Chest line */}
<Path
d="M 100 88 C 99 108, 99 130, 100 152"
stroke="#c8882a"
strokeWidth={0.8}
opacity={0.18}
strokeLinecap="round"
/>
{/* Left upper arm */}
<Path
d="M 70 114 C 64 116, 58 122, 56 130 C 54 136, 56 142, 62 146 C 68 148, 75 146, 78 140 C 74 136, 70 128, 70 118 Z"
fill="#d4a045"
opacity={0.82}
/>
{/* Right upper arm */}
<Path
d="M 130 114 C 136 116, 142 122, 144 130 C 146 136, 144 142, 138 146 C 132 148, 125 146, 122 140 C 126 136, 130 128, 130 118 Z"
fill="#d4a045"
opacity={0.82}
/>
{/* Left hand */}
<Path
d="M 84 154 C 80 152, 76 152, 75 155 C 74 158, 76 162, 82 163 C 88 164, 96 162, 98 158 C 98 155, 92 154, 84 154 Z"
fill="#d4a045"
opacity={0.8}
/>
{/* Right hand */}
<Path
d="M 102 152 C 98 150, 94 150, 92 153 C 90 156, 91 160, 96 162 C 102 163, 110 161, 114 157 C 116 154, 112 151, 106 151 Z"
fill="#c8973e"
opacity={0.78}
/>
{/* Thumb tips touching */}
<Ellipse cx="99" cy="153" rx="4" ry="2.5" fill="#e8b86d" opacity={0.55} />
{/* Neck */}
<Path
d="M 93 78 C 93 74, 96 70, 100 69 C 104 70, 107 74, 107 78 L 107 86 L 93 86 Z"
fill="#e0a84e"
opacity={0.88}
/>
{/* Head */}
<Path
d="M 82 54 C 82 36, 90 26, 100 25 C 110 26, 118 36, 118 54 C 118 66, 112 74, 106 77 L 100 79 L 94 77 C 88 74, 82 66, 82 54 Z"
fill="#e0a84e"
opacity={0.88}
/>
{/* Hair */}
<Path
d="M 82 50 C 82 36, 90 26, 100 25 C 110 26, 118 36, 118 50 C 112 43, 106 39, 100 38 C 94 39, 88 43, 82 50 Z"
fill="#b8812a"
opacity={0.8}
/>
{/* Face features */}
{/* Left brow */}
<Path
d="M 89 53 Q 93 51, 97 53"
stroke="#8a6020"
strokeWidth={0.8}
strokeLinecap="round"
opacity={0.35}
/>
{/* Right brow */}
<Path
d="M 103 53 Q 107 51, 111 53"
stroke="#8a6020"
strokeWidth={0.8}
strokeLinecap="round"
opacity={0.35}
/>
{/* Left eye closed */}
<Path
d="M 89 57 Q 93 61, 97 57"
stroke="#7a5010"
strokeWidth={1.4}
strokeLinecap="round"
opacity={0.65}
/>
{/* Right eye closed */}
<Path
d="M 103 57 Q 107 61, 111 57"
stroke="#7a5010"
strokeWidth={1.4}
strokeLinecap="round"
opacity={0.65}
/>
{/* Nose */}
<Path
d="M 100 62 C 99 65, 97 67, 97 68"
stroke="#8a6020"
strokeWidth={1}
strokeLinecap="round"
opacity={0.25}
/>
{/* Mouth - serene half smile */}
<Path
d="M 95 72 Q 100 75, 105 72"
stroke="#8a6020"
strokeWidth={1.2}
strokeLinecap="round"
opacity={0.3}
/>
{/* Crown chakra */}
<Circle cx="100" cy="24" r="3.5" fill="rgba(232,184,109,0.2)" />
<Circle cx="100" cy="24" r="1.8" fill="#e8b86d" opacity={0.75} />
<Circle cx="100" cy="24" r="0.8" fill="#fff" opacity={0.6} />
{/* Subtle inner light */}
<Circle cx="100" cy="110" r="12" fill="rgba(232,184,109,0.04)" />
</Svg>
</View>
);
export const ZenCenter: React.FC<ZenCenterProps> = ({ isPlaying }) => {
const orbitAnimPrimary = React.useRef(new Animated.Value(0)).current;
const orbitAnimSecondary = React.useRef(new Animated.Value(0)).current;
const starBreathPrimary = React.useRef(new Animated.Value(0)).current;
const starBreathSecondary = React.useRef(new Animated.Value(0)).current;
const solarHaloAnim = React.useRef(new Animated.Value(0)).current;
React.useEffect(() => {
orbitAnimPrimary.setValue(0);
orbitAnimSecondary.setValue(0);
starBreathPrimary.setValue(0);
starBreathSecondary.setValue(0);
solarHaloAnim.setValue(0);
const primaryLoop = Animated.loop(
Animated.timing(orbitAnimPrimary, {
toValue: 1,
duration: 18000,
easing: Easing.linear,
useNativeDriver: true,
})
);
const secondaryLoop = Animated.loop(
Animated.timing(orbitAnimSecondary, {
toValue: 1,
duration: 26000,
easing: Easing.linear,
useNativeDriver: true,
})
);
const primaryBreathLoop = Animated.loop(
Animated.sequence([
Animated.timing(starBreathPrimary, {
toValue: 1,
duration: 4200,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}),
Animated.timing(starBreathPrimary, {
toValue: 0,
duration: 4200,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}),
])
);
const secondaryBreathLoop = Animated.loop(
Animated.sequence([
Animated.timing(starBreathSecondary, {
toValue: 1,
duration: 6100,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}),
Animated.timing(starBreathSecondary, {
toValue: 0,
duration: 6100,
easing: Easing.inOut(Easing.quad),
useNativeDriver: true,
}),
])
);
const solarHaloLoop = Animated.loop(
Animated.timing(solarHaloAnim, {
toValue: 1,
duration: 210000,
easing: Easing.linear,
useNativeDriver: true,
})
);
primaryLoop.start();
secondaryLoop.start();
primaryBreathLoop.start();
secondaryBreathLoop.start();
solarHaloLoop.start();
return () => {
primaryLoop.stop();
secondaryLoop.stop();
primaryBreathLoop.stop();
secondaryBreathLoop.stop();
solarHaloLoop.stop();
};
}, [orbitAnimPrimary, orbitAnimSecondary, solarHaloAnim, starBreathPrimary, starBreathSecondary]);
const primaryRotate = orbitAnimPrimary.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const secondaryRotate = orbitAnimSecondary.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '-360deg'],
});
const primaryStarOpacity = starBreathPrimary.interpolate({
inputRange: [0, 1],
outputRange: [0.45, 0.9],
});
const primaryStarScale = starBreathPrimary.interpolate({
inputRange: [0, 1],
outputRange: [0.92, 1.14],
});
const secondaryStarOpacity = starBreathSecondary.interpolate({
inputRange: [0, 1],
outputRange: [0.16, 0.55],
});
const secondaryStarScale = starBreathSecondary.interpolate({
inputRange: [0, 1],
outputRange: [0.9, 1.2],
});
const haloRotate = solarHaloAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const haloOpacity = solarHaloAnim.interpolate({
inputRange: [0, 0.12, 0.18, 0.26, 0.62, 0.72, 0.8, 1],
outputRange: [0.02, 0.03, 0.12, 0.04, 0.03, 0.11, 0.04, 0.02],
});
const haloScale = solarHaloAnim.interpolate({
inputRange: [0, 0.18, 0.62, 0.72, 1],
outputRange: [0.88, 1.06, 0.9, 1.08, 0.88],
});
return (
<View style={styles.container}>
<View style={styles.orbitWrap}>
<Animated.View
pointerEvents="none"
style={[
styles.solarHaloTrack,
{
opacity: haloOpacity,
transform: [{ rotate: haloRotate }],
},
]}
>
<Animated.View
style={[
styles.solarHalo,
{
transform: [{ scale: haloScale }],
},
]}
>
<View style={styles.solarCore} />
</Animated.View>
</Animated.View>
<Animated.View
pointerEvents="none"
style={[styles.orbitLayerPrimary, { transform: [{ rotate: primaryRotate }] }]}
>
<View style={styles.orbitRing} />
<Animated.View
style={[
styles.orbitDot1,
{
opacity: primaryStarOpacity,
transform: [{ scale: primaryStarScale }],
},
]}
/>
<Animated.View
style={[
styles.orbitDot1Trail,
{
opacity: secondaryStarOpacity,
transform: [{ scale: secondaryStarScale }],
},
]}
/>
</Animated.View>
<Animated.View
pointerEvents="none"
style={[styles.orbitLayerSecondary, { transform: [{ rotate: secondaryRotate }] }]}
>
<View style={styles.orbitRing2} />
<Animated.View
style={[
styles.orbitDot2,
{
opacity: secondaryStarOpacity,
transform: [{ scale: secondaryStarScale }],
},
]}
/>
<Animated.View
style={[
styles.orbitDot3,
{
opacity: primaryStarOpacity,
transform: [{ scale: primaryStarScale }],
},
]}
/>
</Animated.View>
{/* Zen glow */}
<View style={[styles.zenGlow, { opacity: isPlaying ? 1 : 0.55 }]} />
{/* Zen waves */}
<View style={styles.zenWaves}>
<View style={styles.zenWave} />
</View>
{/* Zen figure */}
<ZenFigure />
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
orbitWrap: {
width: 220,
height: 220,
justifyContent: 'center',
alignItems: 'center',
},
orbitLayerPrimary: {
position: 'absolute',
width: 220,
height: 220,
justifyContent: 'center',
alignItems: 'center',
},
orbitLayerSecondary: {
position: 'absolute',
width: 256,
height: 256,
justifyContent: 'center',
alignItems: 'center',
},
solarHaloTrack: {
position: 'absolute',
width: 320,
height: 320,
justifyContent: 'center',
alignItems: 'center',
},
solarHalo: {
position: 'absolute',
top: -18,
right: 24,
width: 92,
height: 92,
borderRadius: 46,
backgroundColor: 'rgba(232,184,109,0.14)',
shadowColor: theme.colors.primary,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.18,
shadowRadius: 24,
elevation: 8,
justifyContent: 'center',
alignItems: 'center',
},
solarCore: {
width: 26,
height: 26,
borderRadius: 13,
backgroundColor: 'rgba(255,244,214,0.28)',
},
orbitRing: {
position: 'absolute',
width: 220,
height: 220,
borderRadius: 110,
borderWidth: 1,
borderColor: 'rgba(232,184,109,0.12)',
},
orbitRing2: {
position: 'absolute',
width: 256,
height: 256,
borderRadius: 128,
borderWidth: 1,
borderColor: 'rgba(232,184,109,0.06)',
},
orbitDot1: {
position: 'absolute',
top: -3,
left: 107,
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: theme.colors.primary,
opacity: 0.55,
},
orbitDot1Trail: {
position: 'absolute',
top: 26,
right: 30,
width: 3,
height: 3,
borderRadius: 1.5,
backgroundColor: theme.colors.primary,
opacity: 0.22,
},
orbitDot2: {
position: 'absolute',
bottom: -2,
left: 127,
width: 4,
height: 4,
borderRadius: 2,
backgroundColor: theme.colors.primary,
opacity: 0.3,
},
orbitDot3: {
position: 'absolute',
top: 36,
left: 18,
width: 5,
height: 5,
borderRadius: 2.5,
backgroundColor: theme.colors.primary,
opacity: 0.18,
},
zenGlow: {
position: 'absolute',
width: 160,
height: 160,
borderRadius: 80,
backgroundColor: theme.colors.primarySoft,
opacity: 0,
},
zenWaves: {
position: 'absolute',
width: 220,
height: 220,
justifyContent: 'center',
alignItems: 'center',
},
zenWave: {
position: 'absolute',
width: 160,
height: 160,
borderRadius: 80,
borderWidth: 1,
borderColor: 'rgba(232,184,109,0.08)',
},
zenFigure: {
width: 140,
height: 140,
},
zenSvg: {
width: '100%',
height: '100%',
},
});