since3-website/modules/shared/ui/components/magnetic.tsx
2026-09-25 00:45:33 +02:00

55 lines
1.4 KiB
TypeScript

"use client"
import { useEffect, useRef } from "react"
import { cn } from "@/lib/utils"
/** Pulls its child towards the pointer while hovered. */
export function Magnetic({
children,
strength = 0.35,
className,
}: {
children: React.ReactNode
strength?: number
className?: string
}) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
const el = ref.current
if (!el) return
const fine = window.matchMedia("(hover: hover) and (pointer: fine)").matches
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches
if (!fine || reduced) return
const onMove = (event: PointerEvent) => {
const rect = el.getBoundingClientRect()
const dx = event.clientX - (rect.left + rect.width / 2)
const dy = event.clientY - (rect.top + rect.height / 2)
el.style.translate = `${dx * strength}px ${dy * strength}px`
}
const onLeave = () => {
el.style.translate = "0px 0px"
}
el.addEventListener("pointermove", onMove)
el.addEventListener("pointerleave", onLeave)
return () => {
el.removeEventListener("pointermove", onMove)
el.removeEventListener("pointerleave", onLeave)
}
}, [strength])
return (
<div
ref={ref}
className={cn(
"inline-block transition-[translate] duration-700 ease-out-expo",
className
)}
>
{children}
</div>
)
}