121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
'use client'
|
|
|
|
import { motion, useMotionValue, useSpring, useTransform } from 'framer-motion'
|
|
import { useRef, ReactNode } from 'react'
|
|
|
|
interface MagneticButtonProps {
|
|
children: ReactNode
|
|
className?: string
|
|
onClick?: () => void
|
|
strength?: number
|
|
}
|
|
|
|
export function MagneticButton({
|
|
children,
|
|
className = '',
|
|
onClick,
|
|
strength = 0.3
|
|
}: MagneticButtonProps) {
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
|
|
const x = useMotionValue(0)
|
|
const y = useMotionValue(0)
|
|
|
|
const springConfig = { stiffness: 300, damping: 20 }
|
|
const springX = useSpring(x, springConfig)
|
|
const springY = useSpring(y, springConfig)
|
|
|
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
if (!ref.current) return
|
|
|
|
const rect = ref.current.getBoundingClientRect()
|
|
const centerX = rect.left + rect.width / 2
|
|
const centerY = rect.top + rect.height / 2
|
|
|
|
const deltaX = (e.clientX - centerX) * strength
|
|
const deltaY = (e.clientY - centerY) * strength
|
|
|
|
x.set(deltaX)
|
|
y.set(deltaY)
|
|
}
|
|
|
|
const handleMouseLeave = () => {
|
|
x.set(0)
|
|
y.set(0)
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
ref={ref}
|
|
onMouseMove={handleMouseMove}
|
|
onMouseLeave={handleMouseLeave}
|
|
onClick={onClick}
|
|
style={{ x: springX, y: springY }}
|
|
className={`inline-block ${className}`}
|
|
>
|
|
{children}
|
|
</motion.div>
|
|
)
|
|
}
|
|
|
|
interface SectionDividerProps {
|
|
variant?: 'wave' | 'diagonal' | 'curve'
|
|
fromColor?: string
|
|
toColor?: string
|
|
flip?: boolean
|
|
}
|
|
|
|
export function SectionDivider({
|
|
variant = 'wave',
|
|
fromColor = 'section-bg-3',
|
|
toColor = 'section-bg-4',
|
|
flip = false
|
|
}: SectionDividerProps) {
|
|
if (variant === 'wave') {
|
|
return (
|
|
<div className={`w-full h-20 -mt-1 overflow-hidden ${flip ? 'rotate-180' : ''}`}>
|
|
<svg
|
|
viewBox="0 0 1200 120"
|
|
preserveAspectRatio="none"
|
|
className="w-full h-full"
|
|
>
|
|
<path
|
|
d="M0,0 Q300,80 600,40 T1200,0 L1200,120 L0,120 Z"
|
|
fill={`hsl(var(--${toColor}))`}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (variant === 'diagonal') {
|
|
return (
|
|
<div
|
|
className={`w-full h-16 ${flip ? '-skew-y-2' : 'skew-y-2'}`}
|
|
style={{
|
|
background: `linear-gradient(to bottom right, hsl(var(--${fromColor})), hsl(var(--${toColor})))`
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
|
|
if (variant === 'curve') {
|
|
return (
|
|
<div className={`w-full h-24 -mt-1 overflow-hidden ${flip ? 'rotate-180' : ''}`}>
|
|
<svg
|
|
viewBox="0 0 1200 120"
|
|
preserveAspectRatio="none"
|
|
className="w-full h-full"
|
|
>
|
|
<path
|
|
d="M0,60 Q300,120 600,60 T1200,60 L1200,120 L0,120 Z"
|
|
fill={`hsl(var(--${toColor}))`}
|
|
/>
|
|
</svg>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return null
|
|
}
|