@@ -19,13 +19,10 @@ export function BackgroundAnimation() {
1919
2020 const canvas = canvasRef . current
2121
22- const morphDuration = 4000
22+ let morphDuration = 2000
2323 const waitDuration = 1000 * 60 * 2
2424
25- function easeInOutCubic ( t : number , b : number , c : number , d : number ) {
26- if ( ( t /= d / 2 ) < 1 ) return ( c / 2 ) * t * t * t + b
27- return ( c / 2 ) * ( ( t -= 2 ) * t * t + 2 ) + b
28- }
25+ const easingFn = cubicBezier ( 0.645 , 0.045 , 0.355 , 1.0 )
2926
3027 if ( canvas ) {
3128 const ctx = canvas . getContext ( '2d' ) !
@@ -67,22 +64,34 @@ export function BackgroundAnimation() {
6764 return array
6865 }
6966
70- let currentBlobs = createBlobs ( )
71- let interBlobs = currentBlobs
67+ let startBlobs = createBlobs ( )
68+ let currentBlobs = startBlobs
7269 let targetBlobs : ReturnType < typeof createBlobs > = [ ]
7370
71+ function resizeHandler ( ) {
72+ // Create an offscreen canvas and copy the current content
73+ const offscreen = document . createElement ( 'canvas' )
74+ offscreen . width = canvas ! . width
75+ offscreen . height = canvas ! . height
76+ offscreen . getContext ( '2d' ) ! . drawImage ( canvas ! , 0 , 0 )
77+
78+ // Resize the main canvas
79+ canvas ! . width = window . innerWidth
80+ canvas ! . height = window . innerHeight
81+
82+ // Stretch and redraw the saved content to fill the new size
83+ ctx . drawImage ( offscreen , 0 , 0 , canvas ! . width , canvas ! . height )
84+ }
85+
7486 function start ( ) {
7587 if ( timeout ) {
7688 clearTimeout ( timeout )
7789 }
7890 if ( rafId ) {
7991 cancelAnimationFrame ( rafId )
8092 }
81- const parent = canvas ! . parentElement
82- canvas ! . width = parent ! . clientWidth
83- canvas ! . height = parent ! . clientHeight
8493
85- currentBlobs = interBlobs
94+ startBlobs = JSON . parse ( JSON . stringify ( currentBlobs ) )
8695 targetBlobs = createBlobs ( )
8796 startTime = performance . now ( )
8897 animate ( )
@@ -92,45 +101,64 @@ export function BackgroundAnimation() {
92101 ctx . clearRect ( 0 , 0 , canvas ! . width , canvas ! . height )
93102
94103 const time = performance . now ( ) - startTime
95- const progress = easeInOutCubic ( time , 0 , 1 , morphDuration )
104+ const progress = time / morphDuration
105+ const easedProgress = easingFn ( progress )
96106
97107 // Draw the blobs
98- currentBlobs . forEach ( ( blob , i ) => {
108+ startBlobs . forEach ( ( startBlob , i ) => {
99109 const targetBlob = targetBlobs [ i ]
100- interBlobs [ i ] . x = blob . x + ( targetBlob . x - blob . x ) * progress
101- interBlobs [ i ] . y = blob . y + ( targetBlob . y - blob . y ) * progress
110+
111+ currentBlobs [ i ] . x = interpolate (
112+ startBlob . x ,
113+ targetBlob . x ,
114+ easedProgress
115+ )
116+ currentBlobs [ i ] . y = interpolate (
117+ startBlob . y ,
118+ targetBlob . y ,
119+ easedProgress
120+ )
102121
103122 const gradient = ctx . createRadialGradient (
104- interBlobs [ i ] . x ,
105- interBlobs [ i ] . y ,
123+ currentBlobs [ i ] . x ,
124+ currentBlobs [ i ] . y ,
106125 0 ,
107- interBlobs [ i ] . x ,
108- interBlobs [ i ] . y ,
109- interBlobs [ i ] . r
126+ currentBlobs [ i ] . x ,
127+ currentBlobs [ i ] . y ,
128+ currentBlobs [ i ] . r
110129 )
111130
112- interBlobs [ i ] . colorH =
113- blob . colorH + ( targetBlob . colorH - blob . colorH ) * progress
114- interBlobs [ i ] . colorS =
115- blob . colorS + ( targetBlob . colorS - blob . colorS ) * progress
116- interBlobs [ i ] . colorL =
117- blob . colorL + ( targetBlob . colorL - blob . colorL ) * progress
131+ currentBlobs [ i ] . colorH = interpolate (
132+ startBlob . colorH ,
133+ targetBlob . colorH ,
134+ easedProgress
135+ )
136+ currentBlobs [ i ] . colorS = interpolate (
137+ startBlob . colorS ,
138+ targetBlob . colorS ,
139+ easedProgress
140+ )
141+ currentBlobs [ i ] . colorL = interpolate (
142+ startBlob . colorL ,
143+ targetBlob . colorL ,
144+ easedProgress
145+ )
118146
119147 gradient . addColorStop (
120148 0 ,
121- `hsla(${ interBlobs [ i ] . colorH } , ${ interBlobs [ i ] . colorS } %, ${ interBlobs [ i ] . colorL } %, 1)`
149+ `hsla(${ currentBlobs [ i ] . colorH } , ${ currentBlobs [ i ] . colorS } %, ${ currentBlobs [ i ] . colorL } %, 1)`
122150 )
123151 gradient . addColorStop (
124152 1 ,
125- `hsla(${ interBlobs [ i ] . colorH } , ${ interBlobs [ i ] . colorS } %, ${ interBlobs [ i ] . colorL } %, 0)`
153+ `hsla(${ currentBlobs [ i ] . colorH } , ${ currentBlobs [ i ] . colorS } %, ${ currentBlobs [ i ] . colorL } %, 0)`
126154 )
127155
128156 ctx . fillStyle = gradient
129157 ctx . beginPath ( )
130158 ctx . arc (
131- interBlobs [ i ] . x ,
132- interBlobs [ i ] . y ,
133- interBlobs [ i ] . r ,
159+ currentBlobs [ i ] . x ,
160+ currentBlobs [ i ] . y ,
161+ currentBlobs [ i ] . r ,
134162 0 ,
135163 Math . PI * 2
136164 )
@@ -141,13 +169,15 @@ export function BackgroundAnimation() {
141169 rafId = requestAnimationFrame ( animate )
142170 } else {
143171 timeout = setTimeout ( ( ) => {
172+ morphDuration = 4000
144173 start ( )
145174 } , waitDuration )
146175 }
147176 }
148177
178+ resizeHandler ( )
149179 start ( )
150- window . addEventListener ( 'resize' , start )
180+ window . addEventListener ( 'resize' , resizeHandler )
151181
152182 return ( ) => {
153183 if ( rafId ) {
@@ -156,7 +186,7 @@ export function BackgroundAnimation() {
156186 if ( timeout ) {
157187 clearTimeout ( timeout )
158188 }
159- window . removeEventListener ( 'resize' , start )
189+ window . removeEventListener ( 'resize' , resizeHandler )
160190 }
161191 }
162192 } , [ prefersReducedMotion ] )
@@ -178,3 +208,24 @@ export function BackgroundAnimation() {
178208 </ div >
179209 )
180210}
211+
212+ function cubicBezier ( p1x : number , p1y : number , p2x : number , p2y : number ) {
213+ return function ( t : number ) {
214+ const cx = 3 * p1x
215+ const bx = 3 * ( p2x - p1x ) - cx
216+ const ax = 1 - cx - bx
217+
218+ const cy = 3 * p1y
219+ const by = 3 * ( p2y - p1y ) - cy
220+ const ay = 1 - cy - by
221+
222+ const x = ( ( ax * t + bx ) * t + cx ) * t
223+ const y = ( ( ay * t + by ) * t + cy ) * t
224+
225+ return y
226+ }
227+ }
228+
229+ function interpolate ( start : number , end : number , progress : number ) {
230+ return start + ( end - start ) * progress
231+ }
0 commit comments