Компоненты/Волновой скраулер

Волновой скраулер

31 января 2025

Волновой скраулер на шейдере Skia

Установка

npx astra-rn add wave-scrawler
pnpm dlx astra-rn add wave-scrawler
yarn dlx astra-rn add wave-scrawler
bunx --bun astra-rn add wave-scrawler

Файлы

index.tsx
НазадСтрелочные часыДалееПрелоадер приветствий

Вручную

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

npm install @shopify/react-native-skia react-native-reanimated react-native-worklets
pnpm add @shopify/react-native-skia react-native-reanimated react-native-worklets
yarn add @shopify/react-native-skia react-native-reanimated react-native-worklets
bun 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