@revrag-ai/embed-react-native
Version:
A powerful React Native library for integrating AI-powered voice agents into mobile applications. Features real-time voice communication, intelligent speech processing, customizable UI components, and comprehensive event handling for building conversation
172 lines (161 loc) • 4.64 kB
JavaScript
;
/**
* @file EmbedButton.animations.ts
* @description Animation logic and hooks for the EmbedButton component
*/
import { useEffect } from 'react';
import { Gesture } from 'react-native-gesture-handler';
import { getReanimatedAPI, showReanimatedSetupError } from "../utils/reanimated.helper.js";
import { BUTTON_DIMENSIONS, calculateMaxX, calculateMaxY, clamp, SCREEN_WIDTH } from "./EmbedButton.helpers.js";
// Get reanimated API with fallbacks
const {
useSharedValue,
useAnimatedStyle,
withTiming,
withSpring,
withRepeat,
withSequence,
runOnJS,
Easing,
Animated,
isAvailable: isReanimatedAvailable
} = getReanimatedAPI();
// Show warning if reanimated is not available
if (!isReanimatedAvailable) {
showReanimatedSetupError();
}
// Export the Animated component for use in the main component
export { Animated };
// ==================== ANIMATION VALUES HOOK ====================
/**
* Hook to initialize and manage all animation shared values
*/
export const useAnimationValues = () => {
const isPressed = useSharedValue(false);
const offset = useSharedValue({
x: 0,
y: 0
});
const start = useSharedValue({
x: 0,
y: 0
});
const menuAnimation = useSharedValue(0);
const buttonWidth = useSharedValue(BUTTON_DIMENSIONS.WIDTH);
const buttonScale = useSharedValue(1);
return {
isPressed,
offset,
start,
menuAnimation,
buttonWidth,
buttonScale
};
};
// ==================== BUTTON ANIMATIONS ====================
/**
* Hook to handle button expand/collapse animations
*/
export const useButtonAnimations = (isOpen, menuAnimation, buttonWidth) => {
useEffect(() => {
menuAnimation.value = withTiming(isOpen ? 0.8 : 0, {
duration: 300
});
buttonWidth.value = withTiming(isOpen ? BUTTON_DIMENSIONS.EXPANDED_WIDTH : BUTTON_DIMENSIONS.WIDTH);
}, [isOpen, menuAnimation, buttonWidth]);
};
// ==================== BREATHING ANIMATION ====================
/**
* Hook to handle breathing animation when auto-popup is shown
*/
export const useBreathingAnimation = (isOpen, isAutoOpen, buttonScale) => {
useEffect(() => {
if (!isOpen && isAutoOpen) {
// Start breathing animation
buttonScale.value = withRepeat(withSequence(withTiming(1.1, {
duration: 1500,
easing: Easing.inOut(Easing.ease)
}), withTiming(1, {
duration: 1500,
easing: Easing.inOut(Easing.ease)
})), -1,
// Infinite repeat
false);
} else {
// Reset animation
buttonScale.value = withTiming(1, {
duration: 300
});
}
}, [buttonScale, isAutoOpen, isOpen]);
};
// ==================== ANIMATED STYLES ====================
/**
* Hook to create animated styles for the button
*/
export const useButtonAnimatedStyles = (isOpen, offset, buttonWidth, isPressed, buttonScale) => {
return useAnimatedStyle(() => {
const maxX = calculateMaxX(isOpen);
const clampedX = clamp(offset.value.x, -maxX, 0);
return {
width: buttonWidth.value,
height: BUTTON_DIMENSIONS.HEIGHT,
transform: [{
translateX: clampedX
}, {
translateY: offset.value.y
}, {
scale: withSpring(isPressed.value ? 0.95 : buttonScale.value)
}],
justifyContent: isOpen ? 'space-between' : 'flex-start',
overflow: 'hidden'
};
});
};
/**
* Hook to create animated styles for the popup text
*/
export const usePopupAnimatedStyles = (offset, isPressed) => {
return useAnimatedStyle(() => {
const maxX = SCREEN_WIDTH;
const clampedX = clamp(offset.value.x, -maxX, 0);
return {
transform: [{
translateX: clampedX
}, {
translateY: offset.value.y
}, {
scale: withSpring(isPressed.value ? 1 : 1)
}]
};
});
};
// ==================== GESTURE HANDLER ====================
/**
* Create pan gesture for drag functionality
*/
export const createPanGesture = (isPressed, offset, start, isOpen, setIsAutoOpen) => {
return Gesture.Pan().onBegin(() => {
isPressed.value = true;
if (setIsAutoOpen) {
runOnJS(setIsAutoOpen)(false);
}
}).onUpdate(e => {
const maxX = calculateMaxX(isOpen);
const maxY = calculateMaxY();
const newX = clamp(e.translationX + start.value.x, -maxX, 0);
const newY = clamp(e.translationY + start.value.y, -maxY, 0);
offset.value = {
x: newX,
y: newY
};
}).onEnd(() => {
start.value = {
x: offset.value.x,
y: offset.value.y
};
}).onFinalize(() => {
isPressed.value = false;
});
};
//# sourceMappingURL=EmbedButton.animations.js.map