In addition to my rainbow hero headers on my blog post, I wanted a scrolling effect that would keep the title in view as the reader read the post. Here is the commit where I implemented it on my personal site.
The effect happens in roughly three stages. The initial scroll:
The scroll continuation
and finally setting the heading as fixed on the page.
My first need was to put a ref
on the <Header>
element in the above image. This required a forwardRef
call in the component itself, as well as a useRef
in the parent page wrapping component.
import { forwardRef } from "preact/compat";const Header = forwardRef((props, ref) => (<headerref={ref}...>...</header>))
Each stage is triggered by window scroll event and some very cheap calculations.
window.addEventListener(`scroll`, () => {const currentPos = window.scrollY;window.requestAnimationFrame(() => {const titleOffset = scrollTargetRef.current.getBoundingClientRect().top;console.log(titleOffset);if (titleOffset < -50) {setFontSize(18);} else if (titleOffset < 0) {// TODO: calculate lock values for smooth scrollsetFontSize(33);} else {// reset with original style values, etcsetFontSize(48);}if (titleOffset < -75) {setHoverTitle(true);} else {setHoverTitle(false);}});});
You'll notice that I have a TODO here to calculate smoother positions. This is because the chunkiness of the if statement (and the position fixed swap) is acceptable but not to the level I want it to be.
In the future I'll likely use Framer Motions useMotionValue
helper to smoother out the font changes while adding in some extra divs/spacing to smoother out the fixed change as well.