Skip to content

Android Mask not working #2875

Description

@sokolovdm

Description

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

It works perfectly on iOS, but the mask doesn't work on Android.
iOS screenshots:

Image

Android screenshots:

Image
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>
    )
}

Steps to reproduce

  1. Just add in App layout

Snack or a link to a repository

  • Nope

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Missing reproThis issue need minimum repro scenario

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions