Компоненты/Прогресс

Прогресс

31 января 2025

Полоса прогресса плавно заполняется по значению, при желании — с градиентом

Установка

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

Файлы

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

Вручную

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

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

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

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

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

import { AnimatedProgressBar } from "@/shared/components/organisms/progress";
import React, { useMemo, useRef, useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";

import { Showcase, useAutoplay, useCustomize } from "~/showcase";

const AUTOPLAY_STEPS: [number, number][] = [
  [0.1, 220],
  [0.2, 220],
  [0.3, 700],
  [0.4, 200],
  [0.5, 200],
  [0.6, 200],
  [0.7, 200],
  [0.8, 200],
  [0.9, 450],
  [0.8, 230],
  [0.7, 230],
  [0.6, 230],
  [0.5, 230],
  [0.4, 230],
  [0.3, 230],
  [0.2, 230],
  [0.1, 230],
  [0, 900],
];

const CUSTOMIZE = [
  { key: "colorFrom", label: "Цвет начала", type: "color", default: "#FFF3BF" },
  { key: "colorTo", label: "Цвет конца", type: "color", default: "#FFD43B" },
  { key: "gradient", label: "Градиент", type: "toggle", default: true },
  { key: "track", label: "Цвет дорожки", type: "color", default: "#e0e0e0" },
  { key: "height", label: "Толщина", type: "slider", min: 2, max: 32, step: 1, unit: "px", default: 10 },
  { key: "radius", label: "Скругление", type: "slider", min: 0, max: 99, step: 1, unit: "px", default: 99 },
  { key: "duration", label: "Анимация", type: "slider", min: 100, max: 2000, step: 50, unit: "ms", default: 800 },
  { key: "percent", label: "Проценты", type: "toggle", default: false },
  { key: "tempo", label: "Темп автопоказа", type: "slider", min: 0.5, max: 3, step: 0.1, default: 1 },
] as const;

export default function ProgressScreen() {
  const [progress, setProgress] = useState<number>(0);
  const custom = useCustomize(CUSTOMIZE);
  const tempo = useRef(custom.tempo);
  tempo.current = custom.tempo;
  const { colorFrom, colorTo } = custom;
  const gradientColors = useMemo(() => [colorFrom, colorTo], [colorFrom, colorTo]);

  useAutoplay(async ({ wait }) => {
    setProgress(0);
    await wait(500 / tempo.current);
    for (const [value, delay] of AUTOPLAY_STEPS) {
      setProgress(value);
      await wait(delay / tempo.current);
    }
  });
  return (
    <Showcase>
      <View style={styles.container}>
        <AnimatedProgressBar
          progress={progress}
          containerStyle={{
            padding: 50,
            width: "100%",
          }}
          useGradient={custom.gradient}
          gradientColors={gradientColors}
          progressColor={custom.gradient ? undefined : colorTo}
          trackColor={custom.track}
          height={custom.height}
          animationDuration={custom.duration}
          showPercentage={custom.percent}
          percentageTextStyle={PERCENT_TEXT}
          borderRadius={custom.radius}
        />
        <View style={styles.row}>
          <Pressable
            style={styles.button}
            onPress={() => setProgress((v) => v + 0.1)}
          >
            <Text style={styles.buttonText}>+1</Text>
          </Pressable>
          <Pressable
            style={styles.button}
            onPress={() => setProgress((v) => v - 0.1)}
          >
            <Text style={styles.buttonText}>-1</Text>
          </Pressable>
        </View>
      </View>
    </Showcase>
  );
}

const PERCENT_TEXT = { color: "#fff" };

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#0a0a0a",
    justifyContent: "center",
    alignItems: "center",
  },
  button: {
    paddingHorizontal: 16,
    paddingVertical: 10,
    borderRadius: 12,
    backgroundColor: "#1c1c1e",
  },
  buttonText: { color: "#fff", fontSize: 14, fontWeight: "600" },
  row: { flexDirection: "row", gap: 12 },
});

Пропсы

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

Progress value between 0 and 1

Тип number·По умолчанию 0

animationDuration?

Duration of the animation in milliseconds

Тип number·По умолчанию 800

width?

Width of the progress bar container

Тип number | string·По умолчанию '100%'

height?

Height of the progress bar

Тип number·По умолчанию 10

progressColor?

Color of the progress indicator

Тип string·По умолчанию '#2089dc'

trackColor?

Color of the background track

Тип string·По умолчанию '#e0e0e0'

borderRadius?

Border radius of the progress bar

Тип number·По умолчанию 4

showPercentage?

Whether to show the progress percentage

Тип boolean·По умолчанию false

percentagePosition?

Position of the percentage text

Тип "left" | "right" | "top" | "bottom" | "inside"·По умолчанию 'right'

percentageTextStyle?

Style for the percentage text

Тип TextStyle

containerStyle?

Style for the container

Тип ViewStyle

formatPercentage?

Format function for the percentage display

Тип (value: number) => string·По умолчанию (value: number) => `${Math.round(value * 100)}%`

indeterminate?

Whether to use indeterminate animation style for unknown progress

Тип boolean·По умолчанию false

pulsate?

Whether to show the pulse animation effect

Тип boolean·По умолчанию false

useGradient?

Whether to use a gradient for the progress bar

Тип boolean·По умолчанию false

gradientColors?

Colors for gradient (requires useGradient to be true)

Тип string[]·По умолчанию ['#4dabf7', '#3b5bdb']

onAnimationComplete?

Optional callback when animation completes

Тип () => void