1- const heroAnimation = async ( ) => {
1+ const heroAnimation = async ( animContainer ) => {
22 const isReduceMotionEnabled = window . matchMedia (
33 '(prefers-reduced-motion: reduce)' ,
44 ) . matches
@@ -14,6 +14,10 @@ const heroAnimation = async () => {
1414 } )
1515 }
1616
17+ // Skip to visible portion of animation when cropped on small screens
18+ const { left, width } = animContainer . getClientRects ( ) [ 0 ]
19+ const offScreenDelta = Math . abs ( left ) / width
20+
1721 const heroSwoops = [
1822 {
1923 canvas : document . querySelector ( '#purple-swoop' ) ,
@@ -25,7 +29,7 @@ const heroAnimation = async () => {
2529 lineWidth : 210 ,
2630 debugColor : 'purple' ,
2731 image : null ,
28- state : { progress : 0 } ,
32+ state : { progress : offScreenDelta } ,
2933 } ,
3034 {
3135 canvas : document . querySelector ( '#white-swoop-1' ) ,
@@ -37,7 +41,7 @@ const heroAnimation = async () => {
3741 lineWidth : 140 ,
3842 debugColor : 'red' ,
3943 image : null ,
40- state : { progress : 0 } ,
44+ state : { progress : offScreenDelta } ,
4145 } ,
4246 {
4347 canvas : document . querySelector ( '#white-swoop-2' ) ,
@@ -49,7 +53,7 @@ const heroAnimation = async () => {
4953 lineWidth : 73.6 ,
5054 debugColor : 'cyan' ,
5155 image : null ,
52- state : { progress : 0 } ,
56+ state : { progress : offScreenDelta } ,
5357 } ,
5458 {
5559 canvas : document . querySelector ( '#orange-swoop-bottom' ) ,
@@ -61,7 +65,7 @@ const heroAnimation = async () => {
6165 lineWidth : 202.2 ,
6266 debugColor : 'yellow' ,
6367 image : null ,
64- state : { progress : 0 } ,
68+ state : { progress : offScreenDelta } ,
6569 } ,
6670 {
6771 canvas : document . querySelector ( '#orange-swoop-top' ) ,
@@ -73,7 +77,7 @@ const heroAnimation = async () => {
7377 lineWidth : 163.4 ,
7478 debugColor : 'green' ,
7579 image : null ,
76- state : { progress : 0 } ,
80+ state : { progress : offScreenDelta } ,
7781 } ,
7882 ]
7983 const logo = {
@@ -84,8 +88,9 @@ const heroAnimation = async () => {
8488 position : [ 610 , 672.5 ] ,
8589 imagePath : '/assets/images/landing-page/hero/bird.png' ,
8690 image : null ,
87- state : { progress : 0 } ,
91+ state : { progress : offScreenDelta } ,
8892 }
93+
8994 const initSwoops = ( {
9095 path,
9196 pathLength,
@@ -96,13 +101,12 @@ const heroAnimation = async () => {
96101 image,
97102 } ) => {
98103 const ctx = canvas . getContext ( '2d' )
99- // Convert position value to account for the center anchor point in AE
104+ // The reference animation's transform origin is in the center of the canvas
100105 // We're not going to reset this as it will make pulling values directly from AE easier
101106 ctx . translate ( posX - image . naturalWidth / 2 , posY - image . naturalHeight / 2 )
102107 // Set mask styles
103108 ctx . lineWidth = lineWidth
104109 ctx . lineCap = 'round'
105- // Convert SVG path pulled from AE masks
106110 let pathInstance = new Path2D ( path )
107111
108112 if ( ! isReduceMotionEnabled ) {
@@ -127,13 +131,10 @@ const heroAnimation = async () => {
127131 positionEnd : [ endX , endY ] ,
128132 } ) => {
129133 const ctx = canvas . getContext ( '2d' )
130- // Same reason for conversion as initSwoops
134+ // Applying this conversion for the same purpose as init swoops
131135 ctx . translate ( posX - image . naturalWidth / 2 , posY - image . naturalHeight / 2 )
132136
133- if ( ! isReduceMotionEnabled ) {
134- ctx . globalAlpha = 0
135- ctx . drawImage ( image , 0 , 0 )
136- } else {
137+ if ( isReduceMotionEnabled ) {
137138 ctx . globalAlpha = 1
138139 const deltaX = endX - posX
139140 const deltaY = endY - posY
@@ -144,144 +145,119 @@ const heroAnimation = async () => {
144145 }
145146
146147 try {
147- // load swoop image
148+ // Load swoop images
148149 const swoopImages = await Promise . all (
149150 heroSwoops . map ( ( swoop ) => loadImage ( swoop . imagePath ) ) ,
150151 )
151- // load logo
152+ // Load logo
152153 const logoImage = await loadImage ( logo . imagePath )
153154
154155 logo . image = logoImage
155- // init canvas for each swoop layer
156+ // Init canvas for each swoop layer
156157 heroSwoops . forEach ( ( swoop , i ) => {
157158 swoop . image = swoopImages [ i ]
158159 const canvasData = initSwoops ( swoop )
159160 swoop . ctx = canvasData . ctx
160161 swoop . pathInstance = canvasData . pathInstance
161162 } )
162- // init logo canvas
163+ // Init logo canvas
163164 logo . ctx = initLogo ( logo )
164165 } catch ( error ) {
165166 console . error ( 'Error loading images:' , error )
166167 throw error
167168 }
168169
170+ // Skip animation if reduced motion is enabled
169171 if ( isReduceMotionEnabled ) {
170172 return
171173 }
172174
173- const DURATION = 1000
175+ const DURATION = 1000 - 1000 * offScreenDelta
174176
175177 const tl = anime . createTimeline ( {
176178 defaults : { duration : DURATION , ease : 'inOut(.8)' } ,
177179 } )
178180
179181 tl . label ( 'start' , 0 )
180182
181- // white swoop 1
183+ const swoopUpdate = ( {
184+ state,
185+ ctx,
186+ pathLength,
187+ pathInstance,
188+ image,
189+ canvas,
190+ } ) => {
191+ // Clear canvas before next draw
192+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
193+ // Progress line dash offset
194+ ctx . lineDashOffset = pathLength * ( 1 - state . progress )
195+ // Draw stroke
196+ ctx . stroke ( pathInstance )
197+ // Source-in will allow us to only draw as far as the stroke
198+ ctx . globalCompositeOperation = 'source-in'
199+ ctx . drawImage ( image , 0 , 0 )
200+ // Reset to default for our next stroke paint
201+ ctx . globalCompositeOperation = 'source-out'
202+ }
203+
204+ // White swoop 1
182205 tl . add (
183206 heroSwoops [ 1 ] . state ,
184207 {
185208 progress : 1 ,
186- duration : 950 ,
187- onUpdate : ( ) => {
188- const { state, ctx, pathLength, pathInstance, image, canvas } =
189- heroSwoops [ 1 ]
190- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
191- ctx . lineDashOffset = pathLength * ( 1 - state . progress )
192- ctx . stroke ( pathInstance )
193- ctx . globalCompositeOperation = 'source-in'
194- ctx . drawImage ( image , 0 , 0 )
195- ctx . globalCompositeOperation = 'source-out'
196- } ,
209+ duration : 950 - 950 * offScreenDelta ,
210+ onUpdate : ( ) => swoopUpdate ( heroSwoops [ 1 ] ) ,
197211 } ,
198212 'start' ,
199213 )
200- // // purple swoop
214+ // Purple swoop
201215 tl . add (
202216 heroSwoops [ 0 ] . state ,
203217 {
204218 progress : 1 ,
205- duration : 950 ,
206- onUpdate : ( ) => {
207- const { state, ctx, pathLength, pathInstance, image, canvas } =
208- heroSwoops [ 0 ]
209- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
210- ctx . lineDashOffset = pathLength * ( 1 - state . progress )
211- ctx . stroke ( pathInstance )
212- ctx . globalCompositeOperation = 'source-in'
213- ctx . drawImage ( image , 0 , 0 )
214- ctx . globalCompositeOperation = 'source-out'
215- } ,
219+ duration : 950 - 950 * offScreenDelta ,
220+ onUpdate : ( ) => swoopUpdate ( heroSwoops [ 0 ] ) ,
216221 } ,
217222 'start' ,
218223 )
219- // // white swoop 2 swoop
224+ // White swoop 2
220225 tl . add (
221226 heroSwoops [ 2 ] . state ,
222227 {
223228 progress : 1 ,
224- onUpdate : ( ) => {
225- const { state, ctx, pathLength, pathInstance, image, canvas } =
226- heroSwoops [ 2 ]
227- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
228- ctx . lineDashOffset = pathLength * ( 1 - state . progress )
229- ctx . stroke ( pathInstance )
230- ctx . globalCompositeOperation = 'source-in'
231- ctx . drawImage ( image , 0 , 0 )
232- ctx . globalCompositeOperation = 'source-out'
233- } ,
229+ onUpdate : ( ) => swoopUpdate ( heroSwoops [ 2 ] ) ,
234230 } ,
235231 'start' ,
236232 )
237- // // orange swoop bottom
233+ // Orange swoop bottom
238234 tl . add (
239235 heroSwoops [ 3 ] . state ,
240236 {
241237 progress : 1 ,
242- onUpdate : ( ) => {
243- const { state, ctx, pathLength, pathInstance, image, canvas } =
244- heroSwoops [ 3 ]
245- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
246- ctx . lineDashOffset = pathLength * ( 1 - state . progress )
247- ctx . stroke ( pathInstance )
248- ctx . globalCompositeOperation = 'source-in'
249- ctx . drawImage ( image , 0 , 0 )
250- ctx . globalCompositeOperation = 'source-out'
251- } ,
238+ onUpdate : ( ) => swoopUpdate ( heroSwoops [ 3 ] ) ,
252239 } ,
253240 'start' ,
254241 )
255- // orange top
242+ // Orange top
256243 tl . add (
257244 heroSwoops [ 4 ] . state ,
258245 {
259246 progress : 1 ,
260- // ease: 'inOutQuad',
261- duration : 480 ,
262- delay : 520 ,
263- onUpdate : ( ) => {
264- const { state, ctx, pathLength, pathInstance, image, canvas } =
265- heroSwoops [ 4 ]
266- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
267- ctx . lineDashOffset = pathLength * ( 1 - state . progress )
268- ctx . stroke ( pathInstance )
269- ctx . globalCompositeOperation = 'source-in'
270- ctx . drawImage ( image , 0 , 0 )
271- ctx . globalCompositeOperation = 'source-out'
272- } ,
247+ duration : 480 - 480 * offScreenDelta ,
248+ delay : 520 - 520 * offScreenDelta ,
249+ onUpdate : ( ) => swoopUpdate ( heroSwoops [ 4 ] ) ,
273250 } ,
274251 'start' ,
275252 )
276- // logo
253+ // Logo
277254 tl . add (
278255 logo . state ,
279256 {
280257 ease : 'out(1.1)' ,
281- duration : 200 ,
282- delay : 750 ,
258+ duration : 200 - 200 * offScreenDelta ,
259+ delay : 750 - 750 * offScreenDelta ,
283260 progress : 1 ,
284- // ease: 'inOutQuad',
285261 onUpdate : ( ) => {
286262 const {
287263 state : { progress } ,
@@ -292,6 +268,7 @@ const heroAnimation = async () => {
292268 positionEnd : [ endX , endY ] ,
293269 } = logo
294270 ctx . clearRect ( 0 , 0 , canvas . width , canvas . height )
271+ // Progresses logo opacity from 0 to 1
295272 ctx . globalAlpha = progress
296273 const deltaX = ( endX - startX ) * progress
297274 const deltaY = ( endY - startY ) * progress
@@ -302,10 +279,12 @@ const heroAnimation = async () => {
302279 )
303280}
304281
282+ // Start animation when container is mounted
305283const observer = new MutationObserver ( ( ) => {
306- if ( document . querySelector ( '.animation-container' ) ) {
284+ const animContainer = document . querySelector ( '.animation-container' )
285+ if ( animContainer ) {
307286 observer . disconnect ( )
308- heroAnimation ( )
287+ heroAnimation ( animContainer )
309288 }
310289} )
311290
0 commit comments