Orbitdot Loader
A pulsating center circle with multiple dots
Last updated on
Manual
Install the following dependencies:
npm install react-native-reanimated react-native-svgCopy and paste the following code into your project.
component/molecules/orbiting-dots
// SpinnerSegments.tsximport React, { useEffect } from "react";import { StyleSheet, View, ViewStyle } from "react-native";import Svg, { Circle } from "react-native-svg";import Animated, { useSharedValue, useAnimatedStyle, useAnimatedProps, withTiming, withRepeat, withSequence, Easing,} from "react-native-reanimated";const AnimatedView = Animated.createAnimatedComponent(View);const AnimatedCircle = Animated.createAnimatedComponent(Circle);export interface OrbitDotLoaderProps { dotColor?: string; dotRadius?: number; centerRadius?: number; size?: number; duration?: number; numDots?: number; style?: ViewStyle;}export const OrbitDotLoader: React.FC<OrbitDotLoaderProps> = ({ dotColor = "#fff", dotRadius = 4, centerRadius = 5, size = 40, duration = 900, numDots = 4, style,}) => { const rotation = useSharedValue(0); const centerScale = useSharedValue(1); useEffect(() => { rotation.value = withRepeat( withTiming(360, { duration, easing: Easing.linear, }), -1, ); }, [duration]); useEffect(() => { centerScale.value = withRepeat( withSequence( withTiming(1.3, { duration: 400, easing: Easing.out(Easing.ease) }), withTiming(1, { duration: 400, easing: Easing.in(Easing.ease) }), ), -1, ); }, []); const rotateStyle = useAnimatedStyle(() => ({ transform: [{ rotate: `${rotation.value}deg` }], })); const centerProps = useAnimatedProps(() => ({ r: centerRadius * centerScale.value, })); const center = size / 2; const orbitY = center - size * 0.3; return ( <View style={[styles.container, { width: size, height: size }, style]}> <Svg width={size} height={size}> {/* Pulsing center circle */} <AnimatedCircle cx={center} cy={center} r={centerRadius} fill={dotColor} animatedProps={centerProps} /> </Svg> {/* Rotating orbiting dots */} <AnimatedView style={[styles.spinner, rotateStyle]}> <Svg width={size} height={size}> {Array.from({ length: numDots }).map((_, i) => { const angle = (360 / numDots) * i; return ( <Circle key={i} cx={center} cy={orbitY} r={dotRadius} fill={dotColor} transform={`rotate(${angle}, ${center}, ${center})`} /> ); })} </Svg> </AnimatedView> </View> );};const styles = StyleSheet.create({ container: { justifyContent: "center", alignItems: "center", }, spinner: { position: "absolute", justifyContent: "center", alignItems: "center", },});Usage
import { View, Text, StyleSheet } from "react-native";import { GestureHandlerRootView } from "react-native-gesture-handler";import { StatusBar } from "expo-status-bar";import { useFonts } from "expo-font";import { SymbolView } from "expo-symbols";import { DisclosureGroup } from "@/components/molecules/disclosure-group";import DynamicText from "@/components/molecules/dynamic-text";import { DynamicTextItem } from "@/components/molecules/dynamic-text/types";import GooeyText from "@/components/molecules/gooey-text";import { CircleLoadingIndicator, OrbitDotLoader } from "@/components";import { CircularLoader } from "@/components/molecules/Loaders/circular";export default function App() { const [fontLoaded] = useFonts({ SfProRounded: require("@/assets/fonts/sf-pro-rounded.ttf"), HelveticaNowDisplay: require("@/assets/fonts/HelveticaNowDisplayMedium.ttf"), }); const OPTIONS = [ { label: "Edit", icon: "pencil" }, { label: "Duplicate", icon: "doc.on.doc" }, { label: "Share", icon: "square.and.arrow.up" }, { label: "Delete", icon: "trash", destructive: true }, ]; const GOOEY_TEXTS: string[] = ["REACTICX", "IS", "AWESOME!"]; return ( <GestureHandlerRootView style={styles.container}> <StatusBar style="light" /> <View style={styles.content}> <OrbitDotLoader numDots={3} size={40} /> </View> </GestureHandlerRootView> );}const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#0a0a0a", }, content: { paddingHorizontal: 20, paddingTop: 90, justifyContent: "center", alignItems: "center", gap: 0, }, title: { fontSize: 28, fontWeight: "700", color: "#fff", }, subtitle: { fontSize: 15, color: "#555", }, card: { backgroundColor: "#141414", borderRadius: 16, overflow: "hidden", marginTop: 20, }, triggerContent: { padding: 16, }, triggerLeft: { flexDirection: "row", alignItems: "center", gap: 12, }, triggerText: { fontSize: 16, fontWeight: "500", color: "#fff", }, item: { flexDirection: "row", alignItems: "center", gap: 12, padding: 14, backgroundColor: "#1a1a1a", borderRadius: 12, marginBottom: 6, }, itemText: { fontSize: 15, color: "#fff", }, destructiveText: { color: "#ff453a", },});Props
React Native Reanimated
React Native Svg
