1
1
import React , {
2
2
useState ,
3
+ useCallback ,
3
4
useEffect ,
5
+ useLayoutEffect ,
4
6
useContext ,
5
- useMemo ,
6
7
useRef ,
8
+ useMemo ,
7
9
createContext
8
10
} from 'react'
9
11
@@ -13,102 +15,113 @@ import { generateRandomNumber } from '../utils/generateRandomNumber'
13
15
import styles from './index.css'
14
16
15
17
/** ====================================
16
- * 🔰Hook
17
- Hook for Animation
18
- ==================================== **/
18
+ * 🔰Hook
19
+ Hook for Animation
20
+ ==================================== **/
19
21
20
- const useClapAnimation = ( { duration : tlDuration } ) => {
22
+ const useClapAnimation = ( {
23
+ duration : tlDuration ,
24
+ bounceEl,
25
+ fadeEl,
26
+ burstEl
27
+ } ) => {
21
28
const [ animationTimeline , setAnimationTimeline ] = useState (
22
29
new mojs . Timeline ( )
23
30
)
24
31
25
- useEffect (
26
- ( ) => {
27
- const triangleBurst = new mojs . Burst ( {
28
- parent : '#clap' ,
29
- radius : { 50 : 95 } ,
30
- count : 5 ,
31
- angle : 30 ,
32
- children : {
33
- shape : 'polygon' ,
34
- radius : { 6 : 0 } ,
35
- scale : 1 ,
36
- stroke : 'rgba(211,84,0 ,0.5)' ,
37
- strokeWidth : 2 ,
38
- angle : 210 ,
39
- delay : 30 ,
40
- speed : 0.2 ,
41
- easing : mojs . easing . bezier ( 0.1 , 1 , 0.3 , 1 ) ,
42
- duration : tlDuration
43
- }
44
- } )
45
-
46
- const circleBurst = new mojs . Burst ( {
47
- parent : '#clap' ,
48
- radius : { 50 : 75 } ,
49
- angle : 25 ,
50
- duration : tlDuration ,
51
- children : {
52
- shape : 'circle' ,
53
- fill : 'rgba(149,165,166 ,0.5)' ,
54
- delay : 30 ,
55
- speed : 0.2 ,
56
- radius : { 3 : 0 } ,
57
- easing : mojs . easing . bezier ( 0.1 , 1 , 0.3 , 1 )
58
- }
59
- } )
60
-
61
- const countAnimation = new mojs . Html ( {
62
- el : '#clapCount' ,
63
- isShowStart : false ,
64
- isShowEnd : true ,
65
- y : { 0 : - 30 } ,
66
- opacity : { 0 : 1 } ,
32
+ useLayoutEffect ( ( ) => {
33
+ if ( ! bounceEl || ! fadeEl || ! burstEl ) {
34
+ return
35
+ }
36
+
37
+ const triangleBurst = new mojs . Burst ( {
38
+ parent : burstEl ,
39
+ radius : { 50 : 95 } ,
40
+ count : 5 ,
41
+ angle : 30 ,
42
+ children : {
43
+ shape : 'polygon' ,
44
+ radius : { 6 : 0 } ,
45
+ scale : 1 ,
46
+ stroke : 'rgba(211,84,0 ,0.5)' ,
47
+ strokeWidth : 2 ,
48
+ angle : 210 ,
49
+ delay : 30 ,
50
+ speed : 0.2 ,
51
+ easing : mojs . easing . bezier ( 0.1 , 1 , 0.3 , 1 ) ,
67
52
duration : tlDuration
68
- } ) . then ( {
69
- opacity : { 1 : 0 } ,
70
- y : - 80 ,
71
- delay : tlDuration / 2
72
- } )
73
-
74
- const countTotalAnimation = new mojs . Html ( {
75
- el : '#clapCountTotal' ,
76
- isShowStart : false ,
77
- isShowEnd : true ,
78
- opacity : { 0 : 1 } ,
79
- delay : ( 3 * tlDuration ) / 2 ,
80
- duration : tlDuration ,
81
- y : { 0 : - 3 }
82
- } )
83
-
84
- const scaleButton = new mojs . Html ( {
85
- el : '#clap' ,
86
- duration : tlDuration ,
87
- scale : { 1.3 : 1 } ,
88
- easing : mojs . easing . out
89
- } )
90
-
91
- const clap = document . getElementById ( 'clap' )
53
+ }
54
+ } )
55
+
56
+ const circleBurst = new mojs . Burst ( {
57
+ parent : burstEl ,
58
+ radius : { 50 : 75 } ,
59
+ angle : 25 ,
60
+ duration : tlDuration ,
61
+ children : {
62
+ shape : 'circle' ,
63
+ fill : 'rgba(149,165,166 ,0.5)' ,
64
+ delay : 30 ,
65
+ speed : 0.2 ,
66
+ radius : { 3 : 0 } ,
67
+ easing : mojs . easing . bezier ( 0.1 , 1 , 0.3 , 1 )
68
+ }
69
+ } )
70
+
71
+ const countAnimation = new mojs . Html ( {
72
+ el : bounceEl ,
73
+ isShowStart : false ,
74
+ isShowEnd : true ,
75
+ y : { 0 : - 30 } ,
76
+ opacity : { 0 : 1 } ,
77
+ duration : tlDuration
78
+ } ) . then ( {
79
+ opacity : { 1 : 0 } ,
80
+ y : - 80 ,
81
+ delay : tlDuration / 2
82
+ } )
83
+
84
+ const countTotalAnimation = new mojs . Html ( {
85
+ el : fadeEl ,
86
+ isShowStart : false ,
87
+ isShowEnd : true ,
88
+ opacity : { 0 : 1 } ,
89
+ delay : ( 3 * tlDuration ) / 2 ,
90
+ duration : tlDuration ,
91
+ y : { 0 : - 3 }
92
+ } )
93
+
94
+ const scaleButton = new mojs . Html ( {
95
+ el : burstEl ,
96
+ duration : tlDuration ,
97
+ scale : { 1.3 : 1 } ,
98
+ easing : mojs . easing . out
99
+ } )
100
+
101
+ if ( typeof burstEl === 'string' ) {
92
102
clap . style . transform = 'scale(1, 1)'
103
+ const el = document . getElementById ( id )
104
+ el . style . transform = 'scale(1, 1)'
105
+ } else {
106
+ burstEl . style . transform = 'scale(1, 1)'
107
+ }
93
108
94
- const updatedAnimationTimeline = animationTimeline . add ( [
95
- countAnimation ,
96
- countTotalAnimation ,
97
- scaleButton ,
98
- circleBurst ,
99
- triangleBurst
100
- ] )
101
-
102
- setAnimationTimeline ( updatedAnimationTimeline )
103
- } ,
104
- [ tlDuration , animationTimeline ]
105
- )
109
+ const updatedAnimationTimeline = animationTimeline . add ( [
110
+ countAnimation ,
111
+ countTotalAnimation ,
112
+ scaleButton ,
113
+ circleBurst ,
114
+ triangleBurst
115
+ ] )
116
+
117
+ setAnimationTimeline ( updatedAnimationTimeline )
118
+ } , [ tlDuration , animationTimeline , bounceEl , fadeEl , burstEl ] )
106
119
107
120
return animationTimeline
108
121
}
109
122
/** ====================================
110
- * 🔰 MediumClap
111
- ==================================== **/
123
+ * 🔰 MediumClap
124
+ ==================================== **/
112
125
const initialState = {
113
126
count : 0 ,
114
127
countTotal : generateRandomNumber ( 500 , 10000 ) ,
@@ -123,7 +136,24 @@ const MediumClap = ({ children, onClap }) => {
123
136
const [ clapState , setClapState ] = useState ( initialState )
124
137
const { count, countTotal, isClicked } = clapState
125
138
126
- const animationTimeline = useClapAnimation ( { duration : 300 } )
139
+ const [ { clapRef, clapCountRef, clapTotalRef } , setRefState ] = useState ( { } )
140
+
141
+ const setRef = useCallback ( node => {
142
+ if ( node !== null ) {
143
+ setRefState ( prevRefState => ( {
144
+ ...prevRefState ,
145
+ [ node . dataset . refkey ] : node
146
+ } ) )
147
+ }
148
+ } , [ ] )
149
+
150
+ const animationTimeline = useClapAnimation ( {
151
+ duration : 300 ,
152
+ bounceEl : clapCountRef ,
153
+ fadeEl : clapTotalRef ,
154
+ burstEl : clapRef
155
+ } )
156
+
127
157
const handleClapClick = ( ) => {
128
158
// 👉 prop from HOC
129
159
animationTimeline . replay ( )
@@ -137,38 +167,41 @@ const MediumClap = ({ children, onClap }) => {
137
167
138
168
const componentJustMounted = useRef ( true )
139
169
140
- useEffect (
141
- ( ) => {
142
- if ( ! componentJustMounted . current ) {
143
- onClap ( clapState )
144
- }
145
- componentJustMounted . current = false
146
- } ,
147
- [ count , onClap ]
148
- )
170
+ useEffect ( ( ) => {
171
+ if ( ! componentJustMounted . current ) {
172
+ onClap ( clapState )
173
+ }
174
+ componentJustMounted . current = false
175
+ } , [ count , onClap ] )
149
176
150
177
const memoizedValue = useMemo (
151
178
( ) => ( {
152
179
count,
153
180
countTotal,
154
- isClicked
181
+ isClicked,
182
+ setRef
155
183
} ) ,
156
- [ count , countTotal , isClicked ]
184
+ [ count , countTotal , isClicked , setRef ]
157
185
)
158
186
159
187
return (
160
188
< Provider value = { memoizedValue } >
161
- < button id = 'clap' className = { styles . clap } onClick = { handleClapClick } >
189
+ < button
190
+ ref = { setRef }
191
+ data-refkey = 'clapRef'
192
+ className = { styles . clap }
193
+ onClick = { handleClapClick }
194
+ >
162
195
{ children }
163
196
</ button >
164
197
</ Provider >
165
198
)
166
199
}
167
200
168
201
/** ====================================
169
- * 🔰SubComponents
170
- Smaller Component used by <MediumClap />
171
- ==================================== **/
202
+ * 🔰SubComponents
203
+ Smaller Component used by <MediumClap />
204
+ ==================================== **/
172
205
173
206
const ClapIcon = ( ) => {
174
207
const { isClicked } = useContext ( MediumClapContext )
@@ -187,17 +220,17 @@ const ClapIcon = () => {
187
220
)
188
221
}
189
222
const ClapCount = ( ) => {
190
- const { count } = useContext ( MediumClapContext )
223
+ const { count, setRef } = useContext ( MediumClapContext )
191
224
return (
192
- < span id = 'clapCount ' className = { styles . count } >
225
+ < span ref = { setRef } data-refkey = 'clapCountRef ' className = { styles . count } >
193
226
+{ count }
194
227
</ span >
195
228
)
196
229
}
197
230
const CountTotal = ( ) => {
198
- const { countTotal } = useContext ( MediumClapContext )
231
+ const { countTotal, setRef } = useContext ( MediumClapContext )
199
232
return (
200
- < span id = 'clapCountTotal ' className = { styles . total } >
233
+ < span ref = { setRef } data-refkey = 'clapTotalRef ' className = { styles . total } >
201
234
{ countTotal }
202
235
</ span >
203
236
)
@@ -218,10 +251,10 @@ MediumClap.Total = CountTotal
218
251
MediumClap . Info = ClapInfo
219
252
220
253
/** ====================================
221
- * 🔰USAGE
222
- Below's how a potential user
223
- may consume the component API
224
- ==================================== **/
254
+ * 🔰USAGE
255
+ Below's how a potential user
256
+ may consume the component API
257
+ ==================================== **/
225
258
226
259
const Usage = ( ) => {
227
260
const [ total , setTotal ] = useState ( 0 )
0 commit comments