Created
August 22, 2026 15:47
-
-
Save tannerhallman/678dbab918eedab175517901d823031a to your computer and use it in GitHub Desktop.
Horizontal Scroll for Colin
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
| /* Horizontal auto-scroll for the first post panel on | |
| * https://www.vortexofinfinitediscovery.com/blogs/detourism | |
| * Paste into the DevTools console. Any user input (wheel/trackpad, touch, | |
| * drag, arrow keys) cancels it. Manual stop: __hScroll.stop() | |
| */ | |
| (() => { | |
| const PX_PER_SECOND = 40; // ← adjust speed here | |
| const LOOP = false; // true = wrap back to the start instead of stopping | |
| if (window.__hScroll) window.__hScroll.stop('restarted'); | |
| // The block's classes are hash-suffixed per section, so match by prefix and | |
| // take the first one on the page = the first panel of posts. | |
| const wrap = document.querySelector('[class*="ai-blog-scroll-wrapper-"]'); | |
| if (!wrap) return console.warn('[hScroll] post panel not found.'); | |
| const track = wrap.querySelector('[class*="ai-blog-scroll-track-"]'); | |
| // Most of these blocks are an overflow-x wrapper around a flex track. If the | |
| // wrapper doesn't actually scroll, fall back to translating the track. | |
| const useScrollLeft = wrap.scrollWidth - wrap.clientWidth > 1; | |
| const max = useScrollLeft | |
| ? wrap.scrollWidth - wrap.clientWidth | |
| : (track ? track.scrollWidth - wrap.clientWidth : 0); | |
| if (max <= 1) return console.warn('[hScroll] nothing to scroll — panel fits the viewport.', wrap); | |
| // Snap / smooth scrolling fights a per-frame nudge; suspend and restore later. | |
| const prev = { snap: wrap.style.scrollSnapType, behavior: wrap.style.scrollBehavior, | |
| transform: track ? track.style.transform : '', | |
| transition: track ? track.style.transition : '' }; | |
| wrap.style.scrollSnapType = 'none'; | |
| wrap.style.scrollBehavior = 'auto'; | |
| if (!useScrollLeft && track) track.style.transition = 'none'; | |
| const KEYS = new Set(['ArrowLeft','ArrowRight','ArrowUp','ArrowDown', | |
| 'PageUp','PageDown','Home','End',' ']); | |
| let pos = useScrollLeft ? wrap.scrollLeft : 0; | |
| let last = null; | |
| function stop(reason) { | |
| const s = window.__hScroll; | |
| if (!s) return; | |
| cancelAnimationFrame(s.raf); | |
| s.cleanup(); | |
| window.__hScroll = null; | |
| console.log(`[hScroll] stopped (${reason}).`); | |
| } | |
| const onInput = (e) => { | |
| if (e.type === 'keydown' && !KEYS.has(e.key)) return; | |
| stop('user input detected'); | |
| }; | |
| // The wrapper also has its own drag-to-scroll; bail if the position drifts | |
| // from what we last wrote. | |
| const onScroll = () => { | |
| if (useScrollLeft && Math.abs(wrap.scrollLeft - pos) > 2) stop('user scrolled the panel'); | |
| }; | |
| function step(now) { | |
| if (last === null) last = now; | |
| const dt = Math.min((now - last) / 1000, 0.1); // clamp tab-switch jumps | |
| last = now; | |
| pos += PX_PER_SECOND * dt; | |
| if (pos >= max - 0.5) { | |
| if (!LOOP) { apply(max); return stop('reached the end'); } | |
| pos = 0; | |
| } | |
| apply(pos); | |
| window.__hScroll.raf = requestAnimationFrame(step); | |
| } | |
| function apply(x) { | |
| if (useScrollLeft) { wrap.scrollLeft = x; pos = wrap.scrollLeft; } | |
| else if (track) { track.style.transform = `translateX(${-x}px)`; } | |
| } | |
| const passive = { passive: true }; | |
| wrap.addEventListener('wheel', onInput, passive); | |
| wrap.addEventListener('touchstart', onInput, passive); | |
| wrap.addEventListener('pointerdown', onInput, passive); | |
| wrap.addEventListener('scroll', onScroll, passive); | |
| window.addEventListener('wheel', onInput, passive); | |
| window.addEventListener('touchstart', onInput, passive); | |
| window.addEventListener('keydown', onInput); | |
| window.__hScroll = { | |
| el: wrap, | |
| raf: requestAnimationFrame(step), | |
| stop, | |
| cleanup() { | |
| wrap.removeEventListener('wheel', onInput); | |
| wrap.removeEventListener('touchstart', onInput); | |
| wrap.removeEventListener('pointerdown', onInput); | |
| wrap.removeEventListener('scroll', onScroll); | |
| window.removeEventListener('wheel', onInput); | |
| window.removeEventListener('touchstart', onInput); | |
| window.removeEventListener('keydown', onInput); | |
| wrap.style.scrollSnapType = prev.snap; | |
| wrap.style.scrollBehavior = prev.behavior; | |
| if (track) track.style.transition = prev.transition; | |
| }, | |
| }; | |
| console.log('[hScroll] running on', wrap, useScrollLeft ? '(scrollLeft)' : '(transform)'); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment