544 lines
14 KiB
TypeScript
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%',
|
|
},
|
|
});
|