LottieFiles / lottie-react

lottie web player as a react component
https://lottiefiles.com
MIT License
721 stars 80 forks source link

Learning the instant frame #22

Closed ismailyagci closed 2 years ago

ismailyagci commented 3 years ago

Hello, I want to play an animation only between certain frames and I could not find a document about it. I just found the function named setSeeker. I can start it in certain frame, but how do I stop it?

github-actions[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs.

DavidSilveraGabriel commented 3 years ago

Hi buddy, I have the same question, have you fixed the issue ? or have finded a way for that?

github-actions[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs.

soerenBoisen commented 2 years ago

This is a very relevant question.

I attempted to use state.seeker to get the current frame, but it appears to work async like React state, so it does not get updated immediately after a call to setSeeker, meaning that my javascript ends up in an infinite loop.

samuelOsborne commented 2 years ago

@soerenBoisen I'll take a look at this and get back to you about this problem

soerenBoisen commented 2 years ago

A brilliant solution would be to have a version of the play call like the iOS player, where you can play/loop a range of the animation: http://airbnb.io/lottie/#/ios?id=play-with-frame-time

But that method seems to be missing from the underlying Lottie web player.

samuelOsborne commented 2 years ago

Hi @soerenBoisen, @gron01 you can play segments of lottie animations like this:

import './App.css';
import { Player, Controls } from '@lottiefiles/react-lottie-player';
import React, {useRef} from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { lottie: null }; // initialize your state
  }

  render() {
    return (
      <Player
        lottieRef={instance => {
          this.setState({ lottie: instance }); // the lottie instance is returned in the argument of this prop. set it to your local state
        }}
        onEvent={event => {
          if (event === 'load') {
            this.state.lottie.playSegments([20,30], true);
          }
        }}
        autoplay={true}
        loop={true}
        controls={true}
        src="https://assets3.lottiefiles.com/packages/lf20_XZ3pkn.json"
        style={{ height: '300px', width: '300px' }}
      ></Player>
    );
  }
}

export default App;

This will play the animation from frame 20 to 30. If you don't want it to loop, set the loop prop to false.

Basically after the animation is loaded, we catch the load event, access the internal lottie instance and call the playSegments method on it to play parts of our lottie animation.

soerenBoisen commented 1 year ago

Thank you Samuel!

I finally had a chance to try out your suggestion and it worked perfectly for playing only parts of an animation.

For those interested, here is how you can do it in a functional component (this is using MUI, but that is not a requirement):

type ThankYouStepProps = Readonly<{}>

export function ThankYouStep({}: ThankYouStepProps) {
    const classes = useStyles({})

    const [lottie, setLottie] = useState<AnimationItem>()
    const [loaded, setLoaded] = useState(false)

    const handleLottieEvent = useCallback((event: PlayerEvent) => {
        if (event === PlayerEvent.Load) setLoaded(true)
    }, [setLoaded])

    useEffect(() => {
        // Need to wait for both our reference to be set and for the animation to be loaded
        // before calling playSegments
        if (lottie && loaded) {
            // Play first part of animation then loop part of it
            lottie.playSegments([[0, 200], [100, 200]], true)
        }
    }, [lottie, loaded])

    return (
        <Slide direction="left" in={true}>
            <div className={classes.thankYouContainer}>
                <LocalizedStrict id="user-survey-thank-you-message">
                    <Typography variant="h5" align="center" paragraph>
                        Thank you for your feedback
                    </Typography>
                </LocalizedStrict>
                <div className={classes.animationContainer}>
                    <Player src={TrophyAnim} controls={false} loop lottieRef={setLottie} onEvent={handleLottieEvent} />
                </div>
            </div>
        </Slide>
    )
}