Saving an animation instance for later use

Viewed 176

I'm trying to save a Lottie object with methods for later use (another useEffect) into the state, but the state says it's null.

I would like to use the anim.playSegments method specified in the documentation.

Lottie documentation.

Update #1: Tried including anim into the dependency array and event listener for checking if the animation has loaded, but that didn't help and the application resulted in a cycle.

Animation now loads indefinitely

Update #2: Declared a variable outside of the useEffect and defined it inside of it. Got rid of the state approach. Anim is still null.

import React, { useEffect, useRef, useState } from 'react';
import { useColorMode } from '@chakra-ui/react';
import lottie from 'lottie-web';
import animationData from '../assets/json/sun-to-night'

const ColorMode = () => {
  const container = useRef(null)
  const { colorMode, toggleColorMode } = useColorMode()

  // Segments of the animation
  const midToEnd = [14, 28]
  const startToMid = [0, 14]

  let anim = null

  useEffect(() => {
    anim = lottie.loadAnimation({
      container: container.current,
      animationData: animationData,
      autoplay: false,
      loop: false,
      name: 'anim'
    })

    // Here the animation instance exists and animation is played
    console.log(`${anim}`)
    anim.playSegments(
      (colorMode === 'dark'
        ? startToMid
        : midToEnd), true
    )
  }, []);

  useEffect(() => {
    //anim is null in here and the React app throws an error
    anim.addEventListener('DOMLoaded', () => {
      anim.playSegments(
        (colorMode === 'dark'
          ? startToMid
          : midToEnd), true
      )
    })
  }, [colorMode])


  return (
    <>
      <div>
          ref={container}
          onClick={toggleColorMode}
      </div>
    </>
  )
}

export default ColorMode;
2 Answers

You need to wait until your lottie is loaded. You can do this by listening for the DOMLoaded event:

 const anim = lottie.loadAnimation({
        container: container.current,
        animationData: animationData,
        autoplay: false,
        loop: false,
        name: 'anim'
      });

anim.addEventListener('DOMLoaded', () => {
  anim.playSegments(
        (colorMode === 'dark'
          ? startToMid
          : midToEnd), true
      )
});

More here: https://github.com/airbnb/lottie-web#events

I solved the problem by using lottie's built-in method "getRegisteredAnimations" in the second useEffect hook and parsing the first element of the array which was my animation.

Then I stored it into a variable which I could manipulate my animation with. It's a bit of a workaround, but it works. If anyone has a better approach, don't hesitate to share.

Here is the final code:

import React, { useEffect, useRef, useState } from 'react';
import { useColorMode } from '@chakra-ui/react';
import lottie from 'lottie-web';
import animationData from '../assets/json/sun-to-night'

const ColorMode = () => {
  const container = useRef(null)
  const { colorMode, toggleColorMode } = useColorMode()

  // Segments of the animation
  const midToEnd = [14, 28]
  const startToMid = [0, 14]

  let anim = null

  useEffect(() => {
    anim = lottie.loadAnimation({
      container: container.current,
      animationData: animationData,
      autoplay: false,
      loop: false,
      name: 'anim'
    })
    // This bit worked from the beginning
    anim.playSegments(
      (colorMode === 'dark'
        ? startToMid
        : midToEnd), true
    )
  }, []);

  useEffect(() => {
    anim = lottie.getRegisteredAnimations()[0]
    //anim no longer throws null
    anim.playSegments(
      (colorMode === 'dark'
        ? startToMid
        : midToEnd), true
    )
  }, [colorMode])

  return (
    <>
      <div>
          ref={container}
          onClick={toggleColorMode}
      </div>
    </>
  )
}

export default ColorMode;
Related