useScrollTurn

@sushindustries/ui

Scroll position as a rotation, delivered once per frame, written straight to wherever it goes.

3 min read
View as Markdown

Why it is a hook and not just part of ScrollSpin

ScrollSpin writes a CSS transform. The hero on the home page writes a three.js object's rotation, because a CSS rotateY on a canvas spins the rendered image like a photograph rather than turning the model inside it.

// ScrollSpin: a transform, straight onto the node.
useScrollTurn(({ turn, wobble }) => {
	node.style.transform = `rotateX(${wobble}deg) rotateY(${turn * 360}deg)`;
});

// The 3D mark: a rotation, inside the scene.
useScrollTurn(({ turn }) => {
	group.rotation.y = turn * Math.PI * 2;
});
ts

Two completely different write targets, one question: how far has this page turned. Sharing the measurement means a screenful of scrolling turns the CSS mark and the GLB by the same amount, which is the sort of agreement that silently stops being true the moment it is written twice.

Revolutions are viewport heights

revolutions is how many screens of scrolling make one full turn, not how many pixels. In pixels a phone would spin four times over the same content a desktop turns once, because the content is the same and the screen is not.

Reduced motion

Under prefers-reduced-motion: reduce the callback fires once, at the current position, and never again.

Once, rather than never: that leaves whatever it drives in a sensible still pose rather than at zero, which matters because zero is a value nobody chose - a model parked at rotation zero may be showing you its back.

Used byScroll SpinScroll SpinRotates its children with the page scroll, in a rAF callback rather than React state.@sushindustries/ui · motion
Mentioned inMotion and depth