Blue Reveal

Mysterious blur reveal animation, just feels awesome.

The fog slowly faded away as the wind blew.

TSX
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
"use client"; import { AnimatePresence, motion } from "motion/react"; import type React from "react"; export interface BlurRevealProps { children: string; className?: string; delay?: number; speedReveal?: number; speedSegment?: number; trigger?: boolean; onAnimationComplete?: () => void; onAnimationStart?: () => void; as?: keyof React.JSX.IntrinsicElements; style?: React.CSSProperties; inView?: boolean; once?: boolean; letterSpacing?: string | number; } const BlurReveal = ({ children, className, delay = 0, speedReveal = 1.5, speedSegment = 0.5, trigger = true, onAnimationComplete, onAnimationStart, as = "p", style, inView = false, once = true, letterSpacing, }: BlurRevealProps) => { const MotionTag = motion[as as keyof typeof motion] as typeof motion.div; const stagger = 0.03 / speedReveal; const baseDuration = 0.3 / speedSegment; const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: stagger, delayChildren: delay, }, }, exit: { transition: { staggerChildren: stagger, staggerDirection: -1, }, }, }; const itemVariants = { hidden: { opacity: 0, filter: "blur(12px)", y: 10 }, visible: { opacity: 1, filter: "blur(0px)", y: 0, transition: { duration: baseDuration, }, }, exit: { opacity: 0, filter: "blur(12px)", y: 10 }, }; return ( <AnimatePresence mode="popLayout"> {trigger && ( <MotionTag initial="hidden" whileInView={inView ? "visible" : undefined} animate={inView ? undefined : "visible"} exit="exit" variants={containerVariants} viewport={{ once }} className={className} onAnimationComplete={onAnimationComplete} onAnimationStart={onAnimationStart} style={style} > <span className="sr-only">{children}</span> {children && children.split(" ").map((word, wordIndex, wordsArray) => ( <span key={`word-${wordIndex}`} className="inline-block whitespace-nowrap" aria-hidden="true" > {word.split("").map((char, charIndex) => ( <motion.span key={`char-${wordIndex}-${charIndex}`} variants={itemVariants} className="inline-block" style={ letterSpacing ? { marginRight: letterSpacing } : undefined } > {char} </motion.span> ))} {wordIndex < wordsArray.length - 1 && ( <motion.span key={`space-${wordIndex}`} variants={itemVariants} className="inline-block" > &nbsp; </motion.span> )} </span> ))} </MotionTag> )} </AnimatePresence> ); }; export default BlurReveal;

Installation

pnpm dlx shadcn@latest add @satoriui/blur-reveal