UNPKG

@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
"use strict"; /** * @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