Zooming Image

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

background
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

PropTypeDescriptionDefault Value
srcstringSource path or link to the image that will be zoomed in onundefined
childrenReactNodeThe element of text that will go on the buttonundefined
heightstringTotal scroll height of the image and card combined"135vh"

Inspired by the be company
Image Credits: the be company