Поставьте зависимости:
npm install expo-haptics react-native-gesture-handler react-native-reanimated react-native-workletspnpm add expo-haptics react-native-gesture-handler react-native-reanimated react-native-workletsyarn add expo-haptics react-native-gesture-handler react-native-reanimated react-native-workletsbun add expo-haptics react-native-gesture-handler react-native-reanimated react-native-workletsСкопируйте эти файлы в src/shared/components/molecules/seek-bar/:
Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.
import { View, StyleSheet, Dimensions, Platform } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useEffect, useRef, useState } from "react";
import { SeekBar } from "@/shared/components/molecules/seek-bar";
import { Showcase, hexToRgb, useAutoplay, useCustomize } from "~/showcase";
const CUSTOMIZE = [
{ key: "activeColor", label: "Цвет заполнения", type: "color", default: "#ffffff" },
{ key: "inactiveColor", label: "Цвет дорожки", type: "color", default: "#ffffff" },
{ key: "inactiveOpacity", label: "Прозрачн. дорожки", type: "slider", min: 0, max: 1, step: 0.05, default: 0.3 },
{ key: "width", label: "Ширина", type: "slider", min: 160, max: 380, step: 2, unit: "px", default: 300 },
{ key: "height", label: "Толщина", type: "slider", min: 2, max: 24, step: 1, unit: "px", default: 8 },
{ key: "activeHeight", label: "Толщина при нажатии", type: "slider", min: 8, max: 60, step: 1, unit: "px", default: 40 },
{ key: "trackScale", label: "Растяжение", type: "slider", min: 1, max: 5, step: 0.1, default: 3.5 },
{ key: "showThumb", label: "Ползунок", type: "toggle", default: false },
{ key: "thumbColor", label: "Цвет ползунка", type: "color", default: "#ffffff" },
] as const;
function rgba(hex: string, alpha: number) {
const { r, g, b } = hexToRgb(hex);
return `rgba(${Math.round(r * 255)}, ${Math.round(g * 255)}, ${Math.round(b * 255)}, ${alpha})`;
}
export default function App() {
const custom = useCustomize(CUSTOMIZE);
const [value, setValue] = useState<number>(0);
const [pressed, setPressed] = useState(false);
const pointerDown = useRef(false);
const releaseOnPointerUp = useRef(false);
useEffect(() => {
if (Platform.OS !== "web") return;
const onDown = () => {
pointerDown.current = true;
};
const onUp = () => {
pointerDown.current = false;
if (!releaseOnPointerUp.current) return;
releaseOnPointerUp.current = false;
setPressed(false);
};
window.addEventListener("pointerdown", onDown, true);
window.addEventListener("pointerup", onUp, true);
window.addEventListener("pointercancel", onUp, true);
return () => {
window.removeEventListener("pointerdown", onDown, true);
window.removeEventListener("pointerup", onUp, true);
window.removeEventListener("pointercancel", onUp, true);
};
}, []);
useAutoplay(async ({ wait }) => {
releaseOnPointerUp.current = false;
const glide = async (from: number, to: number, duration: number) => {
const steps = Math.max(1, Math.round(duration / 40));
for (let step = 1; step <= steps; step++) {
await wait(duration / steps);
setValue(from + ((to - from) * step) / steps);
}
};
try {
setValue(0);
await wait(450);
setPressed(true);
setValue(0.05);
await glide(0.05, 0.4, 200);
await glide(0.4, 0.87, 300);
await glide(0.87, 1, 350);
await wait(300);
await glide(1, 0.47, 400);
await glide(0.47, 0.03, 300);
await wait(200);
setPressed(false);
await wait(500);
} finally {
if (pointerDown.current) releaseOnPointerUp.current = true;
else setPressed(false);
}
});
return (
<Showcase>
<GestureHandlerRootView style={styles.container}>
<StatusBar style="inverted" />
<SeekBar
value={value}
onValueChange={setValue}
key={custom.height}
width={custom.width}
height={custom.height}
activeColor={custom.activeColor}
inactiveColor={rgba(custom.inactiveColor, custom.inactiveOpacity)}
thumbColor={custom.thumbColor}
showThumb={custom.showThumb}
trackScale={custom.trackScale}
activeHeight={custom.activeHeight}
pressed={pressed}
/>
</GestureHandlerRootView>
</Showcase>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0a0a0a",
justifyContent: "center",
alignItems: "center",
},
header: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
paddingTop: 120,
},
title: {
fontSize: 35,
color: "#fff",
letterSpacing: 2,
},
subtitle: {
fontSize: 15,
color: "#aaa",
},
headerRight: {
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: "#1a1a1a",
justifyContent: "center",
alignItems: "center",
},
card: {
width: "100%",
height: 340,
borderRadius: 24,
overflow: "hidden",
},
cardImage: {
width: 300,
height: 400,
resizeMode: "cover",
},
cardGradient: {
...StyleSheet.absoluteFillObject,
},
cardContent: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
padding: 20,
gap: 8,
},
albumName: {
fontSize: 16,
color: "#fff",
letterSpacing: 1,
},
artistRow: {
flexDirection: "row",
alignItems: "center",
gap: 6,
},
artistText: {
fontSize: 13,
color: "rgba(255,255,255,0.7)",
},
dot: {
width: 3,
height: 3,
borderRadius: 1.5,
backgroundColor: "rgba(255,255,255,0.4)",
},
yearText: {
fontSize: 13,
color: "rgba(255,255,255,0.5)",
},
footer: {
paddingHorizontal: 24,
marginTop: 32,
gap: 20,
},
nowPlaying: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "#141414",
padding: 12,
borderRadius: 16,
},
nowPlayingLeft: {
flexDirection: "row",
alignItems: "center",
gap: 12,
flex: 1,
},
nowPlayingImage: {
width: 48,
height: 48,
borderRadius: 10,
},
nowPlayingInfo: {
flex: 1,
gap: 2,
},
nowPlayingTitle: {
fontSize: 14,
fontWeight: "600",
color: "#fff",
},
nowPlayingArtist: {
fontSize: 12,
color: "#666",
},
nowPlayingControls: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: "#fff",
justifyContent: "center",
alignItems: "center",
},
dots: {
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
gap: 6,
},
dotIndicator: {
width: 6,
height: 6,
borderRadius: 3,
backgroundColor: "#333",
},
dotIndicatorActive: {
width: 20,
backgroundColor: "#fff",
},
});valueТип number
onValueChangeТип (value: number) => void
width?Тип number
height?Тип number
activeHeight?Тип number
activeColor?Тип string
inactiveColor?Тип string
disabled?Тип boolean
tapToSeek?Тип boolean
thumbSize?Тип number
thumbColor?Тип string
showThumb?Тип boolean
trackScale?Тип number
thumbScale?Тип number
containerScale?Тип number
pressed?Тип boolean