Here is my component that renders this preloader.
Figma screenshots:

It works perfectly on iOS, but the mask doesn't work on Android.
iOS screenshots:
import React, { useEffect } from 'react'
import Svg, {
Circle,
Defs,
G,
Path,
Mask,
LinearGradient,
Stop,
} from 'react-native-svg'
import Animated, {
Easing,
useAnimatedStyle,
useSharedValue,
withRepeat,
withTiming,
} from 'react-native-reanimated'
import { LoaderProps } from '@/shared/ui/Loader/model'
import { rem } from '@/lib'
export function Loader({ size = 56, style }: LoaderProps) {
const outerRadius = rem(size)
const segments = rem(size)
const id = Date.now().toString()
const segmentsArray = []
const gradientsArray = []
for (let i = 0; i < segments; i++) {
const startAngle = (i / segments) * 360 - 90
const endAngle = ((i + 1) / segments) * 360 - 90
const largeArc = endAngle - startAngle >= 180 ? 1 : 0
const x1 =
outerRadius + outerRadius * Math.cos((Math.PI * startAngle) / 180)
const y1 =
outerRadius + outerRadius * Math.sin((Math.PI * startAngle) / 180)
const x2 =
outerRadius + outerRadius * Math.cos((Math.PI * endAngle) / 180)
const y2 =
outerRadius + outerRadius * Math.sin((Math.PI * endAngle) / 180)
segmentsArray.push(
<Path
key={i}
d={`M ${outerRadius} ${outerRadius} L ${x1} ${y1} A ${outerRadius} ${outerRadius} 0 ${largeArc} 1 ${x2} ${y2} Z`}
stroke="none"
fill={`url(#${id}-grad-${i})`}
/>,
)
const fromOpacity = i / segments
const toOpacity = (i + 1) / segments
gradientsArray.push(
<LinearGradient
key={i}
id={`${id}-grad-${i}`}
x1="0%"
y1="0%"
x2="0%"
y2="100%">
<Stop
offset="0%"
stopColor="black"
stopOpacity={fromOpacity}
/>
<Stop
offset="100%"
stopColor="black"
stopOpacity={toOpacity}
/>
</LinearGradient>,
)
}
const rotation = useSharedValue(0)
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${rotation.value}deg` }],
}))
useEffect(() => {
rotation.value = withRepeat(
withTiming(360, { duration: 1000, easing: Easing.linear }),
-1,
false,
)
})
return (
<Animated.View
style={[
style,
{
width: rem(size),
height: rem(size),
},
animatedStyle,
]}>
<Svg
width={rem(size)}
height={rem(size)}
viewBox={`0 0 ${rem(size)} ${rem(size)}`}>
<Defs>
{gradientsArray}
<Mask id={id}>
<Circle
cx={rem(size) / 2}
cy={rem(size) / 2}
r={outerRadius}
fill="white"
/>
<Circle
cx={rem(size) / 2}
cy={rem(size) / 2}
r={rem(size) / 2 - rem(size) * 0.9090909}
fill="black"
/>
</Mask>
</Defs>
<G mask={`url(#${id})`}>{segmentsArray}</G>
</Svg>
</Animated.View>
)
}
Description
Here is my component that renders this preloader.

Figma screenshots:
It works perfectly on iOS, but the mask doesn't work on Android.
iOS screenshots:
Android screenshots:
Steps to reproduce
Snack or a link to a repository
SVG version
15.12.1
React Native version
0.81.5
Platforms
Android, iOS
JavaScript runtime
None
Workflow
Expo Go
Architecture
Fabric (New Architecture)
Build type
Debug app & dev bundle
Device
Android emulator
Device model
Pixel 9 Pro XL
Acknowledgements
Yes