Files
Emil_Shanayev_Resume/lib/useScroll.ts
T
EmilandClaude Sonnet 5 b5ffc67a82
Deploy to GitHub Pages / build (push) Canceled after 0s
Deploy to GitHub Pages / deploy (push) Canceled after 0s
Redesign the resume site as a 3D-editor viewport
New design: studio backdrop with a perspective floor and X/Z axis lines, an
orientation gizmo that orbits on scroll, Bricolage Grotesque and Instrument
Sans (bundled, no build-time network). Projects are laid out as plates by a
row planner that keeps every row full for any count; a demo image from the
repo root is shown when present, otherwise a generated voxel sculpture.
Text and projects still come live from Gitea.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XtcZtnG4GQA1d6mW5PNrS9
2026-09-20 21:37:35 +03:00

44 lines
1.5 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
const reducedMotion = () => window.matchMedia('(prefers-reduced-motion: reduce)').matches
/** Scroll position of the page as 0..1. Updates at most once per frame; frozen when the user prefers reduced motion. */
export function useScrollProgress(): number {
const [progress, setProgress] = useState(0)
useEffect(() => {
if (reducedMotion()) return
let frame = 0
const update = () => {
frame = 0
const max = document.documentElement.scrollHeight - window.innerHeight
setProgress(max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0)
}
const schedule = () => {
if (!frame) frame = requestAnimationFrame(update)
}
update()
window.addEventListener('scroll', schedule, { passive: true })
window.addEventListener('resize', schedule)
return () => {
window.removeEventListener('scroll', schedule)
window.removeEventListener('resize', schedule)
if (frame) cancelAnimationFrame(frame)
}
}, [])
return progress
}
/** True once the page has scrolled past the very top; only changes state when that flips. */
export function useScrolled(threshold = 8): boolean {
const [scrolled, setScrolled] = useState(false)
useEffect(() => {
const update = () => setScrolled(window.scrollY > threshold)
update()
window.addEventListener('scroll', update, { passive: true })
return () => window.removeEventListener('scroll', update)
}, [threshold])
return scrolled
}