import { Override, useAnimation } from "framer"
import { useRef, useEffect, useState } from "react"
export function ParallaxTiltOnHover(): Override {
const controls = useAnimation()
const ref = useRef<HTMLDivElement>(null)
const rafRef = useRef<number | null>(null)
const state = useRef({
isHovering: false,
mouseX: 0,
mouseY: 0,
})
const update = () => {
if (!ref.current || !state.current.isHovering) return
const bounds = ref.current.getBoundingClientRect()
const x = state.current.mouseX - bounds.left
const y = state.current.mouseY - bounds.top
const centerX = bounds.width / 2
const centerY = bounds.height / 2
const offsetX = (centerX - x) / 20
const offsetY = (centerY - y) / 20
const rotateY = (x - centerX) / 25
const rotateX = (centerY - y) / 25
controls.start({
x: offsetX,
y: offsetY,
rotateX,
rotateY,
transition: { ease: "ease-in-out", duration: 0.1 },
})
rafRef.current = requestAnimationFrame(update)
}
const onPointerMove = (e: React.PointerEvent) => {
state.current.mouseX = e.clientX
state.current.mouseY = e.clientY
if (!state.current.isHovering) {
state.current.isHovering = true
rafRef.current = requestAnimationFrame(update)
}
}
const onPointerLeave = () => {
state.current.isHovering = false
if (rafRef.current) cancelAnimationFrame(rafRef.current)
controls.start({
x: 0,
y: 0,
rotateX: 0,
rotateY: 0,
transition: { ease: "linear", duration: 0.2 },
})
}
useEffect(() => {
return () => {
if (rafRef.current) cancelAnimationFrame(rafRef.current)
}
}, [])
return {
ref,
animate: controls,
onPointerMove,
onPointerLeave,
style: {
transformPerspective: 800,
},
}
}