Search code examples
javascriptreactjsreact-hooksjsxlottie

Saving an animation instance for later use


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;

Solution

  • 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;