This demo has several versions:
scroll-timeline
ScrollTimeline
This demos sports section that scrolls horizontally as you scroll down.
Original demo by Cameron Knight: https://codepen.io/cameronknight/pen/qBNvrRQ
<section id="sectionPin"> <div class="pin-wrap-sticky"> <div class="pin-wrap">…</div> </div> </div>
const $sectionPin = document.querySelector('#sectionPin'); const $pinWrapSticky = document.querySelector('.pin-wrap-sticky'); const $pinWrap = document.querySelector('.pin-wrap'); /* Stretch it out, so that we create room for the horizontal scroll animation */ $sectionPin.style.height = '500vh'; $sectionPin.style.overflow = 'visible'; // To make position sticky work … /* Stick to Top */ $pinWrapSticky.style.height = '100vh'; $pinWrapSticky.style.width = '100vw'; $pinWrapSticky.style.position = 'sticky'; $pinWrapSticky.style.top = '0'; $pinWrapSticky.style.overflowX = 'hidden'; /* Stretch out pinwrap */ $pinWrap.style.height = '100vh'; $pinWrap.style.width = '250vmax'; // Scroll-Linked Animation $pinWrap.animate( { transform: [ ``, `translateX(calc(-100% + 100vw))`], }, { timeline: new ViewTimeline({ subject: $sectionPin, axis: 'block', }), fill: 'forwards', rangeStart: `contain 0%`, rangeEnd: `contain 100%`, } );
By stretching out #sectionPin to a height of 500vh, more scroll estate is created. A ViewTimeline is attached to that element, tracking it as it crosses the scrollport.
#sectionPin
500vh
The intermediary element .pin-wrap-sticky is made sticky, and it’s the .pin-wrap that gets animated on the ViewTimeline.
.pin-wrap-sticky
.pin-wrap
The start position of the horizontal strip .pin-wrap is at 0,0 within its parent. Its end position is a horizontal translation of -100% to which the viewport width is added. If the viewport width were not taken into account, the element would be entirely out of view.
0,0
-100%
⚠️ Your browser does not support Scroll-driven Animations. Please use Chrome 115 or newer.
With CSS view-timeline