Компоненты/Круговой прогресс

Круговой прогресс

21 августа 2026

Круговой индикатор с анимированной дугой и управлением жестом

Установка

npx astra-rn add circular-progress
pnpm dlx astra-rn add circular-progress
yarn dlx astra-rn add circular-progress
bunx --bun astra-rn add circular-progress

Файлы

index.tsx
НазадКруговой лоадерДалееКарусель с маской

Вручную

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

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

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

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

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

import { View, StyleSheet } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useEffect } from "react";
import { useSharedValue, withTiming, Easing } from "react-native-reanimated";

import { CircularProgress } from "@/shared/components/organisms/circular-progress";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";

const FILL_EASING = Easing.bezier(0.95, 0.1, 0.95, 1);

const CUSTOMIZE = [
  { key: "target", label: "Прогресс", type: "slider", min: 0, max: 100, step: 1, unit: "%", default: 72 },
  { key: "fillDuration", label: "Заполнение", type: "slider", min: 200, max: 5000, step: 100, unit: "ms", default: 2000 },
  { key: "size", label: "Размер", type: "slider", min: 40, max: 240, step: 2, unit: "px", default: 120 },
  { key: "strokeWidth", label: "Толщина", type: "slider", min: 2, max: 30, step: 1, unit: "px", default: 12 },
  { key: "color", label: "Цвет дуги", type: "color", default: "#ffffff" },
  { key: "background", label: "Фон", type: "color", default: "#0a0a0a" },
] as const;

export default function App() {
  const custom = useCustomize(CUSTOMIZE);
  const progress = useSharedValue(0);
  const fill = { easing: FILL_EASING, duration: custom.fillDuration };

  useEffect(() => {
    progress.value = withTiming(custom.target, fill);
  }, [custom.target, custom.fillDuration]);

  useAutoplay(
    async ({ wait }) => {
      await wait(1800);
      progress.value = withTiming(0, { duration: 400 });
      await wait(700);
      progress.value = withTiming(custom.target, fill);
      await wait(custom.fillDuration);
    },
    { startDelayMs: 2000 },
  );

  return (
    <Showcase>
      <GestureHandlerRootView
        style={[styles.container, { backgroundColor: custom.background }]}
      >
        <StatusBar style="light" />
        <View>
          <CircularProgress
            progress={progress}
            size={custom.size}
            strokeWidth={custom.strokeWidth}
            progressCircleColor={custom.color}
            outerCircleColor="rgba(255,255,255,0.15)"
            backgroundColor="#0000000f"
            renderIcon={() => <></>}
          />
        </View>
      </GestureHandlerRootView>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#0a0a0a",
    alignItems: "center",
    justifyContent: "center",
  },
});

Пропсы

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

Тип SharedValue<number>

size?

Тип number

strokeWidth?

Тип number

outerCircleColor?

Тип string

progressCircleColor?

Тип string

backgroundColor?

Тип string

onPress?

Тип () => void

gap?

Тип number

renderIcon?

Тип () => ReactNode