Command Palette

Search for a command to run...

Tilt

3D tilt effect that responds to mouse movement, enhancing UI elements with a dynamic depth effect, customizable rotation factors and spring options.

Ghost in the shell - Kôkaku kidôtai

Ghost in the Shell

Kôkaku kidôtai

Installation

Install the following dependencies:

pnpm add motion

Create a file at components/motion/tilt.tsx and paste the given code.

"use client";
 
import React, { useRef } from "react";
import {
    motion,
    useMotionTemplate,
    useMotionValue,
    useSpring,
    useTransform,
    MotionStyle,
    SpringOptions,
} from "motion/react";
 
export type TiltProps = {
    children: React.ReactNode;
    className?: string;
    style?: MotionStyle;
    rotationFactor?: number;
    isRevese?: boolean;
    springOptions?: SpringOptions;
};
 
export function Tilt({
    children,
    className,
    style,
    rotationFactor = 15,
    isRevese = false,
    springOptions,
}: TiltProps) {
    const ref = useRef<HTMLDivElement>(null);
 
    const x = useMotionValue(0);
    const y = useMotionValue(0);
 
    const xSpring = useSpring(x, springOptions);
    const ySpring = useSpring(y, springOptions);
 
    const rotateX = useTransform(
        ySpring,
        [-0.5, 0.5],
        isRevese
            ? [rotationFactor, -rotationFactor]
            : [-rotationFactor, rotationFactor],
    );
    const rotateY = useTransform(
        xSpring,
        [-0.5, 0.5],
        isRevese
            ? [-rotationFactor, rotationFactor]
            : [rotationFactor, -rotationFactor],
    );
 
    const transform = useMotionTemplate`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
 
    const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
        if (!ref.current) return;
 
        const rect = ref.current.getBoundingClientRect();
        const width = rect.width;
        const height = rect.height;
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;
 
        const xPos = mouseX / width - 0.5;
        const yPos = mouseY / height - 0.5;
 
        x.set(xPos);
        y.set(yPos);
    };
 
    const handleMouseLeave = () => {
        x.set(0);
        y.set(0);
    };
 
    return (
        <motion.div
            ref={ref}
            className={className}
            style={{
                transformStyle: "preserve-3d",
                ...style,
                transform,
            }}
            onMouseMove={handleMouseMove}
            onMouseLeave={handleMouseLeave}
        >
            {children}
        </motion.div>
    );
}

Update the import paths to match your project setup.