Skip to main content

ScrollTrigger.scrollerProxy

ScrollTrigger.scrollerProxy( scroller:String | Element, vars:Object )

Allows you to hijack the scrollTop and/or scrollLeft getters/setters for a particular scroller element so that you can implement things like smooth scrolling or other custom effects.

Parameters

  • scroller: String | Element

    Selector text or the Element that is the scroller to be proxied, like "body" or ".container"

  • vars: Object

    An object containing scrollTop and/or scrollLeft functions that serve as proxied getters/setters, like: {scrollTop: function(value) {...}, scrollLeft: function(value) {...}}. It can also contain a method for getBoundingClientRect(), scrollWidth(), scrollHeight() as well as an optional pinType: "fixed" | "transform"

Details

Allows you to hijack the scrollTop and/or scrollLeft getters/setters for a particular scroller element so that you can implement things like smooth scrolling or other custom effects.

GreenSock's own ScrollSmoother is a smooth-scrolling plugin built on top of ScrollTrigger, so it is totally integrated and super easy to use. It uses native scroll technology to avoid most of the accessibility issues that plague other smooth-scrolling libraries. ScrollSmoother is a members-only benefit of Club GSAP but you're welcome to use a 3rd party library if you prefer - that's why scrollerProxy() exists. We don't support other libraries but as a courtesy we've included some demos below with popular libraries.

How does it work?

Typically ScrollTrigger directly gets/sets the scroll position via the regular properties/methods on that scroller element, but you can provide your own getter/setter functions instead in order to deliver customized experiences, like:

// 3rd party library setup:
const bodyScrollBar = Scrollbar.init(document.body, {
damping: 0.1,
delegateTo: document,
});

// Tell ScrollTrigger to use these proxy getter/setter methods for the "body" element:
ScrollTrigger.scrollerProxy(document.body, {
scrollTop(value) {
if (arguments.length) {
bodyScrollBar.scrollTop = value; // setter
}
return bodyScrollBar.scrollTop; // getter
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight,
};
},
});

// when the smooth scroller updates, tell ScrollTrigger to update() too:
bodyScrollBar.addListener(ScrollTrigger.update);

Basically you're saying "Hey ScrollTrigger, whenever you want to get or set the scrollTop or getBoundingClientRect() on this element, use these methods instead" and then you do whatever you want inside those methods.

walkthrough

Special properties

You MUST have either a scrollTop or scrollLeft getter/setter (or both); the rest may or may not be helpful (they're optional):

PropertyDescription
scrollTopFunction - A method that can serve as a getter AND setter; if it receives an argument, it should be treated as a setter. Otherwise, it should be treated as a getter, returning the current scrollTop value.
scrollLeftFunction - A method that can serve as a getter AND setter; if it receives an argument, it should be treated as a setter. Otherwise, it should be treated as a getter, returning the current scrollLeft value.
fixedMarkersBoolean - If true, it treat the markers as if they're position: fixed. This is only helpful if you're integrating with a smooth scrolling library that results in markers being placed inside an element that's being translated. If you notice your markers moving when they shouldn't, try setting this to true. (added in 3.7.0)
getBoundingClientRectFunction - A method that returns an object with top, left, width, and height properties indicating the bounding rect of the proxied scroller. It is most often {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight}.
scrollWidthFunction - A method that can serve as a getter AND setter; if it receives an argument, it should be treated as a setter. Otherwise, it should be treated as a getter, returning the current scrollWidth value.
scrollHeightFunction - A method that can serve as a getter AND setter; if it receives an argument, it should be treated as a setter. Otherwise, it should be treated as a getter, returning the current scrollHeight value.
pinType"fixed" | "transform" - Determines the manner in which elements get pinned when they're associated with this proxied scroller (if the ScrollTrigger has a pin defined). By default, only the <body> uses position: "fixed" for pinning and in all other cases, transform offsets are used. Why? Because if the any ancestor element has a transform applied (even transform: translate(0, 0)), it creates a new context and position: "fixed" doesn't behave the way you'd expect. It's a browser thing, not a ScrollTrigger thing. pinType lets you force ScrollTrigger to use a specific pinning technique for the proxied scroller. If you notice jittery pins, try setting pinType: "fixed" (jitter is usually caused by the fact that the browser handles scrolling of the main page on a different thread, thus transforms that are applied via JS aren't in sync). If pins don't seem to be sticking at all, try setting pinType: "transform".

GreenSock doesn't recommend or endorse any particular smooth scrolling library - the demos below are based on various forums posts.

Demo (Locomotive Scroll)

loading...

Demo (smooth-scrollbar)

loading...

loading...

ScrollTrigger.scrollerProxy() was added in GSAP 3.4.0