rive-app / rive-react-native

MIT License
510 stars 35 forks source link

Setting initial state #115

Open evelant opened 2 years ago

evelant commented 2 years ago

Description

I'm trying to make an Avatar component with Rive. There may be several avatars on the screen at once, each with different outfits. This requires hiding and showing layers via inputs on state machines. The issue with this approach is that there is a "flash of unstyled content". Each avatar is shown for 1 frame in a default state after which the ref is initialized and I can trigger the state machines to instantly show/hide parts to make it look appropriate.

It would be ideal if there was a way to declaratively specify states so that the animation can be loaded in the correct state instead of needing to transition first from a default state.

I can probably work around this by having the root be 0% opacity until I trigger the state update after load but this seems like a sub-optimal solution.

Provide a Repro

//this results in a default state being shown on frame1 after which the "real" state is imperatively set via useEffect
//it would be better if we could specify input states in a declarative way via props so animations can load initialized to correct values
const RiveInstance = observer(function RiveInstance({ num }: { num: number }) {
    const riveRef = React.useRef<RiveRef>(null)

    useEffect(() => {
        if (riveRef.current) {
            riveRef.current.setInputState("ShowHatHeavy", "HatHeavyVariant", (num % 5) + 1)
            riveRef.current.setInputState("ShowShield", "ShieldVariant", (num % 5) + 1)
        }
    }, [num, riveRef.current])

    return (
        <View key={num}>
            <Rive autoplay={false} ref={riveRef} style={{ width: 50, height: 50 }} resourceName={"test_avatar"} />
        </View>
    )
})

const TestRive = observer(function TestRive() {
    return (
        <ScrollView style={{ width: "100%", height: 150 }}>
            {_.range(10).map((i) => (
                <RiveInstance num={i} key={i} />
            ))}
        </ScrollView>
    )
})

Source .riv/.rev file

avatar_test.rev.zip

rlods commented 3 months ago

Several years later, it seems the rive-react-native library still doesn't provide a way override to set the initial value of a component before it's mounted and its ref made available.

Meaning that when a rive component is mounted, there is always a short flash that shows the unwanted internal default state before showing an up-to-date state.

Is there any plans to support that?

Thanks in advance