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>
);
}