Поставьте зависимости:
npm install expo-blur react-native-reanimatedpnpm add expo-blur react-native-reanimatedyarn add expo-blur react-native-reanimatedbun add expo-blur react-native-reanimatedСкопируйте эти файлы в src/shared/components/organisms/fade-text/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import { useMemo, useRef, useState } from "react";
import { StyleSheet } from "react-native";
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useFonts } from "expo-font";
import { FadeText } from "@/shared/components/organisms/fade-text";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";
const CUSTOMIZE = [
{ key: "line1", label: "Строка 1", type: "text", default: "Astra is awesome!", maxLength: 40 },
{ key: "line2", label: "Строка 2", type: "text", default: "UI components made easy.", maxLength: 40 },
{ key: "line3", label: "Строка 3", type: "text", default: "Build stunning apps fast.", maxLength: 40 },
{ key: "line4", label: "Строка 4", type: "text", default: "Customizable and flexible.", maxLength: 40 },
{ key: "line5", label: "Строка 5", type: "text", default: "Join the Astra community!", maxLength: 40 },
{ key: "color", label: "Цвет текста", type: "color", default: "#ffffff" },
{ key: "fontSize", label: "Размер шрифта", type: "slider", min: 12, max: 40, step: 1, unit: "px", default: 20 },
{ key: "textAlign", label: "Выравнивание", type: "select", options: [{ value: "left", label: "Слева" }, { value: "center", label: "Центр" }, { value: "right", label: "Справа" }], default: "left" },
{ key: "duration", label: "Длительность", type: "slider", min: 500, max: 8000, step: 100, unit: "ms", default: 3500 },
{ key: "wordDelay", label: "Задержка слов", type: "slider", min: 0, max: 1000, step: 10, unit: "ms", default: 300 },
{ key: "scaleFrom", label: "Начальный масштаб", type: "slider", min: 0.5, max: 1, step: 0.01, default: 0.98 },
{ key: "cycleMs", label: "Длина цикла", type: "slider", min: 3000, max: 20000, step: 100, unit: "ms", default: 10200 },
] as const;
export default function App(): React.JSX.Element {
const [fontLoaded] = useFonts({
SfProRounded: require("@/assets/fonts/sf-pro-rounded.ttf"),
HelveticaNowDisplay: require("@/assets/fonts/HelveticaNowDisplayMedium.ttf"),
Coolvetica: require("@/assets/fonts/Coolvetica-Rg.otf"),
});
const custom = useCustomize(CUSTOMIZE);
const INPUTS = useMemo<string[]>(
() => [custom.line1, custom.line2, custom.line3, custom.line4, custom.line5],
[custom.line1, custom.line2, custom.line3, custom.line4, custom.line5],
);
const scaleRange = useMemo<[number, number]>(() => [custom.scaleFrom, 1], [custom.scaleFrom]);
const cycleRef = useRef(custom.cycleMs);
cycleRef.current = custom.cycleMs;
const restartKey = [INPUTS.join("|"), custom.duration, custom.wordDelay, custom.scaleFrom].join("~");
const [cycle, setCycle] = useState<number>(0);
const visibility = useSharedValue(1);
const visibilityStyle = useAnimatedStyle(() => ({
opacity: visibility.value,
}));
useAutoplay(
async ({ wait }) => {
try {
await wait(cycleRef.current);
visibility.value = withTiming(0, { duration: 700 });
await wait(800);
setCycle((value) => value + 1);
await wait(120);
} finally {
visibility.value = withTiming(1, { duration: 300 });
}
},
{ startDelayMs: 0 },
);
return (
<Showcase>
<GestureHandlerRootView style={styles.container}>
<StatusBar style="light" />
<Animated.View style={visibilityStyle}>
<FadeText
key={`${cycle}-${restartKey}`}
inputs={INPUTS}
duration={custom.duration}
wordDelay={custom.wordDelay}
blurIntensity={[30, 20, 0]}
scaleRange={scaleRange}
blurTint="systemUltraThinMaterialDark"
textAlign={custom.textAlign as "left" | "center" | "right"}
color={custom.color}
style={{
fontFamily: fontLoaded ? "SfProRounded" : undefined,
fontSize: custom.fontSize,
}}
/>
</Animated.View>
</GestureHandlerRootView>
</Showcase>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0a0a0a",
justifyContent: "center",
alignItems: "center",
},
card: {
backgroundColor: "rgba(255,255,255,0.08)",
borderRadius: 20,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
title: {
color: "#fff",
fontSize: 17,
fontWeight: "600",
},
subtitle: {
color: "rgba(255,255,255,0.6)",
fontSize: 13,
marginTop: 2,
},
body: {
marginTop: 12,
color: "rgba(255,255,255,0.75)",
fontSize: 14,
lineHeight: 20,
},
trigger: {
width: 36,
height: 36,
borderRadius: 10,
backgroundColor: "rgba(255,255,255,0.12)",
justifyContent: "center",
alignItems: "center",
},
menu: {
backgroundColor: "#fff",
},
itemText: {
fontSize: 15,
color: "#111",
},
destructive: {
color: "#dc2626",
},
});inputsТип string[]
wordDelay?Тип number
duration?Тип number
blurIntensity?Тип [number, number, number]
blurTint?Тип BlurTint
scaleRange?Тип [number, number]
translateYRange?Тип [number, number]
opacityRange?Тип [number, number, number]
fontSize?Тип number
fontWeight?Тип TextStyle["fontWeight"]
color?Тип string
textAlign?Тип "left" | "center" | "right"
containerStyle?Тип StyleProp<ViewStyle>
style?Тип StyleProp<TextStyle>