mirror of
https://github.com/Techtonic-Fault/homepage.git
synced 2026-01-23 05:26:30 +00:00
Initial commit
This commit is contained in:
110
components/animations/animate.tsx
Normal file
110
components/animations/animate.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"use client"
|
||||
|
||||
import type React from "react"
|
||||
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { motion, useAnimation, useInView } from "framer-motion"
|
||||
|
||||
type AnimateProps = {
|
||||
children: React.ReactNode
|
||||
animation?: "fadeIn" | "fadeInUp" | "fadeInDown" | "fadeInLeft" | "fadeInRight" | "zoom" | "bounce"
|
||||
delay?: number
|
||||
duration?: number
|
||||
className?: string
|
||||
once?: boolean
|
||||
threshold?: number
|
||||
}
|
||||
|
||||
const animations = {
|
||||
fadeIn: {
|
||||
hidden: { opacity: 0 },
|
||||
visible: { opacity: 1 },
|
||||
},
|
||||
fadeInUp: {
|
||||
hidden: { opacity: 0, y: 50 },
|
||||
visible: { opacity: 1, y: 0 },
|
||||
},
|
||||
fadeInDown: {
|
||||
hidden: { opacity: 0, y: -50 },
|
||||
visible: { opacity: 1, y: 0 },
|
||||
},
|
||||
fadeInLeft: {
|
||||
hidden: { opacity: 0, x: -50 },
|
||||
visible: { opacity: 1, x: 0 },
|
||||
},
|
||||
fadeInRight: {
|
||||
hidden: { opacity: 0, x: 50 },
|
||||
visible: { opacity: 1, x: 0 },
|
||||
},
|
||||
zoom: {
|
||||
hidden: { opacity: 0, scale: 0.8 },
|
||||
visible: { opacity: 1, scale: 1 },
|
||||
},
|
||||
bounce: {
|
||||
hidden: { opacity: 0, y: 50 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
type: "spring",
|
||||
bounce: 0.4,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export function Animate({
|
||||
children,
|
||||
animation = "fadeIn",
|
||||
delay = 0,
|
||||
duration = 0.5,
|
||||
className = "",
|
||||
once = true,
|
||||
threshold = 0.1,
|
||||
}: AnimateProps) {
|
||||
const controls = useAnimation()
|
||||
const ref = useRef(null)
|
||||
const isInView = useInView(ref, { once, threshold })
|
||||
const [shouldReduceMotion, setShouldReduceMotion] = useState(false)
|
||||
|
||||
// Check for prefers-reduced-motion
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)")
|
||||
setShouldReduceMotion(mediaQuery.matches)
|
||||
|
||||
const handleChange = () => setShouldReduceMotion(mediaQuery.matches)
|
||||
mediaQuery.addEventListener("change", handleChange)
|
||||
return () => mediaQuery.removeEventListener("change", handleChange)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (isInView) {
|
||||
controls.start("visible")
|
||||
} else if (!once) {
|
||||
controls.start("hidden")
|
||||
}
|
||||
}, [isInView, controls, once])
|
||||
|
||||
const selectedAnimation = animations[animation]
|
||||
|
||||
// If user prefers reduced motion, use a simpler animation
|
||||
const variants = shouldReduceMotion
|
||||
? {
|
||||
hidden: { opacity: 0 },
|
||||
visible: { opacity: 1 },
|
||||
}
|
||||
: selectedAnimation
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
initial="hidden"
|
||||
animate={controls}
|
||||
variants={variants}
|
||||
transition={{ duration, delay, ease: "easeOut" }}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user