Files
dukang_official/src/components/Reveal.tsx
T
2026-08-11 01:44:32 +08:00

49 lines
1.2 KiB
TypeScript

import { useEffect, useRef, type ReactNode } from 'react'
type RevealProps = {
children: ReactNode
className?: string
/** Custom animation class applied when in view, e.g. anim-slide-left */
variant?: string
delayMs?: number
}
export function Reveal({ children, className = '', variant = 'anim-rise', delayMs = 0 }: RevealProps) {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
const node = ref.current
if (!node) return
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
node.classList.add('is-in')
return
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
node.classList.add('is-in')
observer.unobserve(node)
}
})
},
{ threshold: 0.16, rootMargin: '0px 0px -6% 0px' },
)
observer.observe(node)
return () => observer.disconnect()
}, [])
return (
<div
ref={ref}
className={`reveal-base ${variant} ${className}`.trim()}
style={delayMs ? { transitionDelay: `${delayMs}ms` } : undefined}
>
{children}
</div>
)
}