Jump to content
Search Community

Using ScrollSmoother with Nuxt

OSUblake test
Moderator Tag

Recommended Posts

For those looking to integrate ScrollSmoother with Nuxt, here's a solution that builds off some of the work by @Born05 in this thread.

 

CodeSandbox:

https://codesandbox.io/s/gsap-scrollsmoother-nuxt-pbhmeh?file=/layouts/default.vue

 

All you have to do is include the GSAPScrollSmoother and put whatever content you want inside of this.

 

<GSAPScrollSmoother :vars="{ smooth: 2, effects: true }">
  <Nuxt />
</GSAPScrollSmoother>

 

You'll be able to access the ScrollSmoother in any page/component using this.$scrollSmoother, and can use any of the methods available, for example.

this.$scrollSmoother.paused(true);
let velocity = this.$scrollSmoother.getVelocity();

 

There are also a couple of extra methods available.

 

$scrollSmoother.parseEffects()

Call this to get ScrollTrigger to parse and create effects that are declared with data attributes, (data-speed and data-lag). 

 

$scrollSmoother.killEffects()

Call this to kill all the ScrollSmoother effects. You will typically need to call this when navigating to a new page.

 

$scrollSmoother.refresh()

Refresh the ScrollSmoother and all ScrollTriggers. You should call this when navigating to a new page if you don't call .parseEffects()

 

There are really only 2 files you need to be concerned about. The GSAPScrollSmoother.vue component file, and the nuxt.config.js file. 

 

The nuxt.config.js has some pageTransition callbacks in there that you may need to adjust to your project. And if you plan on using this in your own project, be sure to change all the gsap-trial imports to gsap, otherwise you won't be able to deploy it. 

 

Example usage on a page...

export default {
  mounted() {
    // if you don't have any effects, use this.$scrollSmoother.refresh() instead
    this.$scrollSmoother.parseEffects();
    
    this.myAnimation = gsap.timeline({
      scrollTrigger: {
        ...
      }
    })
  },
  beforeDestroy() {
    // kill any ScrollTriggers you created
    this.myAnimation.scrollTrigger.kill();
    
    // kill the effects that were created
    this.$scrollSmoother.killEffects();
  }
}

 

 

 

  • Like 7
Link to comment
Share on other sites

  • 8 months later...

Hello!

Thanks for the great solution, it has saved me a lot of time.  Everything worked perfectly until I created multiple layouts,  it took me a while until i got a positive result.  At the end i recreated the ScrollSmoother on the layout change in every layout .

 

  mounted() {
    this.$scrollSmoother.create();
  },
  beforeDestroy() {
    this.$scrollSmoother.kill();
  },

https://codesandbox.io/s/sad-bhaskara-tl1ds2

 

If there is a better way, please correct me, if not i hope it will save some time for others.

 

 

 

Link to comment
Share on other sites

Hi @Fisher666,

 

Indeed re-creating the Smoother instance on every route is a good way to have everything working as expected. I'll create an example that uses a layout  for the pages (a common scenario in Nuxt needless to say) to do that.

 

Is worth noticing that your example uses the ScrollSmoother wrapper created by @OSUblake for Nuxt2. Right now we're moving towards Nuxt3, so the setup I'll create will use that particular version with the Composition API as well but it won't create and use a ScrollSmoother wrapper.

 

Please stand by for that example.

 

Happy Tweening!

 

  • Like 1
Link to comment
Share on other sites

  • 1 month later...

@Rodrigo any update on this?

 

I'm currently using Nuxt3 and I'm wondering if anyone has come up with a better/smarter approach than me, creating and destroying the ScrollSmoother using client-side life-cycle hooks. Having a lot of pages, my approach works, but gets very verbose and does not feel right.

Link to comment
Share on other sites

Hi @bennyzen and welcome to the GreenSock forums!

 

First thanks for being a Club GreenSock member and supporting GreenSock!

 

Unfortunately no update yet. I'll try to get something working this week using the layout approach, but in the mean time killing/creating the ScrollSmoother instance using life cycle hooks should be good enough. 

4 hours ago, bennyzen said:

Having a lot of pages, my approach works, but gets very verbose and does not feel right.

Yeah, it gets repetitive to create the ScrollSmoother instance on each page by hand, but in no way I'd get the feeling that is not right. Repetitive maybe, but not right? It might not look elegant but that's another thing. In the mean time you could extract everything into a single method and import it on your pages:

// helpers/createSmoother.js
const createSmoother = (config) => {
  const smoother = ScrollSmoother.create({
    smooth: 1,
    effects: true,
    ...config // add extra config if you use different ones for each page
  });
};

Then in your pages:

import createSmoother from "@/helpers/createSmoother";

let ctx, smoother;

onMounted(() => {
  smoother = createSmoother();
});

onUnmounted(() => {
  ctx.revert();
});

That should take some of the repetitiveness away for a while ;) 

 

Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

@Rodrigo many thanks for your quick reply. And yes, your approach is more elegant than mine. Allowing to pass in some more config options on a per page basis with the spread is a cool idea. Borrowed!

 

I just put the helper method into the plugin file itself ~/plugins/gsap.client.js

import { gsap } from 'gsap'
import { ScrollToPlugin } from 'gsap/ScrollToPlugin'
import ScrollTrigger from 'gsap/ScrollTrigger'
import ScrollSmoother from 'gsap/ScrollSmoother'
// import { Draggable } from 'gsap/Draggable'
// import GSDevTools from 'gsap/GSDevTools'
// import TextPlugin from 'gsap/TextPlugin'
// import SplitText from 'gsap/SplitText'

gsap.registerPlugin(
  ScrollToPlugin,
  // Draggable,
  // GSDevTools,
  ScrollSmoother,
  // TextPlugin,
  // SplitText,
  ScrollTrigger
)

const initScrollSmoother = () => {
  const sm = ScrollSmoother.create({
    effects: true,
    overshoot: 0.1,
    smooth: 1,
    smoothTouch: 0.1,
    speed: 2.0,
  })
  return sm
}

export default defineNuxtPlugin(() => {
  return {
    provide: {
      gsap,
      ScrollToPlugin,
      ScrollTrigger,
      ScrollSmoother,
      initScrollSmoother,
      // Draggable,
      // GSDevTools,
      // SplitText,
    },
  }
})

and used something like

const { $initScrollSmoother, $gsap, $ScrollTrigger } = useNuxtApp()

let sm
onMounted(() => {
  sm = $initScrollSmoother()
})
onUnmounted(() => {
  sm ? sm.kill() : null
})

on every page where it is needed.

 

One thing that I'm still struggling with is when triggering the navigation using the router directly (per code), the instance does not seem to get killed reliably. But I need to further investigate it or just add some more life-cycle hooks to make sure it really gets killed.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

  • 3 months later...

Hi all.

 

Thank you for this post. I did a small plugin that works for me with Nuxt 3, I use this module for loading gsap: @hypernym/nuxt-gsap

 

export default defineNuxtPlugin((nuxtApp) => {
	
	let scrollSmoother = null
	
	const initScrollSmoother = () => {
		scrollSmoother = nuxtApp.$ScrollSmoother.create({
			smooth: 1,               
			effects: true,           
			smoothTouch: 0.1,    
		});
	}
	
	initScrollSmoother()
		
	nuxtApp.hook('page:transition:finish', () => {
		scrollSmoother.kill()
		initScrollSmoother()
	})
})

The plugin is client side only (smoothScroll.client.js).

 

Hope this helps :)

 

  • Thanks 2
Link to comment
Share on other sites

  • 6 months later...

Hey,


Maybe the question is a bit naive - sorry, I'm brand new to gsap on nuxt.


I have used the component exactly as described, but since then I get the following error message and nuxt is no longer usable:

__vite_ssr_import_0__.default is not a constructor


Does anyone have a guess as to what went wrong here?  I am using Nuxt 3.

Link to comment
Share on other sites

@hobbgemma welcome!

It's pretty tough to troubleshoot without a minimal demo - Would you please provide a very simple CodePen or Stackblitz that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. 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 you can fork.

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

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

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