Jump to content
Search Community

Hook a Lottie animation up to ScrollTrigger

lucky111 test
Moderator Tag

Recommended Posts

Hi I testing to connect a Lottie animation to scrolltrigger.
In the documentation there is a nice example with a pin thats work wery well.


https://greensock.com/docs/v3/HelperFunctions#lottie

 

It refere to a guy named Chris Gannon who has develop a tool for this and work as an inspiration for the example i guess.

When I study the two examples I can notice quite a different in
approach in solving this.
Both examples working well for me but I'm curios to know whats the differance and witch approach is the best and most effective.

https://github.com/chrisgannon/ScrollLottie

 


 

The two pens

 




See the Pen QWdjEbx by GreenSock (@GreenSock) on CodePen




 

What approach is the best!!


 

See the Pen 7e302171605cbc4274ce44733189ffe9 by chrisgannon (@chrisgannon) on CodePen

Link to comment
Share on other sites

Hi Lucky111,

 

It's basically the same approach really - Both use GSAP ScrollTrigger to scroll through a Lottie animation.

The only difference is that Chris Gannon's JS is in an external JavaScript file and the GSAP one isn't.

Your choice as to whether you want his script to do the heavy lifting for you or write it yourself.

 

https://github.com/chrisgannon/ScrollLottie

https://s3-us-west-2.amazonaws.com/s.cdpn.io/35984/ScrollLottie.js


 

Link to comment
Share on other sites

Thanks Cassi for your fast response

When I look in the code the approch is complete different i looks to me

in The gsap example

        gsap.to(playhead, {
            frame: animation.totalFrames - 1,
            ease: 'none',
            onUpdate: () => animation.goToAndStop(playhead.frame, true),
            scrollTrigger: st
        });
    });


You use scrolltrigger inside a gsap.to animation of the whole Lottie animation




But in Chris code

 
 
ScrollTrigger.create({
trigger: obj.target,
scrub: true,
pin: true,
start: "top top",
end: endString,
onUpdate: self => {
if(obj.duration) {
gsap.to(timeObj, {
duration: obj.duration,
currentFrame:(Math.floor(self.progress * (anim.totalFrames - 1))),
onUpdate: () => {
anim.goToAndStop(timeObj.currentFrame, true)
},
ease: 'expo'
})
} else {
anim.goToAndStop(self.progress * (anim.totalFrames - 1), true)
}
}

});

He create a scrolltiger and do a lot of small gsap.to animations


I'm very keen to understand the diffrent approches for those two way to code this task?

 

Link to comment
Share on other sites

Ok!

 

So they're really just different ways of using ScrollTrigger.

 

You can add a ScrollTrigger to a tween or timeline, or use it standalone and fire callbacks.

Simplified version - 

See the Pen 6502e216925c5366583cb5186673c2d0 by cassie-codes (@cassie-codes) on CodePen



It's hard to say which is best. I would probably go with the GSAP helper function as it's evolved from Chris's original library and doesn't require an external resource - but they're both good.

Link to comment
Share on other sites

There are certainly differences between the two approaches. @GreenSock mentioned some in another thread when he created that helper function.

 

On 3/24/2021 at 5:28 PM, GreenSock said:
  • Improved performance in the way it handles scrubbing
  • You can add any ScrollTrigger-related value to the object you pass in (trigger, start, end, onEnter, onLeave, onUpdate, markers, whatever.) so you get tons of flexibility

 

He may eventually stop by this thread and comment also. 😉

  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...

Yikes! I totally missed this thread, @lucky111. Sorry! 

 

Yes, I would definitely recommend the helper function in the docs, no question. 

  1. It will perform significantly better because it doesn't constantly create a new tween over and over again on each tick to accomplish the scrubbing technique. Plus the non-GreenSock one didn't set overwrite: true, so it was creating a lot of conflicting tweens. You'd probably never notice functionally, but it's bad for performance. 
  2. It gives you a lot more flexibility, as mentioned above - you can add any ScrollTrigger-related value to the object you pass in. 
  3. You're not limited to "fast", "medium" and "slow" speeds - you can set ANY end value, like end: "+=2000" or whatever. But it still works with speed if you prefer that syntax.  

So overall it's more concise, it performs much better, and offers way more flexibility. I don't mean that as a knock on Chris's version - he built that when he was very new to ScrollTrigger. We all know what a genius he is with animation in general - I just have an unfair advantage of knowing all the ins and outs of ScrollTrigger (having built it) :)

  • Like 2
Link to comment
Share on other sites

  • 2 years later...

Hello! Amazing solutions, thanks for this!

I'd love to be able to have a local json as the path instead of a https link, I couldn't find a solution how to make this work yet, do you have any idea?
 

path: '@/assets/animation_lkmhj2hi.json'


FYI I am trying to add this in react using 'lottie-web'

Link to comment
Share on other sites

Hi @andwhy and welcome to the GreenSock forums!

 

Thanks for being a Club GreenSock member and suporting GreenSock! 💚

 

The issue here could be with the fact that you are using an import path that uses an alias such as @/ for shortening the import path directly on the Lottie config. Maybe you would have to give that particular file a name like this:

import lottiePath from "@/assets/animation_lkmhj2hi.json";

Then use lottiePath in your Lottie config as the animation data property, not the path property, like this:

import lottieJson from "@/assets/my-lottie-file.json"

gsap.registerPlugin(ScrollTrigger);

export default function Scroll() {
  const ScrollRef = useRef();
  const LottieRef = useRef();
  const lottieTween = useRef();

  useLayoutEffect(() => {
    const ctx = gsap.context(() => {
      if (!lottieTween.current) {
        //Animate lottie on scroll
        lottieTween.current = LottieScrollTrigger({
          target: LottieRef.current,
          animationData: lottieJson,
          speed: 'medium',
          start: 'top top',
          end: 'bottom -100%',
          pin: ScrollRef.current,
          scrub: 1,
        });
      }
    }); // <- Scope!
    return () => ctx && ctx.revert(); // <- Cleanup! */
  }, []);

  return (
    <div ref={ScrollRef} className="scroll">
      <div ref={LottieRef} className="lottie"></div>
    </div>
  );
}

Is important to note the fact that the lottie animation is being stored in a ref and we check if that ref already exists. This because on Strict Mode (since version 18) React runs the effect hooks twice, so you'll get a duplicated lottie ScrollTrigger instance that could cause some issues.

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...