Компоненты/Реакции дугой

Реакции дугой

28 сентября 2026

Кнопка «Нравится» с панелью реакций, из которой выбранная реакция перелетает по дуге в кнопку.

Установка

npx astra-rn add reaction-arc
pnpm dlx astra-rn add reaction-arc
yarn dlx astra-rn add reaction-arc
bunx --bun astra-rn add reaction-arc

Файлы

index.tsx
НазадДиапазонДалееПринтер чеков

Кнопка «Нравится» с панелью реакций. Долгое нажатие на кнопку (в вебе — наведение мыши) открывает панель: реакции выскакивают по очереди на пружине, а та, которую держит палец или курсор, увеличивается и раздвигает соседей. После касания реакция перелетает по квадратичной дуге в кнопку и остаётся на ней; короткое нажатие ставит или снимает «Нравится».

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

Вручную

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

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

Скопируйте эти файлы в src/shared/components/molecules/reaction-arc/:

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

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

import React, { useMemo } from "react";
import { StyleSheet, useWindowDimensions, View } from "react-native";
import {
  DEFAULT_REACTIONS,
  ReactionArc,
  type ArcDirection,
} from "@/shared/components/molecules/reaction-arc";
import { Showcase, useCustomize } from "~/showcase";

const CUSTOMIZE = [
  { key: "size", label: "Размер кнопки", type: "slider", min: 36, max: 80, step: 2, unit: "px", default: 56 },
  { key: "count", label: "Реакций в панели", type: "slider", min: 2, max: 6, step: 1, default: 6 },
  { key: "duration", label: "Длительность полёта", type: "slider", min: 200, max: 1500, step: 50, unit: "мс", default: 500 },
  { key: "strength", label: "Изгиб дуги", type: "slider", min: 0, max: 1.5, step: 0.02, default: 0.72 },
  { key: "peak", label: "Вершина дуги", type: "slider", min: 0.05, max: 0.95, step: 0.01, default: 0.28 },
  {
    key: "direction",
    label: "Направление",
    type: "select",
    options: [
      { value: "ccw", label: "Против часовой" },
      { value: "cw", label: "По часовой" },
      { value: "auto", label: "Авто" },
    ],
    default: "ccw",
  },
] as const;

export default function ReactionArcScreen() {
  const custom = useCustomize(CUSTOMIZE);
  const { width } = useWindowDimensions();
  const narrow = width <= 480;
  const baseSize = narrow ? Math.round((custom.size * 44) / 56) : custom.size;
  const size = Math.max(28, Math.min(baseSize, Math.floor((width - 48) / (custom.count * 1.15))));
  const reactions = useMemo(
    () =>
      custom.count === DEFAULT_REACTIONS.length
        ? DEFAULT_REACTIONS
        : DEFAULT_REACTIONS.slice(0, custom.count),
    [custom.count],
  );

  return (
    <Showcase>
      <View style={styles.page}>
        <View style={styles.stage}>
          <ReactionArc
            size={size}
            reactions={reactions}
            duration={custom.duration / 1000}
            strength={custom.strength}
            peak={custom.peak}
            direction={custom.direction as ArcDirection}
          />
        </View>
      </View>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  page: {
    flex: 1,
    alignItems: "center",
    justifyContent: "center",
    paddingVertical: 48,
  },
  stage: {
    width: "100%",
    alignItems: "center",
    justifyContent: "center",
    paddingHorizontal: 16,
    paddingVertical: 64,
  },
});

Изгиб дуги задают strength, peak и direction, а onChange сообщает выбранную реакцию (или null, когда отметку сняли).

Пропсы

Пропс
Описание
reactions?

Тип readonly IReaction[]

size?

Тип number

strength?

Тип number

peak?

Тип number

duration?

Тип number

direction?

Тип ArcDirection

onChange?

Тип (reaction: IReaction | null) => void

style?

Тип StyleProp<ViewStyle>