Add red pulsing animation to VoiceFAB when listening
- Add pulse ring that expands and fades out while in listening mode - Animation uses native driver for smooth 60fps performance - Ring starts at FAB size and scales to 1.8x with opacity fade 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
764c149e2e
commit
dbf6a8a74a
@ -35,6 +35,8 @@ export function VoiceFAB({ onPress, style, disabled = false, isListening = false
|
||||
// Animation values
|
||||
const scale = useRef(new Animated.Value(1)).current;
|
||||
const opacity = useRef(new Animated.Value(1)).current;
|
||||
const pulseScale = useRef(new Animated.Value(1)).current;
|
||||
const pulseOpacity = useRef(new Animated.Value(0)).current;
|
||||
|
||||
// Hide FAB when call is active
|
||||
useEffect(() => {
|
||||
@ -68,6 +70,51 @@ export function VoiceFAB({ onPress, style, disabled = false, isListening = false
|
||||
}
|
||||
}, [isCallActive, scale, opacity]);
|
||||
|
||||
// Pulse animation when listening
|
||||
useEffect(() => {
|
||||
if (isListening && !isCallActive) {
|
||||
// Start pulsing animation
|
||||
const pulseAnimation = Animated.loop(
|
||||
Animated.sequence([
|
||||
Animated.parallel([
|
||||
Animated.timing(pulseScale, {
|
||||
toValue: 1.8,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(pulseOpacity, {
|
||||
toValue: 0,
|
||||
duration: 1000,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]),
|
||||
Animated.parallel([
|
||||
Animated.timing(pulseScale, {
|
||||
toValue: 1,
|
||||
duration: 0,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
Animated.timing(pulseOpacity, {
|
||||
toValue: 0.6,
|
||||
duration: 0,
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
]),
|
||||
])
|
||||
);
|
||||
pulseAnimation.start();
|
||||
|
||||
return () => {
|
||||
pulseAnimation.stop();
|
||||
pulseScale.setValue(1);
|
||||
pulseOpacity.setValue(0);
|
||||
};
|
||||
} else {
|
||||
pulseScale.setValue(1);
|
||||
pulseOpacity.setValue(0);
|
||||
}
|
||||
}, [isListening, isCallActive, pulseScale, pulseOpacity]);
|
||||
|
||||
// Press animation with haptic feedback
|
||||
const handlePressIn = () => {
|
||||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
|
||||
@ -103,6 +150,18 @@ export function VoiceFAB({ onPress, style, disabled = false, isListening = false
|
||||
style,
|
||||
]}
|
||||
>
|
||||
{/* Pulse ring when listening */}
|
||||
{isListening && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.pulseRing,
|
||||
{
|
||||
transform: [{ scale: pulseScale }],
|
||||
opacity: pulseOpacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.fab,
|
||||
@ -133,6 +192,13 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
zIndex: 100,
|
||||
},
|
||||
pulseRing: {
|
||||
position: 'absolute',
|
||||
width: FAB_SIZE,
|
||||
height: FAB_SIZE,
|
||||
borderRadius: BorderRadius.full,
|
||||
backgroundColor: AppColors.error,
|
||||
},
|
||||
fab: {
|
||||
width: FAB_SIZE,
|
||||
height: FAB_SIZE,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user