_❯Scroll Morph
A scroll-progress-driven scale & translate morph using an easeInOutQuad curve — scrub it here with the slider.
scrollmorpheasingCSS
0%
1import { useState } from "react";2import { LabsDemoLayout } from "../../components/labs-experiment-frame/labs-experiment-frame.component";3import {4 ControlGroup,5 ControlPanel,6 ResetButton,7 SliderControl,8} from "../../components/labs-control/labs-control.component";9import * as styles from "./scroll-morph.demo.css";1011// Same easing the hero uses to map scroll progress (0..1) onto the morph.12const easeInOutQuad = (t: number): number => (t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2);1314const MIN_SCALE = 0.62;15const MAX_TRANSLATE_PX = 80;1617export function ScrollMorphDemo() {18 const [percent, setPercent] = useState(0);1920 const progress = percent / 100;21 const eased = easeInOutQuad(progress);22 const scale = 1 - eased * (1 - MIN_SCALE);23 const translateY = eased * MAX_TRANSLATE_PX;2425 return (26 <LabsDemoLayout27 stage={28 <div className={styles.stageInner}>29 <div30 className={styles.morphCard}31 style={{ transform: `translateY(${translateY}px) scale(${scale})` }}32 >33 {percent}%34 </div>35 </div>36 }37 controls={38 <ControlPanel>39 <ControlGroup title="Scroll progress">40 <SliderControl41 label="Progress"42 value={percent}43 min={0}44 max={100}45 step={1}46 onChange={setPercent}47 format={(value) => `${value}%`}48 />49 </ControlGroup>5051 <ResetButton onReset={() => setPercent(0)} />52 </ControlPanel>53 }54 />55 );56}