Поставьте зависимости:
npm install react-native-reanimatedpnpm add react-native-reanimatedyarn add react-native-reanimatedbun add react-native-reanimatedСкопируйте эти файлы в src/shared/components/molecules/number-flow/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import React, { useRef, useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
import { NumberFlow } from "@/shared/components/molecules/number-flow";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";
const AUTOPLAY_STEPS: Array<[number, number]> = [
[1345, 250],
[1344, 400],
[50350, 350],
[50351, 350],
[50352, 350],
[50353, 300],
[50354, 300],
[50355, 300],
[57971, 550],
[103251, 550],
];
const COLOR_HEX = "#ffffff";
const COLOR = "#fff";
const CUSTOMIZE = [
{ key: "color", label: "Цвет цифр", type: "color", default: COLOR_HEX },
{ key: "fontSize", label: "Размер шрифта", type: "slider", min: 32, max: 120, step: 2, unit: "px", default: 90 },
{ key: "fontWeight", label: "Насыщенность", type: "select", options: ["400", "600", "800"], default: "600" },
{ key: "separator", label: "Разделитель", type: "select", options: [{ value: ",", label: "Запятая" }, { value: " ", label: "Пробел" }, { value: "none", label: "Нет" }], default: "," },
{ key: "tempo", label: "Темп автопоказа", type: "slider", min: 0.5, max: 3, step: 0.1, default: 1 },
] as const;
export default function NumberFlowScreen() {
const [value, setValue] = useState<number>(1234);
const custom = useCustomize(CUSTOMIZE);
const tempo = useRef(custom.tempo);
tempo.current = custom.tempo;
useAutoplay(async ({ wait }) => {
for (const [next, delay] of AUTOPLAY_STEPS) {
await wait(delay / tempo.current);
setValue(next);
}
await wait(1100 / tempo.current);
setValue(1234);
await wait(900 / tempo.current);
});
return (
<Showcase>
<View style={styles.stage}>
<NumberFlow
value={value}
groupSeparator={custom.separator === "none" ? undefined : custom.separator}
fontSize={custom.fontSize}
color={custom.color === COLOR_HEX ? COLOR : custom.color}
fontWeight={custom.fontWeight as "400" | "600" | "800"}
/>
<View style={styles.row}>
<Pressable
style={styles.button}
onPress={() => setValue((v) => v - 1)}
>
<Text style={styles.buttonText}>-1</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => setValue((v) => v + 1)}
>
<Text style={styles.buttonText}>+1</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => setValue((v) => v + 111)}
>
<Text style={styles.buttonText}>+111</Text>
</Pressable>
<Pressable
style={styles.button}
onPress={() => setValue(Math.floor(Math.random() * 199999))}
>
<Text style={styles.buttonText}>random</Text>
</Pressable>
</View>
</View>
</Showcase>
);
}
const styles = StyleSheet.create({
stage: { flex: 1, alignItems: "center", justifyContent: "center" },
row: { flexDirection: "row", gap: 12 },
button: {
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 12,
backgroundColor: "#1c1c1e",
},
buttonText: { color: "#fff", fontSize: 14, fontWeight: "600" },
});valueТип number
padStart?Тип boolean
decimals?Тип number
decimalSeparator?Тип string
groupSeparator?Тип string
fontSize?Тип number
color?Тип string
fontWeight?Тип TextStyle["fontWeight"]
style?Тип StyleProp<ViewStyle>