Компоненты/Кинотитры

Кинотитры

28 сентября 2026

Заголовок в духе титров фильма: слова опускаются из сильного размытия и медленно наводятся на резкость.

Установка

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

Файлы

index.tsx
НазадЖивой текстДалееТекст по кругу

Каждое слово появляется по очереди: опускается сверху, чуть уменьшается и выходит из плотного размытия, словно объектив наводится на резкость. Задержку, шаг между словами, силу размытия и длительность можно настроить.

Анимация идёт на UI-потоке Reanimated. Размытие в вебе и на Android — настоящий фильтр, на iOS его имитирует мягкая тень текста. При «Уменьшении движения» текст просто плавно проявляется. Работает на iOS, Android и в вебе.

Вручную

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

npm install react-native-reanimated
pnpm add react-native-reanimated
yarn add react-native-reanimated
bun add react-native-reanimated

Скопируйте эти файлы в src/shared/components/organisms/cinematic-text/:

Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.

Использование

import React from "react";
import { ScrollView, StyleSheet, useWindowDimensions, View } from "react-native";
import { CinematicText } from "@/shared/components/organisms/cinematic-text";
import { Showcase, useCustomize } from "~/showcase";

const CUSTOMIZE = [
  { key: "title", label: "Заголовок", type: "text", default: "Живые компоненты для новых интерфейсов.", maxLength: 80 },
  { key: "subtitle", label: "Подзаголовок", type: "text", default: "Каждое слово опускается из плотного размытия и плавно обретает резкость.", maxLength: 140 },
  { key: "titleSize", label: "Размер заголовка", type: "slider", min: 28, max: 96, step: 2, unit: "px", default: 60 },
  { key: "blur", label: "Размытие заголовка", type: "slider", min: 0, max: 60, step: 1, unit: "px", default: 28 },
  { key: "subtitleBlur", label: "Размытие подзаголовка", type: "slider", min: 0, max: 60, step: 1, unit: "px", default: 16 },
  { key: "stagger", label: "Шаг между словами", type: "slider", min: 0, max: 0.5, step: 0.01, unit: "с", default: 0.11 },
  { key: "duration", label: "Длительность", type: "slider", min: 0.3, max: 4, step: 0.1, unit: "с", default: 1.4 },
  { key: "delay", label: "Задержка заголовка", type: "slider", min: 0, max: 2, step: 0.05, unit: "с", default: 0.2 },
  { key: "subtitleDelay", label: "Задержка подзаголовка", type: "slider", min: 0, max: 3, step: 0.05, unit: "с", default: 0.85 },
] as const;

export default function CinematicTextScreen() {
  const custom = useCustomize(CUSTOMIZE);
  const { width } = useWindowDimensions();
  const titleSize = custom.titleSize === 60 ? (width >= 640 ? 60 : 36) : custom.titleSize;
  const replayKey = [custom.blur, custom.subtitleBlur, custom.stagger, custom.duration, custom.delay, custom.subtitleDelay].join(":");

  return (
    <Showcase>
      <ScrollView contentContainerStyle={styles.scroll}>
        <View key={replayKey} style={styles.stack}>
          <CinematicText
            delay={custom.delay}
            stagger={custom.stagger}
            blur={custom.blur}
            duration={custom.duration}
            containerStyle={{ maxWidth: titleSize * 8.4 }}
            style={[
              styles.title,
              {
                fontSize: titleSize,
                lineHeight: titleSize * 0.95,
                letterSpacing: -0.055 * titleSize,
              },
            ]}
          >
            {custom.title}
          </CinematicText>
          <CinematicText
            delay={custom.subtitleDelay}
            stagger={custom.stagger}
            blur={custom.subtitleBlur}
            duration={custom.duration}
            containerStyle={styles.subtitleBox}
            style={styles.subtitle}
          >
            {custom.subtitle}
          </CinematicText>
        </View>
      </ScrollView>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  scroll: {
    flexGrow: 1,
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 48,
  },
  stack: {
    width: "100%",
    alignItems: "center",
    justifyContent: "center",
    gap: 20,
    paddingHorizontal: 24,
  },
  title: {
    color: "#fafafa",
    fontWeight: "600",
    textAlign: "center",
  },
  subtitleBox: {
    maxWidth: 14 * 0.55 * 42,
  },
  subtitle: {
    color: "#a1a1aa",
    fontSize: 14,
    lineHeight: 24,
    textAlign: "center",
  },
});

Пропсы

Пропс
Описание
children

Тип string

delay?

Тип number

stagger?

Тип number

blur?

Тип number

duration?

Тип number

style?

Тип StyleProp<TextStyle>

containerStyle?

Тип StyleProp<ViewStyle>