Created
July 15, 2025 03:53
-
-
Save wpeasy/ae81d11f64752e83914f047338afd076 to your computer and use it in GitHub Desktop.
Bricks No GSAP Scrub
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (() => { | |
| const parseOffset = (offset, vh) => | |
| offset?.endsWith('%') ? (parseFloat(offset) / 100) * vh : parseFloat(offset || 0); | |
| const getTargetPoint = (rect, side) => { | |
| switch (side) { | |
| case 'top': return rect.top; | |
| case 'center': return rect.top + rect.height / 2; | |
| case 'bottom': return rect.bottom; | |
| default: return rect.top; | |
| } | |
| }; | |
| const updateVisibility = () => { | |
| const vh = window.innerHeight; | |
| const trackers = [...document.querySelectorAll('.at-vp-tracker')]; | |
| trackers.forEach(el => { | |
| const rect = el.getBoundingClientRect(); | |
| const partiallyVisible = rect.bottom > 0 && rect.top < vh; | |
| const side = el.dataset.trackSide || 'top'; | |
| const topOffset = parseOffset(el.dataset.trackTopOffset, vh); | |
| const bottomOffset = vh - parseOffset(el.dataset.trackBottomOffset, vh); | |
| const point = getTargetPoint(rect, side); | |
| // Always update scroll position | |
| el.style.setProperty('--at-scroll-position', point.toFixed(2)); | |
| // Invert logic | |
| let scrub = 0; | |
| const isInverted = el.hasAttribute('data-invert'); | |
| if (isInverted) { | |
| if (point >= topOffset && point <= bottomOffset) { | |
| scrub = 1; | |
| el.removeAttribute('data-outside-top'); | |
| el.removeAttribute('data-outside-bottom'); | |
| } else { | |
| const isAbove = point < topOffset; | |
| const distFromEdge = isAbove | |
| ? topOffset - point | |
| : point - bottomOffset; | |
| const maxDist = isAbove | |
| ? topOffset | |
| : vh - bottomOffset; | |
| scrub = 1 - Math.min(distFromEdge / maxDist, 1); | |
| // Set directional attributes | |
| if (isAbove) { | |
| el.setAttribute('data-outside-top', ''); | |
| el.removeAttribute('data-outside-bottom'); | |
| } else { | |
| el.setAttribute('data-outside-bottom', ''); | |
| el.removeAttribute('data-outside-top'); | |
| } | |
| } | |
| } else { | |
| const clamped = Math.min(Math.max(point, topOffset), bottomOffset); | |
| scrub = (clamped - topOffset) / (bottomOffset - topOffset); | |
| el.removeAttribute('data-outside-top'); | |
| el.removeAttribute('data-outside-bottom'); | |
| } | |
| const scrubNeg = 1 - scrub; | |
| el.style.setProperty('--at-scrub', scrub.toFixed(4)); | |
| el.style.setProperty('--at-scrub-negative', scrubNeg.toFixed(4)); | |
| // Viewport visibility | |
| if (partiallyVisible) { | |
| el.setAttribute('data-in-vp', ''); | |
| } else { | |
| el.removeAttribute('data-in-vp'); | |
| } | |
| // Bounds visibility | |
| if (point >= topOffset && point <= bottomOffset) { | |
| el.setAttribute('data-in-bounds', ''); | |
| } else { | |
| el.removeAttribute('data-in-bounds'); | |
| } | |
| }); | |
| }; | |
| window.addEventListener('scroll', updateVisibility, { passive: true }); | |
| window.addEventListener('resize', updateVisibility); | |
| document.addEventListener('DOMContentLoaded', updateVisibility); // Safe initialization | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment