Lenis Scroll
<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.15/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.15/dist/lenis.min.js"></script>
<script>
const lenis = new Lenis({ duration: 1.4 });
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time) => {
lenis.raf(time * 1000);
});
gsap.ticker.lagSmoothing(0);
</script>
Image Parallax
<script>
document.addEventListener('DOMContentLoaded', () => {
gsap.registerPlugin(ScrollTrigger);
const mm = gsap.matchMedia();
mm.add(
{
mobile: '(max-width: 479px)',
mobileLandscape: '(min-width: 480px) and (max-width: 767px)',
tablet: '(min-width: 768px) and (max-width: 991px)',
desktop: '(min-width: 992px)',
},
(context) => {
const { mobile, mobileLandscape, tablet, desktop } = context.conditions;
document.querySelectorAll('[data-parallax="trigger"]').forEach((trigger) => {
// Disable parallax on selected breakpoints
const disable = trigger.getAttribute('data-parallax-disable');
if ((disable === 'mobile' && mobile) || (disable === 'mobileLandscape' && mobileLandscape) || (disable === 'tablet' && tablet) || (disable === 'desktop' && desktop)) {
return;
}
// Target element
const target = trigger.querySelector('[data-parallax="target"]') || trigger;
// Direction
const direction = trigger.getAttribute('data-parallax-direction') || 'vertical';
const property = direction === 'horizontal' ? 'xPercent' : 'yPercent';
// Parallax values
const startValue = parseFloat(trigger.getAttribute('data-parallax-start')) || 0;
const endValue = parseFloat(trigger.getAttribute('data-parallax-end')) || -15;
// Scrub
const scrubAttribute = trigger.getAttribute('data-parallax-scrub');
const scrub = scrubAttribute !== null ? parseFloat(scrubAttribute) : true;
// ScrollTrigger positions
const scrollStart = trigger.getAttribute('data-parallax-scroll-start') || 'top bottom';
const scrollEnd = trigger.getAttribute('data-parallax-scroll-end') || 'bottom top';
gsap.fromTo(
target,
{
[property]: startValue,
},
{
[property]: endValue,
ease: 'none',
scrollTrigger: {
trigger: trigger,
start: `clamp(${scrollStart})`,
end: `clamp(${scrollEnd})`,
scrub: scrub,
invalidateOnRefresh: true,
},
},
);
});
ScrollTrigger.refresh();
},
);
});
</script>