Zoom Scroll
Content scales from far to close as you scroll — Apple AirPods product page effect with scale and blur interpolation
scrollzoomscaleappleframer-motion
Install dependencies
$npm install framer-motionPreview
Source Code
"use client";
import { ReactNode, useRef } from "react";
import { motion, useScroll, useTransform, MotionValue } from "framer-motion";
interface ZoomScrollProps {
children: ReactNode;
fromScale?: number;
toScale?: number;
fromBlur?: number;
toBlur?: number;
className?: string;
}
export default function ZoomScroll({
children,
fromScale = 0.35,
toScale = 1,
fromBlur = 10,
toBlur = 0,
className = "",
}: ZoomScrollProps) {
const ref = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start end", "end start"],
});
const scale = useTransform(
scrollYProgress,
[0, 0.5, 1],
[fromScale, toScale, toScale],
);
const blur: MotionValue<string> = useTransform(
scrollYProgress,
[0, 0.5, 1],
[`blur(${fromBlur}px)`, `blur(${toBlur}px)`, `blur(${toBlur}px)`],
);
const opacity = useTransform(
scrollYProgress,
[0, 0.2, 0.5, 1],
[0.25, 0.7, 1, 1],
);
return (
<div
ref={ref}
className={`relative h-[200vh] w-full ${className}`}
>
<div className="sticky top-0 flex h-screen w-full items-center justify-center overflow-hidden">
<motion.div
style={{
scale,
filter: blur,
opacity,
transformOrigin: "center center",
willChange: "transform, filter",
}}
className="flex h-full w-full items-center justify-center"
>
{children}
</motion.div>
</div>
</div>
);
}