import MaskedView from '@react-native-masked-view/masked-view';
import { LinearGradient } from 'expo-linear-gradient';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { LayoutChangeEvent } from 'react-native';
import { Animated, Easing, Platform, View } from 'react-native';
import { useTheme } from '../../theme';
import Typography from '../Typography';
interface AnimatedGradientTextProps {
children: string;
fontSize: number;
lineHeight: number;
}
const ANIMATION_DURATION_MS = 2000;
const AnimatedGradientText = ({
children,
fontSize,
lineHeight,
}: AnimatedGradientTextProps) => {
const theme = useTheme();
const gradient = theme.colors.gradients.aiHorizontal;
const [size, setSize] = useState<{ width: number; height: number } | null>(
null
);
const animatedValue = useRef(new Animated.Value(0));
const onLayout = useCallback((event: LayoutChangeEvent) => {
const { width, height } = event.nativeEvent.layout;
setSize((prev) => {
if (prev?.width === width && prev?.height === height) return prev;
return { width, height };
});
}, []);
useEffect(() => {
if (!size) return;
animatedValue.current.setValue(0);
const animation = Animated.loop(
Animated.timing(animatedValue.current, {
toValue: 1,
duration: ANIMATION_DURATION_MS,
easing: Easing.linear,
useNativeDriver: Platform.OS !== 'web',
})
);
animation.start();
return () => animation.stop();
}, [size]);
// Slide left by one full text-width per loop cycle.
// Starting at 0 keeps the gradient visible from the first frame.
const translateX = size
? animatedValue.current.interpolate({
inputRange: [0, 1],
outputRange: [0, -size.width],
})
: animatedValue.current;
return (
{children}
}
>
{size ? (
{/*
* Double the colour pattern so the gradient tiles seamlessly:
* [A, B, A, B, A] at equal spacing means the slice at
* translateX=0 looks identical to the slice at translateX=-width,
* eliminating the jump when the loop restarts.
*/}
) : (
{children}
)}
{/* Visually hidden but accessible text for screen readers */}
{children}
);
};
export default AnimatedGradientText;