Компоненты/Текст с фейдом

Текст с фейдом

31 января 2025

Текст проявляется слово за словом с мягким размытием

Установка

npx astra-rn add fade-text
pnpm dlx astra-rn add fade-text
yarn dlx astra-rn add fade-text
bunx --bun astra-rn add fade-text

Файлы

index.tsx
НазадСменный текстДалееТягучий текст

Вручную

Поставьте зависимости:

npm install expo-blur react-native-reanimated
pnpm add expo-blur react-native-reanimated
yarn add expo-blur react-native-reanimated
bun 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>