Gamote / lottie-react

A lightweight React library for rendering complex After Effects animations in real time using Lottie.
https://lottiereact.com
Other
793 stars 59 forks source link

Help with playSegments #67

Open jordanlambrecht opened 2 years ago

jordanlambrecht commented 2 years ago

I'm trying to have my lottie animation play the first 23 frames, and then skip them when it loops back and restarts and play frames 24-95 indefinitely. I'm dumb and can't seem to figure out how to make this work. Example here of it implemented with react-lottie-player (I'm switching over because you're more active and cool)

Here's my code that isn't working:

function PageHeader_VariableHeight() {
  const playFrames = {
    segments: [
      [0, 23],
      [24, 95],
    ],
    forceFlag: false,
  }
  const LottieAnimation = () => {
    const [animationData, setAnimationData] = useState(undefined)
    useEffect(() => {
      import('@data/Patterns_Peach.json').then(setAnimationData)
    }, [])

    if (!animationData)
      return (<p> Loading</p>)
    return (
      <Lottie
        animationData={animationData}
        loop={true}
        playSegments={playFrames}
        autoplay={true}
        rendererSettings={{ preserveAspectRatio: 'xMidYMid slice' }}
        style={{ height: '100%' }}
      />
    )
  }
  return (
      <LottieAnimation />
  )
}
export default PageHeader_VariableHeight
jordanlambrecht commented 2 years ago

@Gamote Any ideas?

jordanlambrecht commented 2 years ago

@michax @mattvague @PatrickDesign Sorry for the pings, but this is pretty mission-critical for me

PatrickDesign commented 2 years ago

In what way is it not working?

I'm assuming the fact that you're defining a react component within another react component and using dynamic imports doesn't cause the issue.

jordanlambrecht commented 2 years ago

@PatrickDesign Ha, I probably need to separate those components o.O The animation will play just fine. Everything works with no errors. The only issue is the segments part. It simply does not listen to the input and plays the whole animation straight through each loop.

PatrickDesign commented 2 years ago

Could you use the onLoopComplete method to update your segment array to be the shortened one after the first loop? And use the initialSegment prop instead of playSegments.

Something like:

// Defined statically outside of any functions (to keep a stable reference https://lottiereact.com/#initialsegment)
const firstLoopSegment = [0, 23]
const secondLoopSegment = [24, 95]

...

const [activeSegment, setActiveSegment] = useState(firstLoopSegment)

const onLoopComplete = () => {
  setActiveSegment(secondLoopSegment)
}

...

<Lottie onLoopComplete={onLoopComplete} initialSegment={activeSegment} ... />
jordanlambrecht commented 2 years ago

Like this? Because no dice if so =/

import lottie from 'lottie-web'

interface LottieProps {
  animationData: any
  width: number
  height: number
  onLoopComplete: any
  initialSegment: any
}

const firstLoopSegment = [0, 23]
const secondLoopSegment = [24, 95]

const Lottie = ({ animationData }: LottieProps) => {
  const element = useRef<HTMLDivElement>(null)
  const lottieInstance = useRef<any>()

  useEffect(() => {
    if (element.current) {
      lottieInstance.current?.destroy()
      lottieInstance.current = lottie.loadAnimation({
        container: element.current,
        renderer: 'svg',
        loop: true,
        autoplay: true,

        rendererSettings: {
          preserveAspectRatio: 'xMidYMid slice',
        },
        animationData: animationData,
      })
    }
    return () => {
      lottieInstance.current?.destroy()
      lottieInstance.current = null
    }
  }, [animationData])

  return <div style={{ height: '100%', width: '100%' }} ref={element} />
}

const PageHeader_VariableHeight = ({ animationData }) => {
  const [activeSegment, setActiveSegment] = useState(firstLoopSegment)
  const onLoopComplete = () => {
    setActiveSegment(secondLoopSegment)
  }

  return (
    <Lottie
      animationData={animationData}
      width={100}
      height={100}
      onLoopComplete={onLoopComplete}
      initialSegment={activeSegment}
    />
  )
}
export default PageHeader_VariableHeight
jordanlambrecht commented 2 years ago

AHA! I got it!

import lottie from 'lottie-web'

interface LottieProps {
  animationData: any
  width: number
  height: number
}

const Lottie = ({ animationData }: LottieProps) => {
  const element = useRef<HTMLDivElement>(null)
  const lottieInstance = useRef<any>()

  useEffect(() => {
    if (element.current) {
      lottieInstance.current?.destroy()
      lottieInstance.current = lottie.loadAnimation({
        container: element.current,
        renderer: 'svg',
        loop: true,
        // autoplay: true,

        rendererSettings: {
          preserveAspectRatio: 'xMidYMid slice',
        },
        animationData: animationData,
      })
    }
    lottieInstance.current.playSegments(
      [
        [0, 23],
        [24, 95],
      ],
      true,
    )
    return () => {
      lottieInstance.current?.destroy()
      lottieInstance.current = null
    }
  }, [animationData])

  return <div style={{ height: '100%', width: '100%' }} ref={element} />
}

const PageHeader_VariableHeight = ({ animationData }) => {
  return <Lottie animationData={animationData} width={100} height={100} />
}
export default PageHeader_VariableHeight