motion-primitives
components
ui
animation
motion

tilt

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

3d
animated
effect
form
motion
special
transform
View Docs

Source Code

Files
tilt.tsx
1'use client';
2
3import React, { useRef } from 'react';
4import {
5  motion,
6  useMotionTemplate,
7  useMotionValue,
8  useSpring,
9  useTransform,
10  MotionStyle,
11  SpringOptions,
12} from 'motion/react';
13
14export type TiltProps = {
15  children: React.ReactNode;
16  className?: string;
17  style?: MotionStyle;
18  rotationFactor?: number;
19  isRevese?: boolean;
20  springOptions?: SpringOptions;
21};
22
23export function Tilt({
24  children,
25  className,
26  style,
27  rotationFactor = 15,
28  isRevese = false,
29  springOptions,
30}: TiltProps) {
31  const ref = useRef<HTMLDivElement>(null);
32
33  const x = useMotionValue(0);
34  const y = useMotionValue(0);
35
36  const xSpring = useSpring(x, springOptions);
37  const ySpring = useSpring(y, springOptions);
38
39  const rotateX = useTransform(
40    ySpring,
41    [-0.5, 0.5],
42    isRevese
43      ? [rotationFactor, -rotationFactor]
44      : [-rotationFactor, rotationFactor]
45  );
46  const rotateY = useTransform(
47    xSpring,
48    [-0.5, 0.5],
49    isRevese
50      ? [-rotationFactor, rotationFactor]
51      : [rotationFactor, -rotationFactor]
52  );
53
54  const transform = useMotionTemplate`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
55
56  const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
57    if (!ref.current) return;
58
59    const rect = ref.current.getBoundingClientRect();
60    const width = rect.width;
61    const height = rect.height;
62    const mouseX = e.clientX - rect.left;
63    const mouseY = e.clientY - rect.top;
64
65    const xPos = mouseX / width - 0.5;
66    const yPos = mouseY / height - 0.5;
67
68    x.set(xPos);
69    y.set(yPos);
70  };
71
72  const handleMouseLeave = () => {
73    x.set(0);
74    y.set(0);
75  };
76
77  return (
78    <motion.div
79      ref={ref}
80      className={className}
81      style={{
82        transformStyle: 'preserve-3d',
83        ...style,
84        transform,
85      }}
86      onMouseMove={handleMouseMove}
87      onMouseLeave={handleMouseLeave}
88    >
89      {children}
90    </motion.div>
91  );
92}
93