Pulsing Loader
Multiple dots that smoothly pulse in a sequence
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/pulsing-dots
// PulsingDots.tsximport React, { useEffect } from "react";import { View, StyleSheet } from "react-native";import Svg, { Circle, Defs, LinearGradient, Stop } from "react-native-svg";import Animated, { useSharedValue, useAnimatedProps, withRepeat, withTiming, Easing,} from "react-native-reanimated";import type { PulsingDotsProps } from "./PlusingDots.types";const AnimatedCircle = Animated.createAnimatedComponent(Circle);export const PulsingDots: React.FC<PulsingDotsProps> = ({ dotCount = 3, radius = 6, spacing = 25, duration = 800, color = "#00C896", gradient,}): React.ReactNode & React.JSX.Element => { const opacities = Array.from({ length: dotCount }, () => useSharedValue(0.3)); useEffect(() => { opacities.forEach((opacity, i) => { setTimeout(() => { opacity.value = withRepeat( withTiming(1, { duration, easing: Easing.inOut(Easing.ease), }), -1, true, ); }, i * 200); }); }, []); const animatedProps = opacities.map((val) => useAnimatedProps(() => ({ opacity: val.value })), ); const totalWidth = radius * 2 + (dotCount - 1) * spacing; return ( <View style={styles.container}> <Svg width={totalWidth} height={radius * 3}> <Defs> {gradient?.map((g, i) => ( <LinearGradient key={i} id={`grad-${i}`} x1="0" y1="0" x2="1" y2="1" > <Stop offset="0%" stopColor={g.from} /> <Stop offset="100%" stopColor={g.to} /> </LinearGradient> ))} </Defs> {animatedProps.map((props, i) => { const gradientId = gradient?.[i] ? `url(#grad-${i})` : gradient?.[0] ? `url(#grad-0)` : color; return ( <AnimatedCircle key={i} cx={radius + i * spacing} cy={radius * 1.5} r={radius} fill={gradientId} animatedProps={props} /> ); })} </Svg> </View> );};const styles = StyleSheet.create({ container: { height: 40, 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, PulsingDots,} 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}> <PulsingDots color="#fff" /> </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
