|
1 |
| -"use client"; |
2 |
| - |
3 |
| -import { motion } from "framer-motion"; |
4 |
| -import React from "react"; |
| 1 | +import { motion, MotionProps, Variants } from "framer-motion"; |
| 2 | +import { |
| 3 | + ComponentPropsWithoutRef, |
| 4 | + ComponentType, |
| 5 | + ElementType, |
| 6 | + ReactNode, |
| 7 | + useMemo, |
| 8 | +} from "react"; |
5 | 9 | import { useInView } from "react-intersection-observer";
|
6 | 10 |
|
7 | 11 | import {
|
8 | 12 | fadeIn,
|
9 |
| - fadeInUp, |
10 | 13 | fadeInDown,
|
11 | 14 | fadeInLeft,
|
12 | 15 | fadeInRight,
|
| 16 | + fadeInUp, |
13 | 17 | scaleAnimation,
|
14 | 18 | } from "./AnimationUtils";
|
15 | 19 |
|
16 |
| -interface AnimatedElementProps { |
17 |
| - children: React.ReactNode; |
18 |
| - className?: string; |
19 |
| - animationType?: "fade" | "fadeUp" | "fadeDown" | "fadeLeft" | "fadeRight" | "scale"; |
| 20 | +type AnimationType = "fade" | "fadeUp" | "fadeDown" | "fadeLeft" | "fadeRight" | "scale"; |
| 21 | + |
| 22 | +const getAnimationVariant = (animationType: AnimationType): Variants => { |
| 23 | + switch (animationType) { |
| 24 | + case "fadeUp": |
| 25 | + return fadeInUp; |
| 26 | + case "fadeDown": |
| 27 | + return fadeInDown; |
| 28 | + case "fadeLeft": |
| 29 | + return fadeInLeft; |
| 30 | + case "fadeRight": |
| 31 | + return fadeInRight; |
| 32 | + case "scale": |
| 33 | + return scaleAnimation; |
| 34 | + default: |
| 35 | + return fadeIn; |
| 36 | + } |
| 37 | +}; |
| 38 | + |
| 39 | +type AnimatedElementProps<T extends ElementType> = { |
| 40 | + children: ReactNode; |
| 41 | + animationType?: AnimationType; |
20 | 42 | delay?: number;
|
21 | 43 | threshold?: number;
|
22 | 44 | rootMargin?: string;
|
23 | 45 | triggerOnce?: boolean;
|
24 |
| - as?: keyof JSX.IntrinsicElements; |
25 | 46 | interactive?: boolean;
|
26 |
| - [key: string]: unknown; |
27 |
| -} |
| 47 | + as?: T; |
| 48 | +} & MotionProps & |
| 49 | + Omit<ComponentPropsWithoutRef<T>, keyof MotionProps>; |
28 | 50 |
|
29 |
| -const AnimatedElement: React.FC<AnimatedElementProps> = ({ |
| 51 | +const AnimatedElement = <T extends ElementType = "div">({ |
30 | 52 | children,
|
31 |
| - className = "", |
32 | 53 | animationType = "fade",
|
33 | 54 | delay = 0,
|
34 |
| - threshold = 0.1, |
35 |
| - rootMargin = "0px 0px -100px 0px", |
| 55 | + threshold = 0.05, |
| 56 | + rootMargin = "0px 0px -20px 0px", // próg wejscia animacji, -20 powoduje ze animacja wchodzi od razu i nie buguje sie |
36 | 57 | triggerOnce = false,
|
37 |
| - as = "div", |
38 | 58 | interactive = false,
|
39 |
| - ...props |
40 |
| -}) => { |
| 59 | + as, |
| 60 | + ...restProps |
| 61 | +}: AnimatedElementProps<T>) => { |
41 | 62 | const { ref, inView } = useInView({
|
42 | 63 | triggerOnce,
|
43 | 64 | threshold,
|
44 | 65 | rootMargin,
|
45 | 66 | });
|
46 | 67 |
|
47 |
| - const getAnimationVariant = () => { |
48 |
| - switch (animationType) { |
49 |
| - case "fadeUp": |
50 |
| - return fadeInUp; |
51 |
| - case "fadeDown": |
52 |
| - return fadeInDown; |
53 |
| - case "fadeLeft": |
54 |
| - return fadeInLeft; |
55 |
| - case "fadeRight": |
56 |
| - return fadeInRight; |
57 |
| - case "scale": |
58 |
| - return scaleAnimation; |
59 |
| - default: |
60 |
| - return fadeIn; |
61 |
| - } |
62 |
| - }; |
63 |
| - |
64 |
| - const animationVariant = getAnimationVariant(); |
| 68 | + const variants = getAnimationVariant(animationType); |
65 | 69 |
|
66 |
| - const filteredProps = props; |
| 70 | + const Component = useMemo(() => motion(as || "div"), [as]) as ComponentType<any>; |
67 | 71 |
|
68 |
| - const commonProps = { |
69 |
| - ref, |
70 |
| - className, |
71 |
| - variants: animationVariant, |
72 |
| - initial: interactive ? "initial" : "hidden", |
73 |
| - animate: inView ? (interactive ? "initial" : "visible") : interactive ? "initial" : "hidden", |
74 |
| - whileHover: interactive ? "hover" : undefined, |
75 |
| - whileTap: interactive ? "tap" : undefined, |
76 |
| - transition: { delay }, |
77 |
| - ...filteredProps, |
78 |
| - }; |
79 |
| - |
80 |
| - switch (as) { |
81 |
| - case "div": |
82 |
| - return <motion.div {...commonProps}>{children}</motion.div>; |
83 |
| - case "section": |
84 |
| - return <motion.section {...commonProps}>{children}</motion.section>; |
85 |
| - case "article": |
86 |
| - return <motion.article {...commonProps}>{children}</motion.article>; |
87 |
| - case "main": |
88 |
| - return <motion.main {...commonProps}>{children}</motion.main>; |
89 |
| - case "header": |
90 |
| - return <motion.header {...commonProps}>{children}</motion.header>; |
91 |
| - case "footer": |
92 |
| - return <motion.footer {...commonProps}>{children}</motion.footer>; |
93 |
| - case "nav": |
94 |
| - return <motion.nav {...commonProps}>{children}</motion.nav>; |
95 |
| - case "aside": |
96 |
| - return <motion.aside {...commonProps}>{children}</motion.aside>; |
97 |
| - case "span": |
98 |
| - return <motion.span {...commonProps}>{children}</motion.span>; |
99 |
| - case "p": |
100 |
| - return <motion.p {...commonProps}>{children}</motion.p>; |
101 |
| - case "h1": |
102 |
| - return <motion.h1 {...commonProps}>{children}</motion.h1>; |
103 |
| - case "h2": |
104 |
| - return <motion.h2 {...commonProps}>{children}</motion.h2>; |
105 |
| - case "h3": |
106 |
| - return <motion.h3 {...commonProps}>{children}</motion.h3>; |
107 |
| - case "h4": |
108 |
| - return <motion.h4 {...commonProps}>{children}</motion.h4>; |
109 |
| - case "h5": |
110 |
| - return <motion.h5 {...commonProps}>{children}</motion.h5>; |
111 |
| - case "h6": |
112 |
| - return <motion.h6 {...commonProps}>{children}</motion.h6>; |
113 |
| - case "ul": |
114 |
| - return <motion.ul {...commonProps}>{children}</motion.ul>; |
115 |
| - case "ol": |
116 |
| - return <motion.ol {...commonProps}>{children}</motion.ol>; |
117 |
| - case "li": |
118 |
| - return <motion.li {...commonProps}>{children}</motion.li>; |
119 |
| - default: |
120 |
| - return <motion.div {...commonProps}>{children}</motion.div>; |
121 |
| - } |
| 72 | + return ( |
| 73 | + <Component |
| 74 | + ref={ref} |
| 75 | + variants={variants} |
| 76 | + initial={interactive ? "initial" : "hidden"} |
| 77 | + animate={interactive ? "initial" : inView ? "visible" : "hidden"} |
| 78 | + whileHover={interactive ? "hover" : undefined} |
| 79 | + whileTap={interactive ? "tap" : undefined} |
| 80 | + transition={{ delay }} |
| 81 | + {...restProps} |
| 82 | + > |
| 83 | + {children} |
| 84 | + </Component> |
| 85 | + ); |
122 | 86 | };
|
123 | 87 |
|
124 | 88 | export default AnimatedElement;
|
0 commit comments