Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events are not firing in v5 #192

Closed
AndrewBudziszek opened this issue Jul 31, 2023 · 5 comments
Closed

Events are not firing in v5 #192

AndrewBudziszek opened this issue Jul 31, 2023 · 5 comments

Comments

@AndrewBudziszek
Copy link

AndrewBudziszek commented Jul 31, 2023

Description

Prop events aren't firing as expected in v5.

Provide a Repro

Related - #170 (comment)

Events aren't firing on v5 on an iOS 16 sim.

import React from 'react';
import View from 'react-native';
import Rive, { RiveRef } from 'rive-react-native';

function CoolThing() {
  const riveRef = React.useRef<RiveRef>(null)

  React.useEffect(() => {
    riveRef.current?.play()
  }

  return (
    <View>
      <Rive
        autoplay={false}
        ref={riveRef}
        resource={'CoolResourceName'}
        onLoopEnd={() => console.log('loop ended')}
        onStop={() => console.log('stopped')}
        onPlay={() => console.log('playing')}
        onPause={() => console.log('paused')}
        onError={(error) => console.log(error)}
      />
    </View>
  )
}

The only event that fires is onPlay.

Source .riv/.rev file

Not available

Expected behavior

I expect onLoopEnd, onPause, or onStop to fire. Only onPlay is firing.

Screenshots

None

Device & Versions (please complete the following information)

  • Device: iOS Simulator (iPhone 14 Pro)
  • iOS 16.2
  • NPM Version: 8.11.0

Additional context

None

@AndrewBudziszek AndrewBudziszek changed the title Events not firing in v5 Events are not firing in v5 Jul 31, 2023
@HayesGordon
Copy link
Contributor

Hi @AndrewBudziszek,

It may be that you upgraded from a much older version. The default behaviour changed so that the preferred animation to play is the default StateMachine (which can be set in the editor). Or you can programatically provide which animation or state machine to play by setting stateMachineName or animationName props (be sure to specify the correct names)

  • onLoopEnd will only be triggered when playing a single animation, not for State Machines (as a state machine does not have a concept of a loop). In the future we might add the ability to add notification for a state machine when animations loop within it. If that is something that will be useful to you be sure to suggest it in our feature request channel on Discord.
  • Not sure why you're not seeing onPause and onStop and we will need more information to investigate.
  • See onStateChanged in the example below - this is triggered when the State Machine changes state (playing a different animation for example)

I've attached a working sample for you to try as a base:

import * as React from 'react';
import { Button, SafeAreaView, ScrollView, StyleSheet } from 'react-native';
import Rive, { Fit, RiveRef } from 'rive-react-native';

export default function StateMachine() {
  const riveRef = React.useRef<RiveRef>(null);

  const pauseAnimation = () => {
    riveRef.current?.pause();
  };

  const playAnimation = () => {
    riveRef.current?.play();
  };

  const setLevel = (n: number) => {
    riveRef.current?.setInputState('skill-controller', 'level', n);
  };

  return (
    <SafeAreaView style={styles.safeAreaViewContainer}>
      <ScrollView contentContainerStyle={styles.container}>
        <Rive
          ref={riveRef}
          autoplay={true}
          fit={Fit.Cover}
          style={styles.box}
          stateMachineName="skill-controller"
          onPlay={(animationName, isStateMachine) => {
            console.log(
              'onPlay: ',
              animationName,
              'isStateMachine: ',
              isStateMachine
            );
          }}
          onPause={(animationName, isStateMachine) => {
            console.log(
              'onPause:',
              animationName,
              'isStateMachine: ',
              isStateMachine
            );
          }}
          onStop={(animationName, isStateMachine) => {
            console.log(
              'onStop: ',
              animationName,
              'isStateMachine: ',
              isStateMachine
            );
          }}
          onLoopEnd={(animationName, loopMode) => {
            console.log('onLoopEnd: ', animationName, 'loopMode: ', loopMode);
          }}
          onStateChanged={(stateMachineName, stateName) => {
            console.log(
              'onStateChanged: ',
              'stateMachineName: ',
              stateMachineName,
              'stateName: ',
              stateName
            );
          }}
          resourceName={'skills_listener'}
        />
        <Button title="Pause" onPress={pauseAnimation} />
        <Button title="Play" onPress={playAnimation} />
        <Button
          title="Level - Beginner"
          onPress={() => {
            setLevel(0);
          }}
        />
        <Button
          title="Level - Intermediate"
          onPress={() => {
            setLevel(1);
          }}
        />
        <Button
          title="Level - Expert"
          onPress={() => {
            setLevel(2);
          }}
        />
      </ScrollView>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  safeAreaViewContainer: {
    flex: 1,
  },
  container: {
    flexGrow: 1,
    alignItems: 'center',
    justifyContent: 'center',
    marginBottom: 150,
  },
  wrapper: {
    flex: 1,
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
    marginBottom: 20,
  },
  box: {
    width: '100%',
    height: 500,
    marginVertical: 20,
  },
  picker: {
    width: '100%',
    height: 50,
  },
  radioButtonsWrapper: {
    flexDirection: 'row',
  },
  radioButtonWrapper: {
    flexDirection: 'row',
    alignItems: 'center',
    marginRight: 16,
  },
});

riv:
skills_listener.riv.zip

@AndrewBudziszek
Copy link
Author

Thanks for the help!

In my case, we're using animations - not state machines. The same concept applies to my case as suggested here - I needed the animation name. It looks like I got it to work with your help here.

That being said, when using an animation name that doesn't exist, the app crashes with no error propagating.

Tangentially, do you have any defined expectations on how Rive file should be handed off to devs? A lot of the issues I had here had to do with not understanding how Rives are created.

With Lottie, all we needed was the file. With Rive, it sounds like when a hand off happens, we should also be given the type of Rive as well as the name of the animation/state machine that was defined in the Rive editor (both things devs can't derive on their own from the file.)

@HayesGordon
Copy link
Contributor

Glad you got it working @AndrewBudziszek

To catch errors in the React Native SDK you need to provide an onError prop, see an example here.

Though there may be a problem with the underlying iOS runtime, that results in the errors not being handled correctly. We'll investigate this soon. But the above error handling should work on Android, if you encounter issues on both Android + iOS then it's a React Native problem. Otherwise it's platform specific.

With regards to your question on hand off: That communication is only needed if you want to play a specific animation or state machine at runtime, or modify state machine inputs (which is part of the magic of Rive). Alternatively, you can setup the default animations to play within the editor:

  • Set the default state machine, and configure it to play the animation you want.
  • Or, delete the state machine and only have your animations. Then the first animation it finds will be played (which is probably what is happening for you now).

That said though, I think it's better to be explicit at runtime and specify the name of the animation or state machine you want to play (same for specifying the artboard name). And to use any of the more advanced features, such as inputs and events, there will have to be some communication between the designers and engineers

Please note though that there are known discrepancies in our various runtimes with regards to the default behaviour - which is unfortunate for React Native as it uses both Android + iOS. We're busy resolving these and it will be pushed out over time. Which is why I recommend specifying the names at runtime

@HayesGordon
Copy link
Contributor

Going to close this issue for now, but feel free to reopen if you encounter issues. We will investigate the iOS error handling 👍

@AndrewBudziszek
Copy link
Author

@HayesGordon Awesome. Thanks for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants