Use Parallax Tilt

PreviousNext

The use-parallax-tilt hook.

Docs
animbitslib

Preview

Loading preview…
lib/use-parallax-tilt.ts
import * as React from "react";
import { useMotionValue, useSpring, useTransform } from "motion/react";

export interface UseParallaxTiltOptions {
  maxTilt?: number;
  duration?: number;
  shadowIntensity?: number;
  stiffness?: number;
  damping?: number;
}

export function useParallaxTilt(options: UseParallaxTiltOptions = {}) {
  const {
    maxTilt = 15,
    duration = 0.3,
    shadowIntensity = 0.3,
    stiffness = 300,
    damping = 30,
  } = options;

  const ref = React.useRef<HTMLElement>(null);
  const x = useMotionValue(0);
  const y = useMotionValue(0);

  const mouseXSpring = useSpring(x, { stiffness, damping });
  const mouseYSpring = useSpring(y, { stiffness, damping });

  const rotateX = useTransform(mouseYSpring, [-0.5, 0.5], [maxTilt, -maxTilt]);
  const rotateY = useTransform(mouseXSpring, [-0.5, 0.5], [-maxTilt, maxTilt]);

  const shadowX = useTransform(mouseXSpring, [-0.5, 0.5], [-20, 20]);
  const shadowY = useTransform(mouseYSpring, [-0.5, 0.5], [-20, 20]);

  const handleMouseMove = (e: React.MouseEvent<HTMLElement>) => {
    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 xPct = mouseX / width - 0.5;
    const yPct = mouseY / height - 0.5;
    x.set(xPct);
    y.set(yPct);
  };

  const handleMouseLeave = () => {
    x.set(0);
    y.set(0);
  };

  const tiltProps = {
    style: {
      rotateX,
      rotateY,
      transformStyle: "preserve-3d" as const,
      boxShadow: useTransform(
        [shadowX, shadowY],
        ([x, y]) => `${x}px ${y}px 30px rgba(0, 0, 0, ${shadowIntensity})`,
      ),
    },
    onMouseMove: handleMouseMove,
    onMouseLeave: handleMouseLeave,
    transition: { duration },
  };

  return { tiltProps, ref };
}

Installation

npx shadcn@latest add @animbits/hooks-use-parallax-tilt

Usage

import { HooksUseParallaxTilt } from "@/lib/hooks-use-parallax-tilt"
HooksUseParallaxTilt()