Компоненты/Раскрывающаяся карточка

Раскрывающаяся карточка

28 сентября 2026

Карточка с фото, которая пружинно раскрывается из своего места в модальное окно с полным текстом.

Установка

npx astra-rn add expanding-card
pnpm dlx astra-rn add expanding-card
yarn dlx astra-rn add expanding-card
bunx --bun astra-rn add expanding-card

Файлы

index.tsx
НазадРаскрывающийся видДалееПлавное появление

Карточка с фото, надзаголовком, заголовком и подписью. По нажатию она пружинно вырастает прямо из своего места в модальное окно по центру экрана: фото растягивается, а под ним проявляется полный текст из children. Закрыть окно можно крестиком, нажатием на фон или системной кнопкой «Назад» — карточка так же плавно возвращается на место.

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

Вручную

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

npm install expo-linear-gradient react-native-reanimated react-native-svg
pnpm add expo-linear-gradient react-native-reanimated react-native-svg
yarn add expo-linear-gradient react-native-reanimated react-native-svg
bun add expo-linear-gradient react-native-reanimated react-native-svg

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

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

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

import React from "react";
import { StyleSheet, View } from "react-native";
import { ExpandingCard } from "@/shared/components/organisms/expanding-card";
import { Showcase, useCustomize } from "~/showcase";
import { PHOTO_SOURCES } from "~/assets/photos";

const CUSTOMIZE = [
  { key: "height", label: "Высота карточки", type: "slider", min: 220, max: 520, step: 4, unit: "px", default: 360 },
  { key: "width", label: "Ширина карточки", type: "slider", min: 200, max: 440, step: 4, unit: "px", default: 300 },
  { key: "radius", label: "Скругление", type: "slider", min: 0, max: 48, step: 1, unit: "px", default: 28 },
  { key: "openWidth", label: "Ширина окна", type: "slider", min: 320, max: 800, step: 8, unit: "px", default: 512 },
  { key: "eyebrow", label: "Надзаголовок", type: "text", default: "Избранное", maxLength: 30 },
  { key: "title", label: "Заголовок", type: "text", default: "Горы на рассвете", maxLength: 50 },
  { key: "subtitle", label: "Подпись", type: "text", default: "Выходные у самого подножия гор.", maxLength: 80 },
  { key: "image", label: "Фото", type: "image", default: PHOTO_SOURCES["cinematic-carousel-2"] },
] as const;

export default function ExpandingCardScreen() {
  const custom = useCustomize(CUSTOMIZE);

  return (
    <Showcase>
      <View style={styles.container}>
        <View style={styles.stage}>
          <View style={[styles.slot, { maxWidth: custom.width }]}>
            <ExpandingCard
              image={custom.image}
              eyebrow={custom.eyebrow || undefined}
              title={custom.title}
              subtitle={custom.subtitle || undefined}
              height={custom.height}
              radius={custom.radius}
              openWidth={custom.openWidth}
            >
              {"Выезжайте до рассвета, пока озеро ещё неподвижно и отражает вторую вершину. Первая электричка из города привозит вас туда, когда пик уже ловит свет, которого улицы ещё не видели."}
              {"Берите вещи на все четыре сезона. Горы создают свою погоду, а лучшие снимки случаются в те десять минут, которые никто не планировал."}
            </ExpandingCard>
          </View>
        </View>
      </View>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 48,
    backgroundColor: "#0a0a0a",
  },
  stage: {
    width: "100%",
    alignItems: "center",
    justifyContent: "center",
    paddingHorizontal: 16,
    paddingVertical: 48,
  },
  slot: {
    width: "100%",
  },
});

Пропсы

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

Тип ExpandingCardImage

eyebrow?

Тип string

title

Тип string

subtitle?

Тип string

children?

Тип ReactNode

height?

Тип number

radius?

Тип number

openWidth?

Тип number

theme?

Тип "dark" | "light"

style?

Тип StyleProp<ViewStyle>