1+ document . querySelectorAll ( '.button' ) . forEach ( button => {
2+
3+ let duration = 3000 ,
4+ svg = button . querySelector ( 'svg' ) ,
5+ svgPath = new Proxy ( {
6+ y : null ,
7+ smoothing : null
8+ } , {
9+ set ( target , key , value ) {
10+ target [ key ] = value ;
11+ if ( target . y !== null && target . smoothing !== null ) {
12+ svg . innerHTML = getPath ( target . y , target . smoothing , null ) ;
13+ }
14+ return true ;
15+ } ,
16+ get ( target , key ) {
17+ return target [ key ] ;
18+ }
19+ } ) ;
20+
21+ button . style . setProperty ( '--duration' , duration ) ;
22+
23+ svgPath . y = 20 ;
24+ svgPath . smoothing = 0 ;
25+
26+ button . addEventListener ( 'click' , e => {
27+
28+ e . preventDefault ( ) ;
29+
30+ if ( ! button . classList . contains ( 'loading' ) ) {
31+
32+ button . classList . add ( 'loading' ) ;
33+
34+ gsap . to ( svgPath , {
35+ smoothing : .3 ,
36+ duration : duration * .065 / 1000
37+ } ) ;
38+
39+ gsap . to ( svgPath , {
40+ y : 12 ,
41+ duration : duration * .265 / 1000 ,
42+ delay : duration * .065 / 1000 ,
43+ ease : Elastic . easeOut . config ( 1.12 , .4 )
44+ } ) ;
45+
46+ setTimeout ( ( ) => {
47+ svg . innerHTML = getPath ( 0 , 0 , [
48+ [ 3 , 14 ] ,
49+ [ 8 , 19 ] ,
50+ [ 21 , 6 ]
51+ ] ) ;
52+ } , duration / 2 ) ;
53+
54+ }
55+
56+ } ) ;
57+
58+ } ) ;
59+
60+ function getPoint ( point , i , a , smoothing ) {
61+ let cp = ( current , previous , next , reverse ) => {
62+ let p = previous || current ,
63+ n = next || current ,
64+ o = {
65+ length : Math . sqrt ( Math . pow ( n [ 0 ] - p [ 0 ] , 2 ) + Math . pow ( n [ 1 ] - p [ 1 ] , 2 ) ) ,
66+ angle : Math . atan2 ( n [ 1 ] - p [ 1 ] , n [ 0 ] - p [ 0 ] )
67+ } ,
68+ angle = o . angle + ( reverse ? Math . PI : 0 ) ,
69+ length = o . length * smoothing ;
70+ return [ current [ 0 ] + Math . cos ( angle ) * length , current [ 1 ] + Math . sin ( angle ) * length ] ;
71+ } ,
72+ cps = cp ( a [ i - 1 ] , a [ i - 2 ] , point , false ) ,
73+ cpe = cp ( point , a [ i - 1 ] , a [ i + 1 ] , true ) ;
74+ return `C ${ cps [ 0 ] } ,${ cps [ 1 ] } ${ cpe [ 0 ] } ,${ cpe [ 1 ] } ${ point [ 0 ] } ,${ point [ 1 ] } ` ;
75+ }
76+
77+ function getPath ( update , smoothing , pointsNew ) {
78+ let points = pointsNew ? pointsNew : [
79+ [ 4 , 12 ] ,
80+ [ 12 , update ] ,
81+ [ 20 , 12 ]
82+ ] ,
83+ d = points . reduce ( ( acc , point , i , a ) => i === 0 ? `M ${ point [ 0 ] } ,${ point [ 1 ] } ` : `${ acc } ${ getPoint ( point , i , a , smoothing ) } ` , '' ) ;
84+ return `<path d="${ d } " />` ;
85+ }
0 commit comments