Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
2malH

Problem with killing and reinitialising ScrollTrigger after Single Page App page transition

Recommended Posts

Hi everyone,

 

first of all: Thanks so much for GSAP and all the help you guys are giving here in the forum! I'm blown away by all the possibilities although I just started using it. First time poster, I did my best to search for a similar problem but didn't find anything related, so here I am:

 

As for my problem with ScrollTrigger: If I go to the page that uses ScrollTrigger directly (https://polymorph.media/work/) everything's working fine. But If I navigate between the pages (e.g. go to home and go back or come from a different page) the markers of the elements to be triggered are moved way down.

I'm pretty sure the problem is related to the BarbaJS-like page transition the wordpress theme has implemented and that it doesn't calculate the position correctly. As for the transition there's not much I can change about it. So on the GSAP side: Is this something I can possibly fix with ScrollTrigger.refresh() or ScrollTrigger.update()? Sorry for not providing a codepen since this seems to be a theme related issue.

 

One further question: Is it possible to use ScrollTrigger to auto-hide and show the navbar like Headroom.js (https://wicky.nillia.ms/headroom.js/) does? I'd love to replicate this in GSAP so that there's less to worry about since I'm having the same transition problem as with ScrollTrigger..

 

Edit: Still building the website so I don't mind activating the markers so it visualizes my problem.

  • Like 1
Link to post
Share on other sites

That's expected behaviour and indeed is related to Barba.js.

 

When you first load a page, everything is there for ScrollTrigger to pick up. Meaning the DOM does have the elements you pass to ScrollTrigger.

When you navigate to a page using Barba.js, the DOM changes (the old container is removed and the new is added) and ScrollTrigger (or any other script), can't 'listen' the events of these newly added elements. You have to destroy the previous instance of ScrollTrigger and reinitialise it, passing it the new elements. 

 

Barba gives you several hooks to work with (i believe the one you need is beforeEnter) so on that hook, you could use the next object, to find the new elements you want to pass to ScrollTrigger. Something like this:

 

beforeEnter: ({ next }) => {
  const items = next.container.querySelectorAll('whatever you want')
},

You can check the docs, they mention how to work with third party scripts. You need a similar logic with this.

 

Regarding your second question, i believe you don't need ScrollTrigger at all (or either headroom.js). You can use your own function to detect if the user scrolls up or down and then use gsap for the animation of the header. Be sure to hook your function in requestAnimationFrame or gsap.ticker for better performance on scroll. 

  • Like 4
Link to post
Share on other sites

  

Thanks a lot @Yannis Yannakopoulos. Your answer really helps as it confirms my assumption. I have now found the events the theme dispatches before and after the transitions (it's not barba js but gsap they're using btw) and I'm listening for transitionOutStart (here I want to kill ScrollTrigger and transitionsDone (here I'll have to initialize ScrollTrigger if used on that page). But I don't really know on how to use ScrollTrigger.kill() as it's not orking as expected.

 

This is how I init ScrollTrigger:

gsap.registerPlugin(ScrollTrigger);

let tl = [];
let projects = ['omrfestival', 'mini', 'porsche', 'boa', 'dinnerberlin'];

for (i = 0; i < projects.length; i++) {
  let tl = gsap.timeline({
    scrollTrigger: {
      trigger: ".portfolio--" + projects[i],
      start: "top 70%",
      end: "top 55%",
    }
  });

  tl.addLabel("animateTitle")
    .fromTo(".portfolio--"+projects[i]+" .portfolio__heading", {opacity: 0, x: -50}, {opacity: 1, x: 0, duration: 0.8})
    .addLabel("animatePlane")
    .fromTo(".portfolio--"+projects[i]+"  .portfolio-img__plane", {opacity: 0, scaleX: 0, transformOrigin:'0% 0%', ease: "expo. out"}, {opacity: 1, scaleX: 1, duration: 0.5}, "-=0.3")
    .addLabel("animateImage")
    .fromTo(".portfolio--"+projects[i]+"  .portfolio-img__image", {opacity: 0, x: -10}, {opacity: 1, x: 0, duration: 0.6}, "-=0.2")
    .addLabel("animateDesc")
    .fromTo(".portfolio--"+projects[i]+"  .portfolio__desc", {opacity: 0, x: -10}, {opacity: 1, x: 0, duration: 0.6}, "-=0.2")
    .addLabel("animateCTA")
    .fromTo(".portfolio--"+projects[i]+"  .portfolio__cta", {opacity: 0, x: -10}, {opacity: 1, x: 0, duration: 0.6}, "-=0.2");
}

 

And here's the eventListener

(function($) {
  function exitPage() {
    console.log("sempliceTransitionOutStart");
    if (tl !== undefined) {
      tl.kill();
      console.log("kill ScrollTrigger");
    }
  }
  function transitionsDone() {
    console.log("sempliceTransitionsDone");
  }

  window.addEventListener("sempliceTransitionOutStart", exitPage );
  window.addEventListener("sempliceTransitionsDone", transitionsDone );
})(jQuery);

 

In the console it show me that the kill section is beeing called but it doesn't destroy it as expected. I've tried both: tl.kill() and ScrollTrigger.kill(). None of them is working. What am I doing wrong?

 

Thanks so much for the help! :)

 

PS: I know that the code above can be optimized. I'm just strating to get into JS. I think utils.toArray() is what I'll have to look into next, right? :)

Link to post
Share on other sites

Welcome to the GreenSock forums!

 

On 6/7/2020 at 4:40 AM, 2malH said:

Is it possible to use ScrollTrigger to auto-hide and show the navbar like Headroom.js (https://wicky.nillia.ms/headroom.js/) does? I'd love to replicate this in GSAP so that there's less to worry about since I'm having the same transition problem as with ScrollTrigger..

Definitely! It's quite straightforward:

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

 

6 hours ago, 2malH said:

In the console it show me that the kill section is beeing called but it doesn't destroy it as expected. I've tried both: tl.kill() and ScrollTrigger.kill(). None of them is working. What am I doing wrong?

What do you mean "doesn't destroy it as expected"? Can you please create a minimal version of the demo?

 

6 hours ago, 2malH said:

PS: I know that the code above can be optimized. I'm just strating to get into JS. I think utils.toArray() is what I'll have to look into next, right? :)

Perhaps my article on animating efficiently can be of use. It talks about how to use functions, methods, and control functions to write animation code more efficiently. 

  • Like 4
Link to post
Share on other sites

@2malH i also don't get the doesn't destroy it as expected part. However to give you a clue on what's happening and maybe you can work it out, the selectors you're using (.portfolio__heading, .portfolio-img__plane) need to be reselected  after each page transition. 


What i'd suggest is wrap the whole logic of initialising your timeline & ScrollTrigger,  in a function, or a Class which, would have some init & destroy methods. On init you could pass as arguments the elements you want to animate and on the according event (when the new DOM is added on the page) you could re-run init to grab the new elements added on the page and pass them to gsap (after destroying the previous instance).

 

That being said and given the fact that you use Semplice, maybe it's a whole lot easier for you. Checked their docs to find the relevant events, but i came up with this:  https://help.semplice.com/hc/en-us/articles/360035699552-Single-Page-App-Behavior. So there is an option to run some custom code either once or after each page transition. So maybe you could try adding your code with that option enabled (after each page transition) and see if it works.  

 

 

  • Like 3
Link to post
Share on other sites

Like the others, I wasn't quite sure what you meant so it'd be great to see a reduced test case but I did want to mention a few things:

  1. If you kill() an animation that created a ScrollTrigger, that does NOT automatically kill the ScrollTrigger instance too because those are distinct. That was intentional. A user could create a ScrollTrigger instance that has various callbacks and ALSO associate an animation with that ScrollTrigger, but they may still want the scroll-related callbacks to fire despite the animation being killed. If you want to kill the ScrollTrigger instance, you have a few options: 
    let animation = gsap.to(... {
      scrollTrigger: {
        ...
        id: "my-id"
      }
    });
    
    // either use the ID:
    ScrollTrigger.getById("my-id").kill();
    
    //or the animation's scrollTrigger property:
    animation.scrollTrigger.kill();

     

  2. I wonder if you were literally calling the undocumented ScrollTrigger.kill() (the static method) instead of the instance's kill() method. The static ScrollTrigger.kill() method literally kills ScrollTrigger completely, so it won't ever work again on that page. It was undocumented intentionally - it's more for our own troubleshooting purposes. So make sure you're calling kill() on the actual ScrollTrigger instance that you want to kill (as shown above). 
  • Like 4
Link to post
Share on other sites

@GreenSock point 1 makes perfect sense.

 

Question: is there a best practice you'd suggest for handing multiple instances on the same page? Performance wise, could we reach a pitfall? 

Link to post
Share on other sites
13 minutes ago, Yannis Yannakopoulos said:

@GreenSock point 1 makes perfect sense.

 

Question: is there a best practice you'd suggest for handing multiple instances on the same page? Performance wise, could we reach a pitfall? 

Not that I can think of. ScrollTrigger is highly optimized for speed and it does NOT constantly check bounds using something like getClientBoundingRect() which would be expensive. The refresh() which happens on page load, on resize, and when a tab become active/visible again, is where those "expensive" operations are, and then it merely remembers the scroll position values where each start/end is mapped to and ONLY checks those against the current scroll position. It uses a central "scroll" event listener, and it even throttles resize events so that it waits until there's about 0.2 seconds of breathing room between resize events before it actually does its refresh() calculations again. 

 

Basically, it should be pretty dang fast and I can't think of a big bottleneck. Of course if you create 10,000 ScrollTrigger instances on a page, that's gonna require a fair amount of processing :)

  • Like 4
Link to post
Share on other sites

@GreenSock you're exactly right with point 2. I was literally just calling ScrollTrigger.kill() in hope to destroy the animation instance I've set before. So that's indeed a great starting point for me.

 

@Yannis Yannakopoulos thanks so much for even digging into the Semplice documentation. I know about the SPA script field and I'm using it for a couple of things already. It's basically listening for the event "sempliceAppendContent" and then calls a function containing the script code I can insert in the admin area. But I'd like to code everything locally into a .js file and embedd it. Makes changing things and testing faster. That being said, you're absolutely right I should create a function or class to control the animations. Since I'm quite a novice, I'll have to read some more on how to accomplish this. @ZachSaucier's article is great especially tip #8 seems to be what I'll have to implement.

 

While setting this up I'm running into some other difficulties you might be able to help me with: In the head section after including ScrollTrigger.min.js via CDN I'm including my customJS file which calls

gsap.registerPlugin(ScrollTrigger);
var tl = gsap.timeline();

function initScrollTrigger() {
  if(tl.ScrollTrigger) {
    tl.ScrollTrigger.kill();
  }
  tl = gsap.fromTo(...);
  ...
}
  

initScrollTrigger();
  
window.addEventListener(
  "sempliceTransitionOutStart",
  function (e) {
    initScrollTrigger();
  },
  false
);

Is this somewhat the right way? Because now it throws me the error "Please gsap.registerPlugin(ScrollTrigger)" which is actually the very first thing I do in the file. I'm kinda lost on how to integrate the whole ScrollTrigger part in an external JS.

 

Edit: Again: I' sorry for not providing a codepen but it's this problem seems to be deeply connected to a lot things, I'm finding it hard to replicate it in a minimal version.

Link to post
Share on other sites
2 minutes ago, 2malH said:

it throws me the error "Please gsap.registerPlugin(ScrollTrigger)" which is actually the very first thing I do in the file.

Sounds like the ScrollTrigger file isn't loading. You can check in the network panel of your dev tools.

 

3 minutes ago, 2malH said:

I'm kinda lost on how to integrate the whole ScrollTrigger part in an external JS.

We have helpful install videos that can help you getting things up and running: greensock.com/install

  • Like 2
Link to post
Share on other sites

@2malH can you also please verify that you're using version 3.3.1?

Link to post
Share on other sites

@GreenSock, @ZachSaucier

I thought that I'd have followed the installation instructions just right but it's not working. :-/

 

This is how I'm loading GSAP.

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/gsap.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/CSSRulePlugin.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollToPlugin.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollTrigger.min.js"></script>
  <script src="https://polymorph.media/wp-content/themes/polymorph/assets/js/polymorph.min.js"></script>
<!-- I've also tried to register the plugin right away in the head section but it didn't remove the errors
<script>gsap.registerPlugin(ScrollTrigger);</script>
-->
</head>

 

polymorph.min.js - this is where I'd like to place the GSAP related code and register the plugin.

gsap.registerPlugin(ScrollTrigger);
var tl = gsap.timeline();


function initScrollTrigger() {
  console.log("initScrollTrigger");

  if(tl.ScrollTrigger) {
    tl.ScrollTrigger.kill();
  }

  tl = gsap.timeline({
    scrollTrigger: {
    trigger: ".portfolio--omrfestival",
    start: "top 70%",
    end: "top 55%",
    markers: true,
    }
  });

  tl.addLabel("animateTitle").fromTo(".portfolio--omrfestival .portfolio__heading", {opacity: 0, x: -50}, {opacity: 1, x: 0, duration: 0.8});

}

initScrollTrigger();

window.addEventListener(
  "sempliceTransitionsDone",
  function (e) {
    initScrollTrigger();
  },
  false
);

 

These are the errors it throws

Please gsap.registerPlugin(ScrollTrigger) ScrollTrigger.min.js:10:13807
Uncaught TypeError: can't access property "indexOf", i is undefined
    C https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollTrigger.min.js:10
    init https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollTrigger.min.js:10
    ScrollTrigger https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollTrigger.min.js:10
    create https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/ScrollTrigger.min.js:10
    Aa https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/gsap.min.js:10
    Timeline https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/gsap.min.js:10
    timeline https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.1/gsap.min.js:10
    initScrollTrigger https://polymorph.media/wp-content/themes/polymorph/assets/js/polymorph.min.js:12
    <anonymous> https://polymorph.media/wp-content/themes/polymorph/assets/js/polymorph.min.js:52

 

Edit: Added the comment that I've also tried to register the plugin right within the head section without success

Link to post
Share on other sites
  • 2malH changed the title to Problem with killing and reinitialising ScrollTrigger after Single Page App page transition

It's almost impossible to troubleshoot blind like this - can you please provide a reduced test case in CodePen? I'd love to help identify the problem, but my hands are tied right now. 

 

One shot in the dark - I bet you've got your code running BEFORE the <body> tag. That'd mean your DOM isn't even available yet, so please make sure you're putting the GSAP-related code at the BOTTOM of the <body> (or just below it). 

  • Like 2
Link to post
Share on other sites

@GreenSock you've nailed it, that was it. 🤦‍♂️ Thanks a lot. I'll try to take it from here. I even managed to kill the ScrollTriggers I intended to. So thanks a lot for that!

 

One last question:

Given this site structure with multiple pages and multiple containers each holding different elements I want to animate. What's the best practice to set this up? Should I setup a timeline for each container or one for each page?

 

page1
  container--1
    container__title
    container__image
    container__text
  container--2
    container__title
    container__image
    container__text
  container--3
    container__title
    container__image
    container__text

page2
  container--a
    container__title
    container__image
    container__text
  container--b
    container__title
    container__image
    container__text
  container--c
    container__title
    container__image
    container__text

 

Link to post
Share on other sites
2 hours ago, 2malH said:

Should I setup a timeline for each container or one for each page?

It depends on your needs. Usually it's best to do per section but depending on the effect you need it might make sense to use one big timeline.

 

Again, I recommend my animating efficiently article

  • Like 1
Link to post
Share on other sites

Also, you had a typo in your code - you used an uppercase "S" instead of lowercase: 

// BAD
tl.ScrollTrigger.kill();

// GOOD
tl.scrollTrigger.kill();

 

  • Like 2
Link to post
Share on other sites
  • 3 weeks later...

Hi there , I'm definitely having the same issue and thank god I found this forum because I was thinking i was going insane trying to find the bug. So basically as

On 6/7/2020 at 2:09 PM, Yannis Yannakopoulos said:

When you first load a page, everything is there for ScrollTrigger to pick up. Meaning the DOM does have the elements you pass to ScrollTrigger.

When you navigate to a page using Barba.js, the DOM changes (the old container is removed and the new is added) and ScrollTrigger (or any other script), can't 'listen' the events of these newly added elements. You have to destroy the previous instance of ScrollTrigger and reinitialise it, passing it the new elements. 

I'm guessing this is why my ScrollTrigger is not working as expected with BarbaJS as I navigate from the homepage to about page on my portfolio site everything else works as expected but sometimes the markers are out of their places . Which is even more weird is that sometimes the behaviour is actually perfect when viewed on mobile and then other times not on mobile but i'm guessing it has something to do with the Barba Removing of my elements so basically what you advise me to do is make reference variables to the elements I want to animate with GSAP and then call a function in beforeAfter () hook to redeclare those variables to new elements that Barba has swapped in and then pass them to the gsap animations. Or what do ye( @GreenSock @Yannis Yannakopoulos, @2malH) mean kill the scrollTrigger instance , why do I need to do that , is that just an easier way of doing what I've just implied above??

 

homepage.js

//Variable Declarations and Function Definitions 
let viewBox = ""
heading_Pos = [0, 0]
displayState = ""
hamburger_display_button = Array.from($('.mobile_nav_sticky'))[0]
opened_nav_buttons = document.querySelector('.options')
logo = $(".Actual_Logo_Svg")
// Morphing Circles and ellipses to paths to be able to morph them and checking the viewbox for device size
MorphSVGPlugin.convertToPath("ellipse");
shapes = Array.from($('.Logo_In_Shapes path'))

const homeInit = () => {
    viewBox = "",
        heading_Pos = [0, 0],
        displayState = ""
    hamburger_display_button = Array.from($('.mobile_nav_sticky'))[0]
    opened_nav_buttons = document.querySelector('.options')
    logo = $(".Actual_Logo_Svg");
    // Morphing Circles and ellipses to paths to be able to morph them and checking the viewbox for device size
    MorphSVGPlugin.convertToPath("ellipse");
    shapes = Array.from($('.Logo_In_Shapes path'))
}

const logo_tl_func = () => {
    let logo_tl = gsap.timeline({
        onComplete: moveLogo,
    })
    // Morphing into the Logo
    logo_tl.from(shapes, 1, {
        y: -600,
        autoAlpha: 0,
        ease: "bounce",
        stagger: 0.15
    })
    logo_tl.to(shapes, 1, {
        fill: '#F0C368',
        stagger: 0.05
    })
    let firstAnimation = gsap.to('.shapes', {
        duration: 2,
        morphSVG: ".Logo_Proper_Background"
    });
    let secondAnimation = gsap.to('.textShape', {
        duration: 2,
        fill: '#1D373F',
        morphSVG: ".Logo_Proper_Text"
    });
    logo_tl.add([firstAnimation, secondAnimation])
}

const changeViewBox = media_query => {
    media_query.matches ? viewBox = "-150 -180 2495 890" : viewBox = "-150 -350 3574 880"
    media_query.matches ? heading_Pos = [-511, -15] : heading_Pos = [-1540, 40];
    media_query.matches ? displayState = "none" : displayState = "block"
}

const moveLogo = () => {
    gsap.to(logo, {
        attr: { viewBox: viewBox },
        duration: 3
    })
    fadeInHeadingAndLinks();
}

const fadeInHeadingAndLinks = () => {
    gsap.to('.nav_links', {
        display: displayState,
        scale: 1,
        duration: 3
    })
    gsap.to('.logo_heading', {
        display: "block",
        x: heading_Pos[0],
        y: heading_Pos[1],
        // scale:1,
        duration: 3
    })
    gsap.to('.mobile_nav_sticky', {
        display: "block",
        scale: 1,
        duration: 5
    }, "+=.7")
}

const pageTransition = () => {
    var tl = gsap.timeline();
    tl.set('.loading_container img', {
        scale: 0.3
    })
    tl.to('.loading_container', {
        duration: 1.2,
        width: "100%",
        left: "0%",
        ease: "circ.out",
    })
        .to('.loading_container img', {
            scale: 0.6,
            duration: 1
        }, "-=1.2")

        .to('.loading_container', {
            duration: 1.2,
            width: "0%",
            right: "0%",
            ease: "circ.out",
        })
        .to('.loading_container img', {
            scale: 0.3,
            duration: 1.2
        }, "-=1.3")
}

// Helper Functions

const delay = (ms) => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// Initialization Methods
$(document).ready(() => {
    window.matchMedia("(max-width: 600px)").matches ? logo.attr('viewBox', '-350 -700 1274 1680') : logo.attr('viewBox', '-680 -380 2074 1080')
    var viewbox = window.matchMedia("(max-width: 600px)")
    changeViewBox(viewbox)
})

hamburger_display_button.onclick = () => {
    opened_nav_buttons.classList.toggle('open')
}

barba.init({
    sync: true,
    transitions: [{
        name: 'transition-base',
        preventRunning: true,
        timeout: 5000,
        async leave() {
            const done = this.async();
            pageTransition();
            await delay(1000);
            done();
        },
        async enter() {
            window.scrollTo(0, 0);
        },
    }],
    views: [
        {
            namespace: 'home',
            afterEnter() {
                homeInit()
                window.matchMedia("(max-width: 600px)").matches ? logo.attr('viewBox', '-350 -700 1274 1680') : logo.attr('viewBox', '-680 -380 2074 1080')
                let viewbox = window.matchMedia("(max-width: 600px)")
                changeViewBox(viewbox)
                logo_tl_func();
                hamburger_display_button.onclick = () => {
                    opened_nav_buttons.classList.toggle('open')
                }
            },
        },
        {
            namespace: 'about',
            afterEnter() {
                aboutInit()
                face_tl_func()
                scroll_p_tl_func()
                scroll_skills_tl_func()
                scroll_facts_tl_func()
            },
        }
    ],
});

// //Global Hooks 
// barba.hooks.leave(() => {
//     const done = this.async();
//     pageTransition();
//     await delay(1000);
//     done();
// })
// barba.hooks.enter(() => {
//     window.scrollTo(0, 0);
// })

 

about.js

// Variable Declarations and Function Definitions
let factsContainer_sm = document.querySelector(".factsContainer_sm")

const aboutInit =() => {
    factsContainer_sm = document.querySelector(".factsContainer_sm")
    let head = document.getElementsByTagName('head')[0],
    link = document.createElement('link')
    link.rel = 'stylesheet'
    link.href= "../../Resources/CSS/about.css"
    head.appendChild(link);
}

const face_tl_func = () => {
    let face_tl = gsap.timeline(),
        paths = document.querySelectorAll('.My_Face path'),
        filledYellowElements = ['.Main_Hair_Part', '.Eyeball_2', '.Eyeball_1', '.Nostril_1', '.Nostril_2', '.Tongue_Part'],
        filledNavyElements = ['.Pupil_2', '.Pupil_1'];
    face_tl.set(filledNavyElements, { fill: 'unset' }),
        face_tl.set(filledYellowElements, { fill: 'unset' }),
        face_tl.fromTo(paths, { drawSVG: "0%" }, { duration: 1, drawSVG: "100% ", stagger: 0.15 })
    let firstAnimation = gsap.to(filledYellowElements, {
        duration: 2,
        ease: "slow",
        fill: '#F0C368'
    }, "-=.7"),
        secondAnimation = gsap.to(filledNavyElements, {
            duration: 2,
            ease: "bounce",
            fill: '#1D373F'
        }, "-=.7")
    face_tl.add([firstAnimation, secondAnimation])
}


const scroll_p_tl_func = () => {
    let scroll_tl = gsap.timeline({
        scrollTrigger: {
            trigger: '.content',
            start: "top center",
            end: "+=1000",
            markers: true,
            scrub: true
            // pin: true
        }
    })
    scroll_tl.to('.first', {
        transform: "rotateX(50deg) rotateZ(331deg) translateX(42px)",
        duration: .5,
    }),
        scroll_tl.to('.flag', {
            scale: 1
        }, '-=.1'),
        scroll_tl.addLabel("first_down")
    scroll_tl.to('.second', {
        transform: "rotateX(50deg) rotateZ(331deg) translateX(42px)",
        duration: 2,
    }, "first_down-=.1")
    scroll_tl.addLabel("second_down")
    scroll_tl.to('.third', {
        transform: "rotateX(50deg) rotateZ(331deg) translateX(42px)",
        duration: 2,
    }, "second_down-=.01")
}


const scroll_skills_tl_func = () => {
    let scroll_tl = gsap.timeline({
        scrollTrigger: {
            trigger: '.skillsContainer',
            start: "top center",
            markers: true,

        }
    }),
        barWidth = "",
        bars = [...document.querySelectorAll('.bar')]
    bars.map(bar => {
        barWidth = bar.dataset.width;
        let barAnimation = gsap.to(bar, {
            width: barWidth,
            duration: 1,
            delay : .2
        }),
            percentageAniamtion = gsap.to('.percentage', {
                scale: 1,
            })
        scroll_tl.add([barAnimation, percentageAniamtion]);
    })

}

const scroll_facts_tl_func = () => {
    let scroll_tl = gsap.timeline({
        scrollTrigger: {
            trigger: '.factsContainer',
            start: "top center",
            // pin: true,
            scrub: true,
            end: "+=300",
            markers: true,
        }
    }),
        facts = [...document.querySelectorAll('.fact')]
    scroll_tl.to('.factsContainer h2', {
        scale: 1.5,
        duration: 1,
        ease: "slow"
    })
    scroll_tl.to(facts, {
        xPercent: -85 * (facts.length - 1),
        scrollTrigger: {
            trigger: ".factsContainer_sm",
            start: "center center",
            pin: true,
            // pinSpacing:false,
            markers: true,
            scrub: 1,
            snap: 1 / (facts.length - 1),
            // base vertical scrolling on how wide the container is so it feels more natural.
            end: () => `+=${factsContainer_sm.offsetWidth}`
        }
    });
}


// //Initialization Methods
// aboutInit()
// face_tl_func()
// scroll_p_tl_func()
// scroll_skills_tl_func()
// scroll_facts_tl_func()

Here's my homepage - https://adamoceallaigh.netlify.app/

and here's my about page - https://adamoceallaigh.netlify.app/about.html

Link to post
Share on other sites

@adamoc Please stick to the thread that you created. We're pretty good about responding to things :) There's no point in cross-posting in other threads.

Link to post
Share on other sites

@adamoc that's a lot of code to digest easily. Maybe if you could provide a reduced case, i could understand better.

 

However, taking a quick look at your code i think your main issue is more in the architecture of the code, than it is about ScrollTrigger.

I noticed you are initialising Barba.js inside your homepage.js file.  Not sure what your structure is, but provided that homepage.js is only used on homepage, that approach is wrong. 

 

You should have something like a main.js file, which will contain all your Barba.js related functions and then use Views for any page specific logic.

 

Take a look at the docs, they have some great details.  

  • Like 3
Link to post
Share on other sites
  • 2 months later...

I also have a question relating to this. I'll post it here BUT do let me know if I should move this to a new thread @ZachSaucier @GreenSock

 

I am also using barba.js with scrollTrigger and when transitioning to pages it's getting a bit mixed up and now working like it does on page load. I have a function for this that I am refiring when the new page comes in.

 

navReveal: function() {

		let $elems = document.querySelectorAll('nav.main, a.logo__main'),
			$trigger;

		console.log('test 1');

		if (ScrollTrigger.getById('nav')) ScrollTrigger.getById('nav').kill();

		if (dev.ui.body.classList.contains('home')) {
			$trigger = document.querySelector('div.home__content');
		} else {
			$trigger = dev.ui.body;
		}

		gsap.fromTo($elems, {
			opacity: 0
		}, {
			opacity: 1,
			ease: 'none',
			scrollTrigger: {
				id: 'nav',
				trigger: $trigger,
				start: '0 center',
				end: '+=100px',
				scrub: true,
				markers: true,
				onUpdate: self => console.log('progress: ', self.progress)
			}
		});
		
	}

The issue seems to be that when I transition from page to page the trigger position is lost (as is moving about per page). The markers remain in tact but that's okay as they don't remove when you kill the scrollTrigger instance. SO. I'm wondering if from looking at the code if everything looks ok and I'm not doing anything stupid? The idea is that if the instance has already been created, then kill it, and then when it gets to the gsap.fromTo part it should be creating it (again).

Link to post
Share on other sites

Hi @aok, I think you might be having the same problem I was with markers moving around and triggering the animation at different stages on reload of page. I made out a Test example to showcase this problem and actually I think (well it works for me ), came up with a solution as I noticed it was adding more markers on reload and then gsap was taking these new markers as the triggers which were being pushed down by the old markers . I explained it much better and came up with a solution I hope to your problem - 

 

Link to post
Share on other sites

@aok It's tough to say what's going wrong based only on the snippet that you provided. If you have a specific GSAP question feel free to post about it in a new thread but please make a minimal demo of the issue :) 

Link to post
Share on other sites
  • 6 months later...

Hi, 

I was also having the same issue with scrollTrigger markers being pushed down after going back to the original page using barba transition, which leads to undesired behaviors on my animation. 

What I found is that the elements top position in the barba-container (the one barba switch between transitions) is pushed down, you can see that by logging getBoundingClientRect() in barba hooks and thus, leads to the pushed down markers. I guess scrollTrigger calculates the start and end positions based on these values under the hood (correct me if I'm wrong). One of the possible cause I found in barba docs is that during the transition, both containers of the previous and current page will exist at the same time on the DOM, so the elements in the current page may be pushed down by the elements in the previous page. 

Barba suggest to use data.current.container.remove() to manually remove the previous container and fix this issues. I used it in the afterLeave hook and it works just fine. Basically, I don't need to call kill() or refresh() before re-initialize the scrollTrigger and also I think it can works if you re-initalize it in afterEnter() hook as well and not neccessary have to be beforeEnter(). The relevant section in barba docs can be found here: Barba.

P/s: sorry for my bad English, it is not my first language and I'm still learning it.

  • Like 2
Link to post
Share on other sites
  • 2 months later...

Something that helped me in Reactjs was to kill all the ScrollTriggers on a page when it dismounts.

 

In the useEffect I have to create the ScrollTriggers I am doing this:

 

useEffect(() => {
  ...
  ...
  return () => {
    // We kill all scroll triggers so the next pages can recreate them.
    const triggers = ScrollTrigger.getAll();
    if (triggers) {
      triggers.forEach((trigger) => {
        trigger.kill();
      })
    }
  };
}, []);

 

Hope this helps someone.

  • Like 1
  • Thanks 2
Link to post
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.

×