Search code examples
javascriptscrollmagiclottiebodymovingsap

Controlling a Lottie Animation with ScrollMagic and TimelineMax


I'm trying to use ScrollMagic to control a TimelineMax which moves the playhead of a Lottie animation.

So, as the user scrolls, the animation plays relative to the speed and direction of the scroll. I'm so close and need a little help to bring the effect home.

First, I include my Libraries

<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>

Then...

  // init scrollmagic
  var controller = new ScrollMagic.Controller();

Now, the animation setup...

  // Manage Animation
  var animation = bodymovin.loadAnimation({
    container: document.getElementById('lottie'), // Required
    path: '/static/animations/animation.json', // Required
    renderer: 'svg', // Required
    loop: false, // Optional
    autoplay: false, // Optional
    name: "Welcome to Awesomeness", // Name for future reference. Optional.
  });

This is where I'm struggling:

  // Setup Timeline
  var lottieControl = new TimelineMax({ repeat: -1, yoyo: true }); // <-- don't loop and let the timeline go back and forth
  lottieControl.to({ frame:0 }, 1, { // <-- is this right? I'm telling the timeline to start at frame 0
    onUpdate:function(){
      animation.goToAndStop(Math.round(this.target.frame), true) // <-- move the playback head of the animation to the target frame that has been rounded and use frames not time (true)
    },
    ease:Linear.easeNone
  })

Finally, bring it all back together...

  // Attach to scroll
  var lottieScene = new ScrollMagic.Scene({
        duration: '80%',
        offset: 1
    })
    .setPin("#header-scroll")
    .setTween(lottieControl)
    .addTo(controller);

For the life of me, I can't see if I'm using the goToAndStop method right or not. Thanks for your time.


Solution

  • After hours of testing I found the answer. I was looking at the wrong thing. I needed to tie the timeslines progress to the frame to go to. In this instance, there are 300 frames, thus the multiplication by the number of frames in the animation.

    Here's the revised code:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.5.9/lottie.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/animation.gsap.js"></script>
    
    <script>
      // init scrollmagic
      var controller = new ScrollMagic.Controller();
    
      // Manage Animation
      var animation = bodymovin.loadAnimation({
        container: document.getElementById('lottie'), // Required
        path: '/static/animations/scroll_animation.json', // Required
        renderer: 'svg', // Required
        loop: false, // Optional
        autoplay: false, // Optional
        name: "Welcome to Awesomeness",
      });
    
      // Setup Timeline
      var tl = new TimelineMax();
      tl.to({frame:0}, 1, {
        frame: animation.totalFrames-1,
        onUpdate:function(){
          animation.goToAndStop((Math.round(this.progress() * 300)), true)
        },
        ease: Linear.easeNone
      })
    
    
      // Attach to scroll
      var lottieScene = new ScrollMagic.Scene({
            duration: '100%',
            offset: 1
        })
        .setPin("#header-scroll")
        .setTween(tl)
        .addTo(controller);
    
    </script>