npx expo install @shopify/react-native-skia react-native-reanimated
import { Canvas, Circle } from '@shopify/react-native-skia';export const ActivityIndicator = () => {const size = 128;const strokeWidth = 10;const radius = (size - strokeWidth) / 2;const canvasSize = size + 30;return (<Canvas style={{ width: canvasSize, height: canvasSize }}><Circlecx={canvasSize / 2}cy={canvasSize / 2}r={radius}color="white"style="stroke"strokeWidth={strokeWidth}/></Canvas>);};
import { Canvas, Path, Skia } from '@shopify/react-native-skia';import { useMemo } from 'react';export const ActivityIndicator = () => {const size = 128;const strokeWidth = 15;const radius = (size - strokeWidth) / 2;const canvasSize = size + 30;const circle = useMemo(() => {const skPath = Skia.Path.Make();skPath.addCircle(canvasSize / 2, canvasSize / 2, radius);return skPath;}, [canvasSize, radius]);return (<Canvas style={{ width: canvasSize, height: canvasSize }}><Pathpath={circle}color="#6370CA"style="stroke"strokeWidth={strokeWidth}start={0.6}end={1}strokeCap="round"/></Canvas>);};
<Pathpath={circle}style="stroke"strokeWidth={strokeWidth}start={0.6}end={1}strokeCap="round"><SweepGradientc={vec(canvasSize / 2, canvasSize / 2)}colors={['cyan', 'magenta', 'yellow', 'cyan']}/><BlurMask blur={5} style="solid" /></Path>
export const ActivityIndicator = () => {const progress = useSharedValue(0);// Basic rotation animationuseEffect(() => {progress.value = withRepeat(withTiming(1, { duration: 1000, easing: Easing.linear }),-1,false);}, []);const rContainerStyle = useAnimatedStyle(() => ({transform: [{ rotate: `${2 * Math.PI * progress.value}rad` }],}));return (<Animated.View style={rContainerStyle}><Canvas style={{ width: canvasSize, height: canvasSize }}><Pathpath={circle}style="stroke"strokeWidth={strokeWidth}start={0.6}end={1}strokeCap="round"><SweepGradient {...gradientProps} /><BlurMask blur={5} style="solid" /></Path></Canvas></Animated.View>);};
export const ActivityIndicator = () => {// ... previous code// Dynamic path animationconst startPath = useDerivedValue(() =>interpolate(progress.value, [0, 0.5, 1], [0.6, 0.3, 0.6]));return (<Animated.Viewentering={FadeIn.duration(1000)}exiting={FadeOut.duration(1000)}style={rContainerStyle}><Canvas style={{ width: canvasSize, height: canvasSize }}><Pathpath={circle}style="stroke"strokeWidth={strokeWidth}start={startPath} // Now animated!end={1}strokeCap="round"><SweepGradient {...gradientProps} /><BlurMask blur={5} style="solid" /></Path></Canvas></Animated.View>);};
import {BlurMask,Canvas,Path,SweepGradient,Skia,vec,} from '@shopify/react-native-skia';import { useImperativeHandle, useMemo } from 'react';import { View, StyleSheet } from 'react-native';import Animated, {Easing,FadeIn,FadeOut,interpolate,useAnimatedStyle,useDerivedValue,useSharedValue,withRepeat,withTiming,cancelAnimation,} from 'react-native-reanimated';export const ActivityIndicator = () => {const progress = useSharedValue(0);// Animation setupconst play = () => {progress.value = withRepeat(withTiming(1, { duration: 1000, easing: Easing.linear }),-1,false);};const rContainerStyle = useAnimatedStyle(() => ({transform: [{ rotate: `${2 * Math.PI * progress.value}rad` }],}));const startPath = useDerivedValue(() =>interpolate(progress.value, [0, 0.5, 1], [0.6, 0.3, 0.6]));return (<Animated.Viewentering={FadeIn.duration(1000)}exiting={FadeOut.duration(1000)}style={rContainerStyle}><Canvas style={{ width: canvasSize, height: canvasSize }}><Pathpath={circle}style="stroke"strokeWidth={strokeWidth}start={startPath}end={1}strokeCap="round"><SweepGradientc={vec(canvasSize / 2, canvasSize / 2)}colors={['cyan', 'magenta', 'yellow', 'cyan']}/><BlurMask blur={5} style="solid" /></Path></Canvas></Animated.View>);};
Every week I send out a newsletter sharing new things about React Native animations.