Jump to content
Search Community

GSAP scrollTrigger animation breaks in Vue Radix Tabs Upon Tab Switch

jandorn test
Moderator Tag

Recommended Posts

I've got a scrollTrigger animation working that breaks as soon as I switch the tabs inside my tabs vue component. That component is based on the vue port of the UI library shadcn ui that is built on top of radix ui.

The project is built using Astro and I use the Astro island architecture to make certain (vue) components interactive.

 

Here is the stackblitz I created that shows the issue:

https://stackblitz.com/edit/gasp-problem?file=src%2Fanimations%2FscrollTrigger%2Fpinning.astro

 

The 4 relevant files are:

  • @/pages/index.astro (importing Card component and animation)
  • @/components/AnimationCard.vue (building card using shadcn ui, receives animation through a slot element)
  • @/animations/scrollTrigger/pinning.astro and @/animations/scrollTrigger/pinning.vue (where the GSAP animation is defined)

In this example there are shown 2 different Card components. One that implements the GSAP animation within an astro component and one within a vue component. Both work just perfectly fine upon initial page load but both break differently when I change to the code tab within the card and back to the animation. It's very odd that it doesn't even break conistently. The astro animation would just not play at all and freeze. The vue animation seems to get the  start and end state of the animation right but the animation itself is all over the place. In very few instances it even seemed to work fine after changing tabs.

 

I can't figure out where exactly the problem lies and I've tried a couple of different approaches that didn't work.

I've tried resetting the animation upon tab change using the watchEffect callback function and also setting clearProps to "all" (not entirely sure what that does exactly as I couldn't find proper docs for that). clearProps made the animation reappear but it's still broken.

 

I would be really grateful if someone could give me ideas about what the issue could be so I know where to look at. I'm new to gsap and am struggling to figure this one out.

Thanks a lot

Jan

Link to comment
Share on other sites

Hi @jandorn and welcome to the GSAP forums!

 

Thanks for being a GSAP Club member and supporting GSAP!

 

I have zero experience with Astro so I couldn't really tell you what the issue is here with a lot of certainty.

 

I can see a couple of odd things though. When I scroll into the second section I can see the elements of the first section animating as well:

https://i.imgur.com/ItSxEPw.mp4

 

As you can see the elements in the first section start to disappear and then re-appear as the second section's ScrollTrigger is running.

 

In your ScrollTrigger config, on both files you have this:

gsap.from('.rect', {
  clearProps: 'all',
  scale: 4,
  opacity: 0,
  transformOrigin: 'center center',
  scrollTrigger: {
    trigger: '.container',
    start: 'top 45%',
    end: '+=300',
    scrub: true,
  },
  stagger: 0.1,
});

That basically is targeting all the elements with that class in the DOM, which includes both sections.

 

Then I can't seem to find where exactly the code for the tabs is. Most likely those elements are being unmounted and then mounted again. If that is the case, the GSAP instance (with the ScrollTrigger configuration) is pointing to a group of elements that were removed from the DOM, so when you go back to the animation tab the GSAP instance is running again the elements are a completely different set. Another option is that the instance is running again and the elements opacity is already 0 and the from() instance is taking them from opacity: 0 to their current state, which also is opacity: 0 (among other properties), so basically GSAP is tweening the opacity value between 0 and 0, so nothing happens.

 

Normally in frameworks like Vue, we rely on the lifecycle hooks in order to revert things in order to prevent keeping instances that points to DOM elements that are no longer present in the DOM. IDK if Astro actually offser that type of hooks (my guess is that they do, but I couldn't really tell) so you should take advantage of those (at least in Vue) to properly revert your GSAP instances. For that GSAP Context is the best thing you can use:

https://gsap.com/docs/v3/GSAP/gsap.context()

 

With GSAP Context you can do this:

import { onMounted, onUnmounted, nextTick } from "vue";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

let ctx;

onMounted(() => {
  nextTick(() => {
    ctx = gsap.context(() => {
      gsap.from(".rect", {...});
    });
  });
});

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

Also you can setup a scope in order to target the elements with the rect class that reside inside a specific element and not the entire DOM:

ctx = gsap.context(() => {
  // This will get the elements with the class
  // inside the element with the first-container class
  gsap..from(".rect", {...});
}, ".first-container");

That is pretty much like doing this:

const container = document.querySelector(".first-container");
const rects = gsap.utils.toArray(".rect");

 

Finally if you can't seem to make this work, I'd look into the tabs package API you have implemented in order to use some sort of hook to cleanup, revert and then create the ScrollTrigger instances again in order to create your instances with the elements added to the DOM.

 

Hopefully this helps.

Happy Tweening!

  • Like 1
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...