Jump to content
Search Community

GSAP ScrollTrigger with Nuxt 3 not working as expected

Skrypt test
Moderator Tag

Recommended Posts

Hi everyone,

I'm working on my first Nuxt 3 website with GSAP and ScrollTrigger, however I can't get things to work as I want.

 

First of all, there's a minimal demo over here: https://codesandbox.io/s/skrypt-nuxt-dev-cvw0o (codesandbox instead of CodePen, so everything is fully loaded).

 

The problem:

When you load the homepage, you can see the markers of the ScrollTriggers. These ScrollTriggers are initialized inside each component (./components/sections). The components are loaded dynamically on the website. When going to another page (e.g. /over-ons) and navigating back to the home page, you can see the Start marker has move to the top of the viewport. This causes the timeline in the second component to start playing while the ScrollTrigger shouldn't have fired yet. I know I have to kill the ScrollTriggers before the new components are being loaded, but none of the lifecycle hooks seem to work. I've tried this inside the pages/[...slug].vue and pages/index.vue files like this:

 

import { ScrollTrigger } from "gsap/ScrollTrigger"

export default {
  async beforeMount() {
    ScrollTrigger.getAll().forEach(ST => ST.kill())
    ScrollTrigger.refresh()
  },
}

 

I guess the problem has to do with Nuxt 3 or with my way of dynamically loading the components (they're first fetched from the CMS, then loaded inside Nuxt). The weird thing is, if I add a setTimeout() to the mounted() hooks to delay the GSAP code, the ScrollTriggers do work as expected. Anyone who can help me work around this issue?

Link to comment
Share on other sites

Hey there!

 

Dropping these links here in case they help.

If you don't manage to find the answer here maybe chat to the nuxt community about the right approach - it sounds like you'll get more benefit from someone with a deep understanding of Nuxt's lifecycle hooks than GSAP.
 

 

  • Like 1
Link to comment
Share on other sites

@Cassie thanks for your reply! Unfortunately, the links you dropped don't contain the solution. I've currently implemented a setTimeout() of 250ms and the issue seems to be gone. Also dropped the question in the Nuxt discord, maybe someone over there can point me in the right direction.

  • Like 2
Link to comment
Share on other sites

  • 7 months later...
On 11/30/2021 at 2:50 PM, Skrypt said:

@Cassie thanks for your reply! Unfortunately, the links you dropped don't contain the solution. I've currently implemented a setTimeout() of 250ms and the issue seems to be gone. Also dropped the question in the Nuxt discord, maybe someone over there can point me in the right direction.

Hey @Skrypt, can you share new solutions from Discord? Of course if you have it. :D

Link to comment
Share on other sites

  • 1 month later...

Hi @Skrypt — I've got an implementation working when navigating across pages — you have to declare the animation in the script setup, not bound to a ref, and then activate it on the onMounted hook, eg

 

 

<script setup>
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);

const animation = gsap.timeline({
  scrollTrigger: {
    start: "top top",
    end: "center top",
    scrub: 1,
  },
});

const logo = ref(null);
onMounted(() => {
  animation.to(logo.value, { yPercent: -100, opacity: 0, duration: 1 });
});
</script>

 

  • Like 1
Link to comment
Share on other sites

  • 3 months later...

(I am a Japanese speaker, translated by deepl.)

 

We have confirmed a bug in the execution of ScrollTrigger when pageTransition is set.

 

My solution is one of the following.

 

1、Do not use pageTransition.

2、Delay the execution of the ScrollTrigger in onMounted longer than the time required for the pageTransition (use setTimeout).

3、Set page-key in the NuxtPage component in app.vue.

4、Set definePageMeta key in each page component.

 

-- postscript --

 

However, I found that changing the page-key using methods 3 and 4 causes another problem with the pageTransition.

I just found another solution!

 

5. create plugins and use Nuxt's 'page:transition:finish' hook.



image.thumb.png.ba6566eb6b3a715fe4e52f596a52eedc.png

 

 

Link to comment
Share on other sites

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

It sounds like maybe you're loading ScrollTrigger dynamically, so the normal "DOMContentLoaded" and "load" events aren't being dispatched by the browser, thus ScrollTrigger has no idea that it needs to refresh(). I'm totally guessing here. Have you tried running ScrollTrigger.refresh() when you know that the DOM/layout has settled? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

 

Here's a Nuxt starter template.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

  • 2 weeks later...

Thanks for your feedback. Yes, to see the error, all you have to do is click "reload" on the ScrollTrigger page in the results section. The LayerSection page has the problem too.

 

As you can see also on this screenshot, the script is not interpreted after the reload.

 

gsap-bug.png.95cb4425600c130663db12d33864db8f.png

Link to comment
Share on other sites

My goal is to use ScrollTrigger to animate elements on entering page. When I navigate to another page I want to animate the elements off to get a nice page transition. 

 

So, when I enable pageTransition in nuxt.config.ts in your GSAP & Nuxt 3 Starter and navigate through the pages, the scripts don't work anymore.

export default defineNuxtConfig({
  app: {
    pageTransition: { name: 'page', mode: 'out-in' }
  },
})

 

Do you have a solution for this or an alternative to get the desired behaviour?

Link to comment
Share on other sites

7 hours ago, svenilla said:

Great, thank you very much! What was the problem?

Just some small issues in the HTML that caused Vue to not render the styles properly until a route change.

 

In terms of that I'm working on a page transitions example using Nuxt3 so please stand by and as soon as it's live I'll get back to you on this thread.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Hi,

 

Here is the example for page transitions in Nuxt3:

https://stackblitz.com/edit/nuxt-starter-bthjlg

 

As you can see it follows the same pattern as this example in Vue3:

https://stackblitz.com/edit/vitejs-vite-w8wtpj

 

The only difference is that in Vue we have a top level file (App.vue) where we setup all our transition code for the entire app. In the case of Nuxt there is not such option as we have to configure it on each page using the definePageMeta global method. In order to not repeat the same code over and over I came up with the helper file that has all the configuration and that uses the transition composable, which keeps pages files small and nice, at least in that particular aspect. The transition purposely affects the position of the page container on the Y axis as well as it's scale, in order to require the animation to be completed to create the ScrollTrigger instances, so everyone can see that particular pattern. Of course if your transitions don't change any of that there shouldn't be needed, but just in case is a good idea to have it there so everyone can see the best possible approach.

 

Finally if someone has an idea to better improve this or any other example, we're all ears as we look forward from your inputs and ways to make things as good as possible.

 

Hopefully these are helpful and let us know if you have any comments, questions or issues.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

3 hours ago, svenilla said:

But as soon as I refresh the page Scrolltrigger or the page LayersSection, the tweening is no longer executed (The problem we've had before). This occurs in both versions.

Hey @svenilla, thanks for reporting (again 😆)!

5b74cac7a399b07482cea6aac889ca404252f407

 

So the issue is that the state is being updated only on the transition and not when the page is loaded for those routes. The solution is different in Vue and Nuxt, because Nuxt uses <suspense> for routing so when the app.vue file is mounted the state changes but the page/view component is not rendered yet, so when it renders the watcher doesn't trigger. Since Nuxt uses suspense, when the component renders the watcher will be ran again but in Vue that doesn't happen, that's why we have to add the immediate flag in the watch hook:

watch(
  [() => transitionState.transitionComplete, main],
  (newValue) => {
    if (newValue && main.value) {
      ctx.value = gsap.context((self) => {
        const boxes = self.selector(".box");
        boxes.forEach((box) => {
          gsap.to(box, {
            x: 150,
            scrollTrigger: {
              trigger: box,
              start: "bottom bottom",
              end: "top 20%",
              scrub: true,
            },
          });
        });
      }, main.value); // <- Scope!
    }
  },
  {
    immediate: true,
  }
);

That forces the execution of the watcher hook when the component is created. That brings another issue though that the ref is not yet available, so we have to watch that as well.

 

Hopefully this works as expected.

 

Happy Tweening!

  • Thanks 1
Link to comment
Share on other sites

Thank you very much @Rodrigo, now the reloading works. 

However, when I navigate forwards or backwards via the browser, the progress of the animation and the scroll position are always reset, which is bad for usability. What would you have to change if you want to keep the status and scroll position when changing pages.

Link to comment
Share on other sites

Hi,

 

Yeah it should be possible using scroll restoration:

https://developer.mozilla.org/en-US/docs/Web/API/History/scrollRestoration

 

Or perhaps local storage to keep track of it:

https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage

https://developer.mozilla.org/en-US/docs/Web/API/Storage

 

Or perhaps tinker with  Nuxt Router's configuration:

https://github.com/nuxt/nuxt.js/issues/3471

 

Although I don't know how feasible the last option is in Nuxt3, you'll have to check the docs and ask in their discord channel:

https://discord.com/channels/473401852243869706/

 

Unfortunately I haven't had time to circle back to this yet, so hopefully you'll be able to make some progress.

 

Let us know how it goes and if you have any questions.

 

Happy Tweening!

  • Thanks 1
Link to comment
Share on other sites

  • 1 month later...

Hey @Rodrigo,

First of all I want to thank you for sharing an example how to use GSAP with Nuxt 3. Just as Svenilla I'm trying to use Scroll Trigger, but seem to have mount / unmount issues when page transition is enabled.

I've tried to use the same set-up as the example project above and I get a different issue, but perhaps they are related to what I am having now in my own project. When using page transitions (enabling it in the nuxt.config.ts) with same basic fade effect will result into an error and/or not playing the scroll animation:

 

URL: https://stackblitz.com/edit/nuxt-starter-mvowjp?file=pages%2Fscroll.vue,nuxt.config.ts


app.vue

<template>
  <Header />
  <NuxtPage />
</template>

<style>
.page-enter-active,
.page-leave-active {
  transition: all 0.2s;
}
.page-enter-from,
.page-leave-to {
  opacity: 0;
  filter: opacity(50%);
}
</style>

nuxt.config.ts

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  app: {
    pageTransition: { name: 'page', mode: 'out-in' },
  },
  css: ['@/assets/styles.css'],
  build: {
    transpile: ['gsap'],
  },
});

Also I noticed if you navigate really fast between the tabs it also breaks the animation and the website stops working.

Link to comment
Share on other sites

Hey @Alleen and welcome to the GreenSock forums!

 

I noticed a few things. First you're using CSS Transitions. Since I've been using GSAP for so many years I never ventured into using CSS for page transitions in Vue/Nuxt, so I can't give you much assistance with that aspect.

 

Also in your Scroll page you have several children at the root level of the template. While this is no problem for Vue3, the Transition component does requires only one children so that is causing an issue.

 

Also when navigating to the layers page, it seems that the ScrollTrigger instances are being created before the page is completely rendered. As I mentioned I've never used CSS for page transitions so this are somehow uncharted waters for me.

 

We built this starter template that uses GSAP for page transitions that has been tested and seems to work as expected:

https://stackblitz.com/edit/nuxt-starter-bthjlg?file=helpers%2FtransitionConfig.js

 

In that particular file you'll find the configuration for the page transitions that you can tinker with in order to use fade in/out in your project.

 

Hopefully this helps. Let us know if you have more questions.

Happy Tweening!

Link to comment
Share on other sites

Dear @Rodrigo,

Thank you for the tips and example. I have moved away from CSS animations and now the timing seems to be better, but while navigating through the pages it appears that the scroll position is not correctly calculated. It jumps or simply won't start the animation at all. I will try to create a simple version in StackBlitz out of the actual project I'm working on. Hopefully you'll have the time to help me out with this.

EDIT:
A sample example of what I think I'm experiencing in my project: https://stackblitz.com/edit/nuxt-starter-tx3skg?file=pages%2Fscroll.vue

When refreshing the page on the "Scroll Trigger" tab it works as expected, but when navigating from "Layers Section" back to "ScrollTrigger", you'll notice that the animation is not working properly anymore. In my project the translation jumps from `translate3d(-120px, 0px, 0px)` to `translate3d(2px, 0px, 0px)`.

 

Have an awesome evening!

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...