This demo is a shoe explorer that features 3D-models of several shoes. The 3D-models are linked to a Scroll-Driven Animation, so that they rotate as you scroll.
Each .card has a 3D-model (.glb) loaded that is displayed through <model-viewer>. The model is set to non-interactive, so you can’t directly control it.
.card
.glb
<model-viewer>
A View Timeline is attached to each .card, which is used to drive all animation effects: the footer fading in and the title revealing itself.
.card { view-timeline: --card inline; & h2 { animation: clip-in linear backwards, clip-out linear forwards; animation-timeline: --card; animation-range: entry 50% entry 100%, exit 0% exit 50%; } & footer { animation: fade-in linear both, fade-out linear both; animation-timeline: --card; animation-range: cover 40% cover 50%, cover 50% cover 60%; } }
To sync the progress of the animation effect, the trackProgress helper from the @bramus/sda-utilities package is used. It constantly reads the animation’s progress in a requestAnimationFrame.
trackProgress
@bramus/sda-utilities
requestAnimationFrame
const trackProgress = (animation, cb, precision = 5) => { let progress = 0; const updateValue = () => { if (animation && animation.currentTime) { let newProgress = animation.effect.getComputedTiming().progress * 1; if (animation.playState === "finished") newProgress = 1; newProgress = Math.max(0.0, Math.min(1.0, newProgress)).toFixed(precision); if (newProgress != progress) { progress = newProgress; cb(progress); } } requestAnimationFrame(updateValue); }; requestAnimationFrame(updateValue); }
This helper is hooked up to an empty animation that is attached to the card. The resulting progress value is used for the rotation of the 3D-model
progress
.card { animation: empty linear forwards; animation-timeline: --card; }
import { trackProgress } from 'https://esm.sh/@bramus/sda-utilities@1'; document.querySelectorAll('.card:has(h2)').forEach($card => { const model = $card.querySelector(':scope > model-viewer'); trackProgress($card.getAnimations()[0], (progress) => { model.orientation = `0deg 0deg ${progress * (-360 - 90)}deg`; }); });
⚠️ Your browser does not support Scroll-driven Animations. Please use Chrome 115 or newer.