Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
joshahayes

Best way to recreate these GSAP transitions on this website?

Recommended Posts

Hi, I'm looking to recreate these sort-of quick swift transitions between the different sections on this page seen here, with Scrolltrigger. I really like how the animations on this site are pretty much instant, so it doesn't look like there's any scrub. 

 

In my case I've got an SVG map that has a fixed position for which I was to use these transitions on, so for example as the user scrolls down, the colours could maybe change and vice-versa. I was originally thinking of just creating multiple different sections in my HTML, and just using each of these sections as triggers in multiple Scrolltriggers. The .sectionWrapper has a position: absolute so it pretty much takes up the entire page. 

      	<div class="sectionWrapper">
            <div class="section one"></div>
            <div class="section two"></div>
            <div class="section three"></div>
            <div class="section four"></div>
        </div>

 

I wasn't sure about how well this would work so I tried something else out, nested timelines:

// Timeline with scroll
let master = gsap.timeline({
    scrollTrigger: {
        trigger: '.sectionWrapper',
        start: 'top top',
        end: 'bottom bottom',
        toggleActions: "play none none reverse",
        markers: true
    }
});

// First section
let greenTL = gsap.timeline();

var rule = CSSRulePlugin.getRule(".bar:after");

greenTL.to('polygon', {
    fill: (i, el) => greenMapCol(el.getAttribute('data-canopy'))
}).to('.innerCircle', {
    background: 'linear-gradient(317deg, rgba(3, 179, 218, 1) 0%, rgba(3, 218, 154, 1) 49%, rgba(1, 242, 207, 1) 100%)'
}).to(rule, {
    background: 'linear-gradient(317deg, rgba(3, 179, 218, 1) 0%, rgba(3, 218, 154, 1) 49%, rgba(1, 242, 207, 1) 100%)'
});


// Second section
let heatTL = gsap.timeline();
heatTL.to('polygon', {
    onEnter: () => currentCol = heatMapCol,
    fill: (i, el) => heatMapCol(el.getAttribute('data-canopy'))
}).to('.innerCircle', {
    background: 'linear-gradient(317deg, rgb(249, 248, 244) 0%, rgb(255, 223, 123) 25%, rgb(255, 193, 82) 50%, rgb(252, 147, 61) 75%, rgb(255, 104, 51) 100%)',
}).to(rule, {
    background: 'linear-gradient(317deg, rgb(249, 248, 244) 0%, rgb(255, 223, 123) 25%, rgb(255, 193, 82) 50%, rgb(252, 147, 61) 75%, rgb(255, 104, 51) 100%)',
});

// Adding the timelines together
master.add(greenTL);
master.add(heatTL);

 

I'm stuck on whether or not a timeline could possibly be useful for this purpose, and just hooking up one Scrolltrigger to the timeline with scrub: true? And in that timeline, maybe have nested timelines with each representing one section? Would this work? What would be the best way to go about this? I just really like how segmented everything was in that page I referenced. Cheers!

Link to comment
Share on other sites

I have no idea how that demo site was actually built, but based on my cursory glance there isn't much need for ScrollTrigger necessarily - I think all they're doing is listening for wheel/touchmove/pointermove events to sense when the user wants to "scroll" to the next section. Notice there's no native scrollbar at all. All "scrolling" is totally fake. 

 

So you could just have a function you'd call for each section (like section1(), section2()) and that dynamically creates your tweens/timelines so that it's all completely dynamic and picks up wherever the elements are at that moment (because people may "scroll" quickly and not let animations finish). Then you just keep track of the section you're in and iterate forward/backward based on the user's scroll intent. 

 

By the way, usually whenever I see someone using CSSRulePlugin (and you forgot to wrap your value in a cssRule:{} object by the way), I like to tell them that they could probably just use CSS variables instead - it strikes me as a cleaner approach in most cases. GSAP can directly animate CSS variables - no need for an extra plugin. {"--yourVariable": 200}

 

Good luck!

  • Thanks 1
Link to comment
Share on other sites

Thank you so much, this really clears it up for me. Here's what I've done so far, would this be the best way to go? I'm using a counter to keep track of what section I'm in at each point, and in each section function I've just got the gsap animations going. 

// Counter that tells which section the user is currently in
let sectionCounter = 1;

window.addEventListener('wheel', (e) => {

    if (!menuOpen) { // This can be ignored
        if (e.deltaY < 0) {
            if (sectionCounter == 1) {
                return;
            } else if (sectionCounter == 2) {
                section1();
                sectionCounter = 1;
            } else if (sectionCounter == 3) {
                section2();
                sectionCounter = 2;
            } else if (sectionCounter == 4) {
                section3();
                sectionCounter = 3;
            }
        }
        if (e.deltaY > 0) {

            if (sectionCounter == 4) {
                return;
            } else if (sectionCounter == 3) {
                section4();
                sectionCounter = 4;
            } else if (sectionCounter == 2) {
                section3();
                sectionCounter = 3;
            } else if (sectionCounter == 1) {
                section2();
                sectionCounter = 2;
            }
        }
    }
})

 

Link to comment
Share on other sites

Sort of, yeah. I generally think more like this: 

let sections = [section1, section2, section3, section4], // function references
	clampIndex = gsap.utils.clamp(0, sections.length - 1),
	curIndex = 0;

window.addEventListener("wheel", e => goto(e.deltaY < 0 ? -1 : 1));
// you'll also need to listen for touchmove and pointermove events to sense when a touch device attempts to scroll/swipe.

function goto(direction) { // direction is 1 for forward, -1 for backard
	let newIndex = clampIndex(curIndex + direction);
	if (newIndex !== curIndex) {
		sections[newIndex](direction);
		curIndex = newIndex;
	}
}

So you'd have a section1() function, a section2() function, etc. that handle animating to that state. Depending on your content, you may need to also pass in the direction so that it knows if it's animating from the previous state or the next state (backwards). 

 

Don't forget to listen for touchmove/pointermove events to sense when a user tries to touch-swipe/scroll. It can be a little tricky. 

 

Good luck!

  • Thanks 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.
×