codechillrepeat

Magnetic

Oct 1st, 2026 (edited)
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 2.74 KB | Source Code | 0 0
  1. import { forwardRef, useEffect, useRef, type ComponentType } from "react"
  2. import { useMotionValue, useSpring } from "framer-motion"
  3. import { RenderTarget } from "framer"
  4.  
  5. /* ───────────── SETTINGS (tweak these) ───────────── */
  6.  
  7. const MAGNET = {
  8.     strength: 0.35, // 0–1, how far the element follows the cursor
  9.     radius: 120, // px around the element where the pull begins
  10.     spring: { stiffness: 200, damping: 15, mass: 0.2 },
  11. }
  12.  
  13. /* ───────────── Helpers ───────────── */
  14.  
  15. const isCanvas = () => RenderTarget.current() === RenderTarget.canvas
  16.  
  17. function mergeRefs(...refs: any[]) {
  18.     return (node: any) => {
  19.         refs.forEach((r) => {
  20.             if (typeof r === "function") r(node)
  21.             else if (r) r.current = node
  22.         })
  23.     }
  24. }
  25.  
  26. /* ───────────── Override: Magnetic element ─────────────
  27.    Apply to a button, card, logo, etc. */
  28.  
  29. export function withMagnetic(Component: ComponentType<any>): ComponentType<any> {
  30.     return forwardRef<any, any>((props, ref) => {
  31.         const el = useRef<HTMLElement | null>(null)
  32.         const x = useMotionValue(0)
  33.         const y = useMotionValue(0)
  34.         const sx = useSpring(x, MAGNET.spring)
  35.         const sy = useSpring(y, MAGNET.spring)
  36.  
  37.         useEffect(() => {
  38.             if (isCanvas()) return
  39.  
  40.             const onMove = (e: MouseEvent) => {
  41.                 const node = el.current
  42.                 if (!node) return
  43.                 const r = node.getBoundingClientRect()
  44.                 const cx = r.left + r.width / 2
  45.                 const cy = r.top + r.height / 2
  46.                 const dx = e.clientX - cx
  47.                 const dy = e.clientY - cy
  48.                 const reach = MAGNET.radius + Math.max(r.width, r.height) / 2
  49.  
  50.                 if (Math.hypot(dx, dy) < reach) {
  51.                     x.set(dx * MAGNET.strength)
  52.                     y.set(dy * MAGNET.strength)
  53.                 } else {
  54.                     x.set(0)
  55.                     y.set(0)
  56.                 }
  57.             }
  58.             const reset = () => {
  59.                 x.set(0)
  60.                 y.set(0)
  61.             }
  62.  
  63.             window.addEventListener("mousemove", onMove)
  64.             document.addEventListener("mouseleave", reset)
  65.             return () => {
  66.                 window.removeEventListener("mousemove", onMove)
  67.                 document.removeEventListener("mouseleave", reset)
  68.             }
  69.         }, [])
  70.  
  71.         return (
  72.             <Component
  73.                 ref={mergeRefs(ref, el)}
  74.                 {...props}
  75.                 style={{ ...props.style, x: sx, y: sy }}
  76.             />
  77.         )
  78.     })
  79. }
Advertisement
Add Comment
Please, Sign In to add comment