-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.js
466 lines (402 loc) · 15.3 KB
/
script.js
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
/*Register plugins for GSAP library*/
gsap.registerPlugin(ScrollToPlugin);
gsap.registerPlugin(TextPlugin);
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.config({ // Ensures ScrollTrigger refreshes on resize or visibility change to prevent scroll from becoming unresponsive on iPads https://gsap.com/docs/v3/Plugins/ScrollTrigger/static.config()/
autoRefreshEvents: "resize,visibilitychange"
});
//Creating a function that adjusts the viewbox of arrow svg depending on the media query as different css positioning was not working
function adjustSVG() {
let width = window.innerWidth;
let arrow = document.getElementById('arrow');
if (width <= 1200) {
arrow.setAttribute('viewBox', '-480 0 42 49');
} else {
arrow.setAttribute('viewBox', '-570 0 42 49');
}
}
window.onload = adjustSVG;
window.onresize = adjustSVG;
//Animation for screens above 1024px and in landscape as smaller screen have the static reading layout, used https://gsap.com/docs/v3/GSAP/gsap.matchMedia()/
let mm = gsap.matchMedia();
mm.add("(min-width: 1025px) and (orientation: landscape)", () =>{ //Within this match media, which includes all screens above 1024px, I have added all my animations, smaller screens do not have the same css and experience implemented see report.
/*Animation for Titles*/
gsap.to(".scroll-out", {
x: () => window.innerWidth + 100, //Function returning the window width plus 100, giving .scroll-out the position it will got to with gsap.to
scrollTrigger:{ //method to make the animation/move to the above calculated x value, depending on scroll
trigger: ".scroll-out", //animation is triggered by the div scroll out
start: "top top", //animation starts at the top of .scroll-out div
scrub: true, //animation smoothly catch up to the scroll progress in that section https://gsap.com/docs/v3/Plugins/ScrollTrigger/?page=1
end: "bottom top", //increasing distance for smoother animation
anticipatePin: 1, //this helps make transitions into and out of pinned state smoother. As I have a long scrolling narrative, this helps mainting a fluid scrolling/pinning avoiding jumps https://gsap.com/community/forums/topic/26335-scrolltrigger-pin-jumpssnaps-on-triggering/
}
});
gsap.to("#indication",{
duration: 1, //animation takes 1 sec to complete
opacity: 0, //the indication div will go to 0 opacity, depending on scroll, same as above.
ease: "none", //aniamtion moves at a steady, linear rate from start to end.
scrollTrigger:{
trigger: ".scroll-out",
start: "top top",
scrub: true,
end: "bottom top",
anticipatePin: 1,
}
});
let continent = document.getElementById("africa-svg");
gsap.set(continent, { attr: { viewBox: "460 390 210 210" } }); //Using gsap to immedialty apply viewbox properties to the SVG
gsap.to(continent, { //Changing the SVG view on scroll using GSAP scrolltrigger
attr: { viewBox: "510 420 1 1" }, //The target viewbox after animation
scrollTrigger: {
trigger: '.scroll-out',
start: "top top",
end: "bottom top",
scrub: true,
anticipatePin: 1,
}
});
/*Introduction paragraph and footprints*/
gsap.set("#ff-svg, #fs-svg, #ft-svg, #fl-svg, #pf-svg", { //resizing and rotating fottprints
scale: 8,
rotate: 150,
});
//https://gsap.com/community/forums/topic/35469-making-a-timeline-with-scrolltrigger/
gsap.timeline({ //timeline for this section, triggered by section with id=zero. Each chapter follow the same structure, with a timeline that starts at the corresponding chapter and ends later to give room for the scroll, then each animated elements within the chapter depends on that timeline
scrollTrigger: {
trigger: '#zero',
start: 'top top',
end: '+=100%',
scrub: true,
pin: true, // Pinning the element '#zero' when the trigger starts and unpins when the trigger ends (element is in fixed position during the scroll range)
anticipatePin: 1,
}
})
.from("#ff-svg", {
autoAlpha: 0, //Both visibility and opacity are set at 0, meaning the svg starts from that states before beeing fully visible and opaque on scroll https://gsap.com/community/forums/topic/15361-understanding-autoalpha/
ease: "none",
duration: 1,
})
.from("#fs-svg", {
autoAlpha: 0,
ease: "none",
duration: 1,
})
.from("#ft-svg", {
autoAlpha: 0,
ease: "none",
duration: 1
})
.from("#fl-svg", {
autoAlpha: 0,
ease: "none",
duration: 1
})
.from("#pf-svg", {
autoAlpha: 0,
ease: "none",
duration: 1
});
/*Chapter One */
let timelineOne = gsap.timeline({
scrollTrigger: {
trigger: "#one",
start: "top top",
end: '+=3000',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineOne.from("#gnu",{
autoAlpha: 0,
ease: "none",
duration: 2,
}, "start"); //The animation applied to Gnu starts when the timeline starts
timelineOne.from("#mum",{
autoAlpha: 0,
ease: "none",
duration: 2,
},"start+=1"); //The animation applied to the mum starts one second after the start of the timeline
timelineOne.fromTo("#paragraph-1", { //Animating the paragraph from a starting position outside the viewport to a dynamic ending postion outside the viewport as well
y: '1000px' // Starting the animation with '#paragraph-1' positioned 1000 pixels down from its original position
}, {
y: () => -1 * (document.getElementById("paragraph-1").offsetHeight + 400), // Dynamically setting the ending y-position. Moving '#paragraph-1' to a position above its original place by its own height plus an additional 400 pixels to ensure it is completly out of the viewport (same principle as title moving on the x axis)
ease: "power1.inOut", //Predefined easing function in gsap, power refers to the strengh of the easing cruve with 1 being relatively mild, inOut means the start and end are slow. I implemented this for all paragraphs to control their speed, making sure they dont arrive and leave the viewport to abruptly and are not missed by the reader https://gsap.com/docs/v3/Eases/
duration: 5,
}, 'start+=1.3'); //Delaying the start of the animation by 1.3sec after the start of the timeline, this way I coordinate how each elements appear in the timeline in relation to each other
timelineOne.fromTo("#paragraph-2", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-2").offsetHeight + 400),
ease: "power1.inOut",
duration: 5,
}, 'start+=5');
timelineOne.from("#gazelle",{
autoAlpha: 0,
ease: "none",
duration: 2,
},"start+=5.2");
timelineOne.from("#zebra",{
autoAlpha: 0,
ease: "none",
duration: 2,
},"start+=6");
let sketch = function(p) { //Creating a rain transition. To do so, I am creating a function that takes a p5 instance to seperate from the global name space and thus other libraries https://www.youtube.com/watch?v=Su792jEauZg. This video https://www.youtube.com/watch?v=YQysSfaLDyo helped me create the rain object and then I modified variables to display it differently visually and I then incorporated GSAP timeline.
let rains = []; //array holding the rain objects declared in the rain class below
p.setup = function() { //the set up function allows me to create the canvas element and initialise all variables https://p5js.org/reference/p5/setup/, this function runs once when the sktech starts.
let canvas = p.createCanvas(p.windowWidth, p.displayHeight +400 ); //Creating the canvas and asssigning its width to match the width of the viewport and the height to be the same as the viewport but with an extra 10px to smooth the colour transition with the next chapter
canvas.position(0, 0); //ensuring the canvas starts at the top left corner of the window
canvas.id("myCanvas"); //assigning id name to help with manipulating the canvas with GSAP
canvas.parent('sketch-holder'); //assigning the 'sketch-holder div' as the parent element of the canvas https://p5js.org/reference/p5.Element/parent/
p.background(48, 48, 50); //setting the background colour, same as next chapter for the transition
canvas.style('opacity', '0'); //initailly setting the canvas to be fully transparent
timelineOne.to('#myCanvas', { //animating canvas on the same timeline as zebra, gzaelle, gnu, etc. It will become fully opaque, hiding all elements and starts 2sec after the timeline
opacity: 1,
duration: 5,
}, "start+=9");
};
p.draw = function() { //the draw function, all code here runs repeatadly as a loop
p.background(48, 48, 50); //the background needs to be declared again as it runs in a loop, it is reset at every frame, illusion of drops of rain instead of lines
for (let r of rains) { // Iterating over all rain objects created below in the array and calling their show and update methods.
r.show();
r.update();
}
if (rains.length < 500) { // Continuously pushing rain objects into the array until there are 500 on the screen.
rains.push(new Rain(p.random(p.width), 0));
}
};
class Rain { //class declaration, creating rain objects https://p5js.org/reference/p5/class/ and https://www.youtube.com/watch?v=YQysSfaLDyo
constructor(x, y) { //creating the Rain object
this.pos = p.createVector(x, y); //assigning inital position of the vector using p5 createvector for x and y coordinates https://p5js.org/reference/p5/createVector/
this.vel = p.createVector(0, p.random(1, 5)); //the instance vel property is set in a downward direction (from 0 to a random position between 1 and 5)
this.len = p.random(10, 20); //randomly setting a lenght between 10 and 20 px
this.opa = p.random(1, 255); //assigning a random number for the opacity between 0 minimum opacity so transparent and 255, maximum level of opacity.
}
show() { //method to display the raindrops
p.stroke(221, 221, 221, this.opa); //setting the stroke color and its opacity defined randomly above
p.line(this.pos.x, this.pos.y, this.pos.x, this.pos.y - this.len); //drawing the rain drops as a line.
}
update() { // Method to update the raindrop's position.
this.pos.add(this.vel); // Moving the raindrop by its velocity
if (this.pos.y > p.height + 50) { // Checking if the raindrop has moved beyond the canvas height and removing it from the array if so to avoid lagging.
let index = rains.indexOf(this);
if (index > -1) {
rains.splice(index, 1);
}
}
}
}
};
new p5(sketch); // Instantiating the sketch by passing the defined function to the p5 constructor.
/*Chapter 2*/
let timelineTwo = gsap.timeline({
scrollTrigger: {
trigger: '#two',
start: 'top top',
endTrigger: '#five',
end: 'top top',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineTwo.to(continent,{opacity:0}); //making the africa SVG transparent
timelineTwo.to("#forest",{
position: "absolute",
opacity: 1,
duration: 10,
});
timelineTwo.fromTo("#paragraph-3", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-3").offsetHeight + 600),
ease: "power1.inOut",
duration: 10,
});
timelineTwo.to("#baobab",{
opacity: 1,
duration: 4,
});
timelineTwo.to("#baobab-2",{
opacity: 1,
duration: 4,
});
timelineTwo.to("#acacia",{
opacity: 1,
duration: 4,
});
timelineTwo.fromTo("#paragraph-4", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-4").offsetHeight + 600),
ease: "power1.inOut",
duration: 10,
});
timelineTwo.to("#blood",{
opacity: 1,
});
timelineTwo.to("#blood-layer",{
opacity: 1,
duration: 1,
}, "start+=5");
/*Chapter 3 */
let timelineThree = gsap.timeline({
scrollTrigger: {
trigger: '#three',
start: 'top top',
end: '+=3000',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineThree.fromTo("#paragraph-5", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-5").offsetHeight + 400),
ease: "power1.inOut",
duration: 8,
}, "start");
gsap.from("#plain",{
autoAlpha: 0,
ease: "none",
scrollTrigger: {
trigger: '#death',
start: 'top top',
endTrigger: '#paragraph-5',
end: 'bottom top',
scrub: true,
anticipatePin: 1,
}
},"start+=2");
/*Chapter 4 */
gsap.set('#cloud-one, #cloud-two, #cloud-three', {
attr: { viewBox: "0 0 500 500" },
});
gsap.set('#plant', {
attr: { viewBox: "0 -750 1800 1900" },
});
let timelineFour = gsap.timeline({
scrollTrigger: {
trigger: '#four',
start: 'top top',
end: '+=6000',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineFour.fromTo('#cloud-one, #cloud-three', {
x: '2000px'
}, {
x: '-2000px',
duration: 10,
}, "start"
);
timelineFour.fromTo('#cloud-two', {
x: '-2000px'
}, {
x: '2000px',
duration: 10,
}, "start");
timelineFour.fromTo("#paragraph-6", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-6").offsetHeight + 400),
ease: "power1.inOut",
duration: 5,
}, "start");
timelineFour.fromTo("#paragraph-7", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-7").offsetHeight + 400),
ease: "power1.inOut",
duration: 5,
}, "start+=4");
timelineFour.from('.green',{
autoAlpha: 0,
ease: "none",
duration: 4,
}, "start+=4.5");
timelineFour.from('#grounds',{
autoAlpha: 0,
ease: "none",
duration: 5,
}, "start+=7");
/*Chapter Five */
let timelineFive = gsap.timeline({
scrollTrigger: {
trigger: '#five',
start: 'top top',
endTrigger: '#end',
end: 'top top',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineFive.fromTo("#paragraph-8", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-8").offsetHeight + 400),
ease: "power1.inOut",
duration: 4,
});
/*Chapter Six */
let timelineSix = gsap.timeline({
scrollTrigger: {
trigger: '#six',
start: 'top top',
endTrigger: '#end',
end: 'top top',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineSix.fromTo("#paragraph-9", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-9").offsetHeight + 400),
ease: "none",
duration: 5,
}, "start");
timelineSix.from("#jump", {
autoAlpha: 0,
ease: "none",
duration: 4,
}, "start+=1");
/*Chapter Seven */
let timelineSeven = gsap.timeline({
scrollTrigger: {
trigger: '#seven',
start: 'top top',
end: '+=1000px',
scrub: true,
pin: true,
anticipatePin: 1,
}
});
timelineSeven.from('#leaf-one, #leaf-two, #repeated-wave, #cloud-five, #acacia-two, #baobab-two',{
autoAlpha: 0,
ease: "none",
duration: 4,
}, "start");
timelineSeven.from('#gazelle-two, #zebra-two, #gnu-mum-two',{
autoAlpha: 0,
ease: "none",
duration: 4,
}, "start+=3");
timelineSeven.from('#gnu-two',{
autoAlpha: 0,
ease: "none",
duration: 4,
}, "start+=9");
timelineSeven.fromTo("#paragraph-10", {
y: '1000px'
}, {
y: () => -1 * (document.getElementById("paragraph-10").offsetHeight + 400),
ease: "power1.inOut",
duration: 10,
});
});