Zooming Image
An image that zooms in as you scroll and a card for information

Zoooooom
Installation
1
Install dependencies
npm install framer-motion tailwind-merge clsx
2
Add util file
lib/utils.ts
import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
3
Copy the source code
components/ui/zooming-image.tsx
'use client';
import React, { useState, useRef, useEffect } from 'react';
import Image from "next/image";
import { motion, useScroll, useTransform } from "framer-motion";
interface ZoomingImageProps {
src: string;
children: React.ReactNode;
height?: string;
}
export function ZoomingImage({
src,
children,
height = '135vh'
}: ZoomingImageProps) {
const targetRef = useRef<HTMLDivElement | null>(null);
const [isSmallScreen, setIsSmallScreen] = useState(false);
useEffect(() => {
const checkScreenSize = () => {
setIsSmallScreen(window.innerWidth < 640);
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => {
window.removeEventListener('resize', checkScreenSize);
};
}, []);
const { scrollYProgress } = useScroll({
target: targetRef,
offset: ["start 0.2", "start start"],
});
const scale = useTransform(scrollYProgress, [0, 1], [0.5, 1]);
const { scrollYProgress: scrollYProgress1 } = useScroll({
target: targetRef,
offset: ["center center", "end end"],
});
const textY = useTransform(
scrollYProgress1,
[0, 1],
isSmallScreen ? [150, -20] : [-100, 10]
);
return (
<div className="relative" style={{ height }} ref={targetRef}>
<motion.div
style={{
scale,
height: "100vh",
}}
className="sticky top-0 w-full z-0"
>
<motion.div
style={{
height: "100%",
width: "100%"
}}
>
<Image
className="h-full w-full object-cover"
width={1920}
height={1080}
src={src}
alt="background"
priority
/>
</motion.div>
<motion.div
className="absolute inset-0"
/>
</motion.div>
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true, amount: 0.5 }}
transition={{ duration: 0.8, ease: "easeOut" }}
style={{
y: textY,
}}
className="absolute left-0 top-0 flex h-full w-full flex-col sm:items-end sm:justify-end items-center justify-end text-white sm:p-[10vh] p-[5%]"
>
{children}
</motion.div>
</div>
);
}
Props
Prop | Type | Description | Default Value |
---|---|---|---|
src | string | Source path or link to the image that will be zoomed in on | undefined |
children | ReactNode | The element of text that will go on the button | undefined |
height | string | Total scroll height of the image and card combined | "135vh" |
Inspired by the be company
Image Credits: the be company