Skip to content

Instantly share code, notes, and snippets.

@Fr1endly
Created April 13, 2023 00:23
Show Gist options
  • Save Fr1endly/9da5aa33b90961369425fd18700c54be to your computer and use it in GitHub Desktop.
Save Fr1endly/9da5aa33b90961369425fd18700c54be to your computer and use it in GitHub Desktop.
Locomotive Scroll with ScrollTrigger scrubbing and pinning
<div class="smooth-scroll">
<section class="description panel blue">
<div><h1>Locomotive Scroll + ScrollTrigger</h1>
<p>Demonstrates how ScrollTrigger can be used with a smooth scrolling library like Locomotive Scroll, including scrubbing and pinning.</p>
<div class="scroll-down">Scroll down<div class="arrow"></div></div>
</div>
</section>
<section class="panel red">
<p><span class="line line-1"></span>This line's animation will begin when it enters the viewport and finish when its top edge hits the top of the viewport, staying perfectly in sync with the scrollbar because it has <code>scrub:&nbsp;true</code></p>
</section>
<section class="panel orange">
<p><span class="line line-2"></span>This orange panel gets pinned when its top edge hits the top of the viewport, then the line's animation is linked with the scroll position until it has traveled 100% of the viewport's height (<code>end: "+=100%"</code>), then the orange panel is unpinned and normal scrolling resumes. Padding is added automatically to push the rest of the content down so that it catches up with the scroll when it unpins. You can set <code>pinSpacing: false</code> to prevent that if you prefer.</p>
</section>
<section class="panel purple">
<p><span class="line line-3"></span>This panel gets pinned in a similar way, and has a more involved animation that's wrapped in a timeline, fading the background color and animating the transforms of the paragraph in addition to the line, all synced with the scroll position perfectly.</p>
</section>
<section class="panel gray">
DONE!
</section>
</div>

Locomotive Scroll with ScrollTrigger scrubbing and pinning

Shows how to use Locomotive Scroll with ScrollTrigger

A Pen by Fr1endly on CodePen.

License.

gsap.registerPlugin(ScrollTrigger);
// --- SETUP START ---
// Using Locomotive Scroll from Locomotive https://github.com/locomotivemtl/locomotive-scroll
const locoScroll = new LocomotiveScroll({
el: document.querySelector(".smooth-scroll"),
smooth: true
});
// each time Locomotive Scroll updates, tell ScrollTrigger to update too (sync positioning)
locoScroll.on("scroll", ScrollTrigger.update);
// tell ScrollTrigger to use these proxy methods for the ".smooth-scroll" element since Locomotive Scroll is hijacking things
ScrollTrigger.scrollerProxy(".smooth-scroll", {
scrollTop(value) {
return arguments.length ? locoScroll.scrollTo(value, {duration: 0, disableLerp: true}) : locoScroll.scroll.instance.scroll.y;
}, // we don't have to define a scrollLeft because we're only scrolling vertically.
getBoundingClientRect() {
return {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight};
},
// LocomotiveScroll handles things completely differently on mobile devices - it doesn't even transform the container at all! So to get the correct behavior and avoid jitters, we should pin things with position: fixed on mobile. We sense it by checking to see if there's a transform applied to the container (the LocomotiveScroll-controlled element).
pinType: document.querySelector(".smooth-scroll").style.transform ? "transform" : "fixed"
});
// each time the window updates, we should refresh ScrollTrigger and then update LocomotiveScroll.
ScrollTrigger.addEventListener("refresh", () => locoScroll.update());
ScrollTrigger.defaults({ scroller: ".smooth-scroll" });
// --- SETUP END ---
// --- RED PANEL ---
gsap.from(".line-1", {
scrollTrigger: {
trigger: ".line-1",
scrub: true,
start: "top bottom",
end: "top top",
onUpdate() {
console.log("Update")
}
},
scaleX: 0,
transformOrigin: "left center",
ease: "none"
});
// --- ORANGE PANEL ---
gsap.from(".line-2", {
scrollTrigger: {
trigger: ".orange",
scrub: true,
pin: true,
start: "top top",
end: "+=100%",
onUpdate() {
console.log("Update")
}
},
scaleX: 0,
transformOrigin: "left center",
ease: "none"
});
// --- PURPLE/GREEN PANEL ---
var tl = gsap.timeline({
scrollTrigger: {
trigger: ".purple",
scrub: true,
pin: true,
start: "top top",
end: "+=100%",
onUpdate() {
console.log("Update")
}
}
});
tl.from(".purple p", {scale: 0.3, rotation:45, autoAlpha: 0, ease: "power2"})
.from(".line-3", {scaleX: 0, transformOrigin: "left center", ease: "none"}, 0)
.to(".purple", {backgroundColor: "#28a92b"}, 0);
// after everything is set up, refresh() ScrollTrigger and update LocomotiveScroll because padding may have been added for pinning, etc.
ScrollTrigger.refresh();
<script src="https://unpkg.co/gsap@3/dist/gsap.min.js"></script>
<script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/locomotive-scroll@4.1.4/dist/locomotive-scroll.min.js"></script>
.panel {
height: 100vh;
}
.line {
width: 100%;
max-width: 800px;
height: 8px;
margin: 0 0 10px 0;
position: relative;
display: inline-block;
background-color: rgba(255,255,255,1);
}
/* by default, ScrollTrigger sets overflow to auto so that the scrollbar is factored in particularly when pinning so that the pinned elements' sizes are correct, but with LocomotiveScroll we want to force that to be hidden because it does its own [fake] scrollbar */
.smooth-scroll {
overflow-y: hidden !important;
}
<link href="https://codepen.io/GreenSock/pen/7ba936b34824fefdccfe2c6d9f0b740b.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/locomotive-scroll@4.1.4/dist/locomotive-scroll.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment