Поставьте зависимости:
npm install @shopify/react-native-skia react-native-reanimated react-native-workletspnpm add @shopify/react-native-skia react-native-reanimated react-native-workletsyarn add @shopify/react-native-skia react-native-reanimated react-native-workletsbun add @shopify/react-native-skia react-native-reanimated react-native-workletsСкопируйте эти файлы в src/shared/components/organisms/wave-scrawler/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import React, { useMemo, useRef, useState } from "react";
import { StyleSheet, View, Pressable } from "react-native";
import WaveScrawler from "@/shared/components/organisms/wave-scrawler";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";
import { PHOTO_SOURCES } from "~/assets/photos";
import { useSafeAreaInsets } from "react-native-safe-area-context";
const IMAGES = [
{
uri: PHOTO_SOURCES["wave-scrawler-1"],
},
{
uri: PHOTO_SOURCES["wave-scrawler-2"],
},
{
uri: PHOTO_SOURCES["wave-scrawler-3"],
},
];
const CUSTOMIZE = [
{ key: "photos", label: "Изображения", type: "images", default: IMAGES.map((image) => image.uri) },
{ key: "amplitude", label: "Амплитуда", type: "slider", min: 0, max: 3, step: 0.05, default: 1 },
{ key: "waves", label: "Волны", type: "slider", min: 0.5, max: 6, step: 0.01, default: 1.92 },
{ key: "colorSeparation", label: "Разделение цвета", type: "slider", min: 0, max: 1, step: 0.01, default: 0.14 },
{ key: "duration", label: "Переход", type: "slider", min: 200, max: 3000, step: 10, unit: "ms", default: 680 },
{ key: "interval", label: "Пауза кадра", type: "slider", min: 300, max: 5000, step: 50, unit: "ms", default: 880 },
{ key: "radius", label: "Скругление", type: "slider", min: 0, max: 60, step: 1, unit: "px", default: 0 },
{ key: "background", label: "Фон", type: "color", default: "#000000" },
] as const;
export default function WaveScrawlerExample() {
const [index, setIndex] = useState(0);
const custom = useCustomize(CUSTOMIZE);
const insets = useSafeAreaInsets();
const intervalRef = useRef(custom.interval);
intervalRef.current = custom.interval;
const photosKey = custom.photos.join("|");
const sources = useMemo(
() => custom.photos.map((uri) => ({ uri: String(uri) })),
[photosKey],
);
const next = () => setIndex((i) => (i + 1) % Math.max(1, sources.length));
useAutoplay(async ({ wait }) => {
await wait(300);
next();
await wait(intervalRef.current);
});
return (
<Showcase>
<View
style={[
styles.container,
{ backgroundColor: custom.background, marginTop: -insets.top, paddingTop: insets.top },
]}
>
<View style={[styles.card, { borderRadius: custom.radius }]}>
<WaveScrawler
source={sources}
index={index}
amplitude={custom.amplitude}
waves={custom.waves}
duration={custom.duration}
colorSeparation={custom.colorSeparation}
style={styles.scrawler}
/>
<Pressable onPress={next} style={StyleSheet.absoluteFill} />
</View>
</View>
</Showcase>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#000",
justifyContent: "center",
alignItems: "center",
},
card: {
width: 320,
height: 420,
overflow: "hidden",
backgroundColor: "#000",
},
scrawler: {
flex: 1,
},
dots: {
position: "absolute",
bottom: 16,
alignSelf: "center",
flexDirection: "row",
gap: 6,
},
dot: {
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: "rgba(255,255,255,0.4)",
},
dotActive: {
backgroundColor: "#fff",
},
});sourceТип ImageSource[]
indexТип number
duration?Тип number
amplitude?Тип number
waves?Тип number
colorSeparation?Тип number
style?Тип StyleProp<ViewStyle>
onTransitionEnd?Тип (index: number) => void