Spinner Arc

A rotating arc with a gradient that spins continuously

Last updated on

Edit on GitHub

Manual

Install the following dependencies:

npm install react-native-reanimated react-native-svg

Copy and paste the following code into your project. component/molecules/spinner-arc

import React from "react";import Svg, { Circle, Defs, LinearGradient, Stop } from "react-native-svg";import Animated, {  useSharedValue,  useAnimatedStyle,  withRepeat,  withTiming,  Easing,} from "react-native-reanimated";import { View, StyleSheet } from "react-native";const AnimatedView = Animated.createAnimatedComponent(View);interface SpinnerArcProps {  size?: number;  colorStart?: string;  colorEnd?: string;  strokeWidth?: number;  speed?: number;  backgroundColor?: string;  arcLength?: number;}export const SpinnerArc: React.FC<SpinnerArcProps> = ({  size = 40,  colorStart = "#FF4E4E",  colorEnd = "#FF7A00",  strokeWidth = 4,  speed = 1000,  backgroundColor = "#ddd",  arcLength = 90,}) => {  const rotation = useSharedValue(0);  // Rotation animation for spinner  rotation.value = withRepeat(    withTiming(360, {      duration: speed,      easing: Easing.linear,    }),    -1,  );  const animatedStyle = useAnimatedStyle(() => ({    transform: [{ rotate: `${rotation.value}deg` }],  }));  return (    <AnimatedView      style={[styles.loader, { width: size, height: size }, animatedStyle]}    >      <Svg width={size} height={size}>        <Circle          cx={size / 2}          cy={size / 2}          r={size / 2 - strokeWidth}          stroke={backgroundColor}          strokeWidth={strokeWidth}          fill="none"        />        <Defs>          <LinearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">            <Stop offset="0%" stopColor={colorStart} />            <Stop offset="100%" stopColor={colorEnd} />          </LinearGradient>        </Defs>        <Circle          cx={size / 2}          cy={size / 2}          r={size / 2 - strokeWidth}          stroke="url(#gradient)"          strokeWidth={strokeWidth}          strokeDasharray={arcLength}          strokeLinecap="round"          fill="none"        />      </Svg>    </AnimatedView>  );};const styles = StyleSheet.create({  loader: {    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,  SpinnerArc,} 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}>        <SpinnerArc          arcLength={270}          colorEnd="#6366f1"          colorStart="#8b5cf6"          backgroundColor="transparent"          size={64}          speed={500}          strokeWidth={3}        />      </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