├── .gitignore
├── README.md
├── css
└── base.css
├── img
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── dos1.jpg
├── dos2.jpg
├── intro1.jpg
├── intro2.jpg
├── last.jpg
├── tres1.jpg
└── tres2.jpg
├── index.html
├── js
├── app.js
├── imagesLoaded.js
├── rolls.js
└── shader
│ ├── fragment.glsl
│ └── vertex.glsl
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .cache/
2 | dist/
3 | node_modules/
4 |
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Unroll Effects with Three.js
2 |
3 | Some unrolling image effects using WebGL. By Yuriy Artyukh
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=46712)
8 |
9 | [Demo](https://tympanus.net/Development/UnrollingImages/)
10 |
11 | ## Instructions
12 |
13 | Run `parcel index.html` if you have [Parcel](https://parceljs.org/) installed, otherwise `npx parcel index.html`
14 |
15 | ## Credits
16 |
17 | - [three.js](https://threejs.org/)
18 | - Images from [Unsplash.com](https://unsplash.com/)
19 | - [imagesLoaded](https://imagesloaded.desandro.com/) by Dave DeSandro
20 |
21 | ## License
22 | This resource can be used freely if integrated or build upon in personal or commercial projects such as websites, web apps and web templates intended for sale. It is not allowed to take the resource "as-is" and sell it, redistribute, re-publish it, or sell "pluginized" versions of it. Free plugins built using this resource should have a visible mention and link to the original work. Always consider the licenses of all included libraries, scripts and images used.
23 |
24 | ## Misc
25 |
26 | Follow Yuriy: [Twitter](https://twitter.com/akella), [Instagram](http://instagram.com/akella_), [Facebook](https://facebook.com/akella), [GitHub](https://github.com/akella)
27 |
28 | Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [GitHub](https://github.com/codrops), [Instagram](https://www.instagram.com/codropsss/)
29 |
30 | [© Codrops 2020](http://www.codrops.com)
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | :root {
10 | font-size: 18px;
11 | }
12 |
13 | body {
14 | --color-text: #000;
15 | --color-bg: #dde8eb;
16 | --color-link: #4b4b4b;
17 | --color-link-hover: #000;
18 | color: var(--color-text);
19 | background-color: var(--color-bg);
20 | font-family: moderno-fb, serif;
21 | -webkit-font-smoothing: antialiased;
22 | -moz-osx-font-smoothing: grayscale;
23 | }
24 |
25 | /* Page Loader */
26 | .js .loading::before,
27 | .js .loading::after {
28 | content: '';
29 | position: fixed;
30 | z-index: 1000;
31 | }
32 |
33 | .js .loading::before {
34 | top: 0;
35 | left: 0;
36 | width: 100%;
37 | height: 100%;
38 | background: var(--color-bg);
39 | }
40 |
41 | .js .loading::after {
42 | top: 50%;
43 | left: 50%;
44 | width: 60px;
45 | height: 60px;
46 | margin: -30px 0 0 -30px;
47 | border-radius: 50%;
48 | opacity: 0.4;
49 | background: var(--color-link);
50 | animation: loaderAnim 0.7s linear infinite alternate forwards;
51 |
52 | }
53 |
54 | @keyframes loaderAnim {
55 | to {
56 | opacity: 1;
57 | transform: scale3d(0.5,0.5,1);
58 | }
59 | }
60 |
61 | a {
62 | text-decoration: none;
63 | color: var(--color-link);
64 | outline: none;
65 | }
66 |
67 | a:hover,
68 | a:focus {
69 | color: var(--color-link-hover);
70 | outline: none;
71 | text-decoration: underline;
72 | }
73 |
74 | .message {
75 | background: var(--color-text);
76 | color: var(--color-bg);
77 | padding: 1rem;
78 | text-align: center;
79 | }
80 |
81 | .frame {
82 | padding: 3rem 5vw;
83 | text-align: center;
84 | position: relative;
85 | z-index: 1000;
86 | }
87 |
88 | .frame__title {
89 | font-size: 1rem;
90 | margin: 0 0 1rem;
91 | font-weight: normal;
92 | }
93 |
94 | .frame__links {
95 | display: inline;
96 | }
97 |
98 | .frame__links a:not(:last-child) {
99 | margin-right: 1rem;
100 | }
101 |
102 | .frame__demos a:not(:last-child) {
103 | margin-right: 2rem;
104 | }
105 |
106 | .frame__demos {
107 | margin: 1rem 0;
108 | }
109 |
110 | .frame__demo {
111 | white-space: nowrap;
112 | }
113 |
114 | .frame__demo--current,
115 | .frame__demo--current:hover {
116 | color: var(--color-text);
117 | }
118 |
119 | .title {
120 | font-family: paralucent, sans-serif;
121 | font-weight: 700;
122 | }
123 |
124 | .js-image {
125 | max-width: 100%;
126 | display: block;
127 | }
128 |
129 | .loaded .js-image {
130 | opacity: 0;
131 | }
132 |
133 | canvas {
134 | position: fixed;
135 | top: 0;
136 | left: 0;
137 | z-index: 100;
138 | pointer-events: none;
139 | }
140 |
141 | .header {
142 | padding: 3rem;
143 | }
144 |
145 | .header__title {
146 | font-family: paralucent, sans-serif;
147 | font-size: 2rem;
148 | }
149 |
150 | .info {
151 | margin: 0.5rem 0;
152 | display: flex;
153 | }
154 |
155 | .info__link {
156 | margin-right: 1.25rem;
157 | }
158 |
159 | .section {
160 | padding: 20vh 0;
161 | margin: 0 100px;
162 | min-height: 100vh;
163 | }
164 |
165 | /*intro*/
166 | .intro {
167 | text-align: right;
168 | font-size: 18px;
169 | padding-top: 100px;
170 | }
171 |
172 | .intro__title {
173 | padding-top: 1em;
174 | position: absolute;
175 | bottom: -2em;
176 | right: 0;
177 | }
178 |
179 | .intro__wrap {
180 | position: relative;
181 | }
182 |
183 | .intro__background-image {
184 | margin-right: 20%;
185 | position: relative;
186 | }
187 |
188 | .intro__background-image img {
189 | width: 100%;
190 | display: block;
191 | }
192 |
193 | .intro__foreground-image {
194 | position: absolute;
195 | right: 0;
196 | width: 60%;
197 | top: 50%;
198 | transform: translate(0, -50%);
199 | }
200 |
201 | /*dos*/
202 | .dos {
203 | margin: 100px 50px;
204 | position: relative;
205 | }
206 |
207 | .dos img {
208 | max-width: 100%;
209 | display: block;
210 | width: 100%;
211 | }
212 |
213 | .dos__foreground {
214 | width: 40%;
215 | position: relative;
216 | padding-left: 6em;
217 | }
218 |
219 | .dos__background {
220 | text-align: right;
221 | margin-left: 30%;
222 | position: relative;
223 | margin-top: -14vh;
224 | }
225 |
226 | .dos__text {
227 | position: absolute;
228 | bottom: 0;
229 | width: 40%;
230 | right: calc(100% + 1em);
231 | }
232 |
233 | .meta {
234 | font-family: paralucent, sans-serif;
235 | font-weight: 700;
236 | }
237 |
238 | .dos .meta {
239 | position: absolute;
240 | left: 0;
241 | /*right: 0;*/
242 | /*left: auto;*/
243 | top: 100%;
244 | transform: rotate(-90deg);
245 | transform-origin: 0 0;
246 |
247 | }
248 |
249 | .meta__subtitle {
250 | font-size: 1.2em;
251 | white-space: nowrap;
252 | }
253 |
254 | .meta__title {
255 | font-size: 2.5em;
256 | white-space: nowrap;
257 | }
258 |
259 | /*tres*/
260 | .tres {
261 | position: relative;
262 | }
263 |
264 | .tres__background {
265 | position: relative;
266 | width: 55%;
267 | margin-top: -40%;
268 | z-index: 1;
269 | }
270 |
271 | .tres__background img {
272 | width: 100%;
273 | display: block;
274 | }
275 |
276 | .tres__text {
277 | position: absolute;
278 | top: 100%;
279 | width: 20em;
280 | left: 0;
281 | padding-top: 1em;
282 | }
283 |
284 | .tres .meta {
285 | position: absolute;
286 | right: -1em;
287 | top: 100%;
288 | width: 8em;
289 | /*background: green;*/
290 | transform-origin: 0 0;
291 | transform: rotate(-90deg);
292 | text-align: left;
293 | }
294 |
295 | .tres__foreground {
296 | padding-right: 8em;
297 | text-align: right;
298 | display: inline-block;
299 | width: 100%;
300 | position: relative;
301 | z-index: 2;
302 | }
303 |
304 | .tres__foreground-image {
305 | float: right;
306 | width: calc(70% - 8em);
307 | }
308 |
309 | .tres__foreground img {
310 | width: 100%;
311 | display: block;
312 | }
313 |
314 | /*cuatro*/
315 | .cuatro ul {
316 | max-width: 800px;
317 | margin: 100px auto;
318 | list-style: none;
319 | overflow: hidden;
320 | }
321 |
322 | .cuatro li {
323 | width: 50%;
324 | text-align: center;
325 | float: left;
326 | margin-bottom: 2em;
327 | }
328 |
329 | /*last*/
330 | .last {
331 | margin: 100px auto 0 auto;
332 | text-align: center;
333 | padding-bottom: 30vh;
334 | }
335 |
336 | .last__image {
337 | max-width: 480px;
338 | margin: 0 auto;
339 | }
340 |
341 | .last img {
342 | display: block;
343 | max-width: 100%;
344 | }
345 |
346 | .button {
347 | display: inline-block;
348 | background: none;
349 | border-radius: 0;
350 | border: 1px solid #94a6ab;
351 | cursor: pointer;
352 | padding: 0.7em 1.3em;
353 | font-size: 1em;
354 | margin: 0 0.5em;
355 | }
356 |
357 | .button:focus {
358 | outline: none;
359 | }
360 |
361 | .buttons {
362 | margin-bottom: 100px;
363 | }
364 |
365 |
366 | @media (min-width: 1480px) {
367 | .section {
368 | margin-left: auto;
369 | margin-right: auto;
370 | max-width: 1200px;
371 | }
372 | }
373 |
374 | @media screen and (min-width: 53em) {
375 | .message {
376 | display: none;
377 | }
378 | .frame {
379 | position: fixed;
380 | text-align: left;
381 | z-index: 100;
382 | top: 0;
383 | left: 0;
384 | display: grid;
385 | align-content: space-between;
386 | width: 100%;
387 | max-width: none;
388 | height: 100vh;
389 | padding: 2rem;
390 | grid-gap: 3rem;
391 | pointer-events: none;
392 | -webkit-touch-callout: none;
393 | -webkit-user-select: none;
394 | -moz-user-select: none;
395 | -ms-user-select: none;
396 | user-select: none;
397 | grid-template-columns: min-content min-content auto 300px;
398 | grid-template-rows: auto auto auto;
399 | grid-template-areas: 'title links info ...'
400 | '... ... ... ...'
401 | '... ... demos demos';
402 | }
403 | .frame__title-wrap {
404 | grid-area: title;
405 | display: flex;
406 | }
407 | .frame__title {
408 | margin: 0;
409 | white-space: nowrap;
410 | }
411 | .frame__info {
412 | grid-area: info;
413 | }
414 | .frame__demos {
415 | margin: 0;
416 | grid-area: demos;
417 | justify-self: end;
418 | }
419 | .frame__links {
420 | grid-area: links;
421 | padding: 0;
422 | white-space: nowrap;
423 | margin: 0 1rem;
424 | }
425 | .frame a {
426 | pointer-events: auto;
427 | }
428 | }
429 |
430 | @media (max-width: 750px) {
431 |
432 | .section,
433 | .last {
434 | margin: 0 50px;
435 | }
436 |
437 | .last {
438 | margin: 0 auto;
439 | }
440 |
441 | .intro__background-image {
442 | position: static;
443 | }
444 |
445 | .intro__background-image img {
446 | display: none;
447 | }
448 |
449 | .intro__foreground-image {
450 | position: static;
451 | margin: 0;
452 | padding: 0;
453 | transform: none;
454 | width: auto;
455 | }
456 |
457 | .intro__foreground-image img {
458 | display: block;
459 | width: 100%;
460 | }
461 |
462 | .dos {
463 | position: relative;
464 | }
465 |
466 | .dos__background {
467 | position: static;
468 | width: 100%;
469 | padding: 0;
470 | margin: 0;
471 | }
472 |
473 | .dos__background-image {
474 | padding-left: 3em;
475 | min-height: 200px;
476 | }
477 |
478 | .dos__foreground {
479 | margin: 0;
480 | position: absolute;
481 | left: 0;
482 | }
483 |
484 | .dos__foreground-image {
485 | display: none;
486 | }
487 |
488 | .dos__foreground {
489 | width: 0;
490 | }
491 |
492 | .dos__text {
493 | position: static;
494 | width: auto;
495 | text-align: right;
496 | margin: 1em 0;
497 | }
498 |
499 | .dos .meta {
500 | transform: rotate(-90deg);
501 | width: 8em;
502 | left: -8em;
503 | transform-origin: 100% 0;
504 | top: 4em;
505 | font-size: 0.5rem;
506 | }
507 |
508 | .tres .meta {
509 | font-size: 0.5rem;
510 | }
511 |
512 | .tres__foreground-image {
513 | width: 100%;
514 | float: none;
515 | }
516 |
517 | .tres__background {
518 | margin: 0;
519 | width: auto;
520 | }
521 |
522 | .tres__background-image {
523 | display: none;
524 | }
525 |
526 | .tres__text {
527 | position: static;
528 | width: auto;
529 | margin: 0;
530 | }
531 |
532 | .cuatro li {
533 | float: none;
534 | width: auto;
535 | }
536 |
537 | .cuatro li img {
538 | width: 100%;
539 | }
540 |
541 | .last__image {
542 | margin: 0 20px;
543 | }
544 |
545 | .button {
546 | font-size: 0.8em;
547 | }
548 | }
549 |
550 |
551 |
--------------------------------------------------------------------------------
/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/1.jpg
--------------------------------------------------------------------------------
/img/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/2.jpg
--------------------------------------------------------------------------------
/img/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/3.jpg
--------------------------------------------------------------------------------
/img/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/4.jpg
--------------------------------------------------------------------------------
/img/dos1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/dos1.jpg
--------------------------------------------------------------------------------
/img/dos2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/dos2.jpg
--------------------------------------------------------------------------------
/img/intro1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/intro1.jpg
--------------------------------------------------------------------------------
/img/intro2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/intro2.jpg
--------------------------------------------------------------------------------
/img/last.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/last.jpg
--------------------------------------------------------------------------------
/img/tres1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/tres1.jpg
--------------------------------------------------------------------------------
/img/tres2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/akella/UnrollingImages/28000ef2de029c59d4fd863a02958b4a72607cd9/img/tres2.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Image Unroll Effects with Three.js | Codrops
8 |
9 |
10 |
11 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
36 |

37 |
Watson Building, Dubai, 2020
38 |
39 |
40 |

41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |

54 |
55 |
56 |
57 |
58 |

59 |
60 |
61 |
Carlon Arc, Chicago, 2019
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |

80 |
81 |
82 |
Plex Construct, Sydney, 2018
83 |
84 |
85 |
86 |
87 |
88 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |

106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/js/app.js:
--------------------------------------------------------------------------------
1 | import FontFaceObserver from "fontfaceobserver";
2 | import imagesLoaded from "./imagesLoaded";
3 | import Scene from "./rolls";
4 | import gsap from "gsap";
5 |
6 | const scene = new Scene("container");
7 |
8 | // helper functions
9 | const MathUtils = {
10 | // map number x from range [a, b] to [c, d]
11 | map: (x, a, b, c, d) => ((x - a) * (d - c)) / (b - a) + c,
12 | // linear interpolation
13 | lerp: (a, b, n) => (1 - n) * a + n * b
14 | };
15 |
16 | // body element
17 | const body = document.body;
18 | let IMAGES;
19 |
20 | // calculate the viewport size
21 | let winsize;
22 | const calcWinsize = () =>
23 | (winsize = { width: window.innerWidth, height: window.innerHeight });
24 | calcWinsize();
25 | // and recalculate on resize
26 | window.addEventListener("resize", calcWinsize);
27 |
28 | window.onbeforeunload = function() {
29 | window.scrollTo(0, 0);
30 | };
31 |
32 | // scroll position and update function
33 | let docScroll;
34 | const getPageYScroll = () =>
35 | (docScroll = window.pageYOffset || document.documentElement.scrollTop);
36 | window.addEventListener("scroll", getPageYScroll);
37 |
38 | // Item
39 | class Item {
40 | constructor(el, scroll) {
41 | // the .item element
42 | this.scroll = scroll;
43 | this.DOM = { el: el.img };
44 | this.currentScroll = docScroll;
45 | this.animated = false;
46 | this.isBeingAnimatedNow = false;
47 | this.shouldRollBack = false;
48 | this.shouldUnRoll = false;
49 | this.positions = [];
50 |
51 | // set the initial values
52 | this.getSize();
53 | this.mesh = scene.createMesh({
54 | width: this.width,
55 | height: this.height,
56 | src: this.src,
57 | image: this.DOM.el,
58 | iWidth: this.DOM.el.width,
59 | iHeight: this.DOM.el.height
60 | });
61 | scene.scene.add(this.mesh);
62 | // use the IntersectionObserver API to check when the element is inside the viewport
63 | // only then the element translation will be updated
64 | this.intersectionRatio;
65 | let options = {
66 | root: null,
67 | rootMargin: "0px",
68 | threshold: [0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
69 | };
70 | this.observer = new IntersectionObserver(entries => {
71 | entries.forEach(entry => {
72 | this.positions.push(entry.boundingClientRect.y);
73 | let compareArray = this.positions.slice(
74 | this.positions.length - 2,
75 | this.positions.length
76 | );
77 | let down = compareArray[0] > compareArray[1] ? true : false;
78 |
79 | this.isVisible = entry.intersectionRatio > 0.0;
80 |
81 | this.shouldRollBack = false;
82 | this.shouldUnRoll = false;
83 | if (
84 | entry.intersectionRatio < 0.5 &&
85 | entry.boundingClientRect.y > 0 &&
86 | this.isVisible &&
87 | !down
88 | ) {
89 | this.shouldRollBack = true;
90 | }
91 |
92 | if (
93 | entry.intersectionRatio > 0.5 &&
94 | entry.boundingClientRect.y > 0 &&
95 | this.isVisible
96 | ) {
97 | this.shouldUnRoll = true;
98 | }
99 |
100 | this.mesh.visible = this.isVisible;
101 | });
102 | }, options);
103 | this.observer.observe(this.DOM.el);
104 | // init/bind events
105 | window.addEventListener("resize", () => this.resize());
106 | this.render(0);
107 | }
108 |
109 | getSize() {
110 | // get all the sizes here, bounds and all
111 | const bounds = this.DOM.el.getBoundingClientRect();
112 | const fromTop = bounds.top;
113 | const windowHeight = window.innerHeight;
114 | const withoutHeight = fromTop - windowHeight;
115 | const withHeight = fromTop + bounds.height;
116 | this.insideTop = withoutHeight - docScroll;
117 | this.insideRealTop = fromTop + docScroll;
118 | this.insideBottom = withHeight - docScroll + 50;
119 | this.width = bounds.width;
120 | this.height = bounds.height;
121 | this.left = bounds.left;
122 | }
123 | resize() {
124 | // on resize rest sizes and update the translation value
125 | this.getSize();
126 | this.mesh.scale.set(this.width, this.height, 200);
127 | this.render(this.scroll.renderedStyles.translationY.current);
128 | this.scroll.shouldRender = true;
129 | }
130 |
131 | render(currentScroll) {
132 | this.currentScroll = currentScroll;
133 | this.mesh.position.y =
134 | currentScroll + winsize.height / 2 - this.insideRealTop - this.height / 2;
135 | this.mesh.position.x = 0 - winsize.width / 2 + this.left + this.width / 2;
136 | if (this.shouldUnRoll && !this.animated) {
137 | this.animated = true;
138 | this.isBeingAnimatedNow = true;
139 | this.shouldUnRoll = false;
140 | gsap.to(this.mesh.material.uniforms.progress, {
141 | duration: 1.7,
142 | value: 1,
143 | ease: "power2.out",
144 | onUpdate: () => {
145 | this.scroll.shouldRender = true;
146 | },
147 | onComplete: () => {
148 | this.isBeingAnimatedNow = false;
149 | }
150 | });
151 | }
152 |
153 | if (this.shouldRollBack && this.animated) {
154 | this.animated = false;
155 | this.isBeingAnimatedNow = true;
156 | this.shouldRollBack = false;
157 | gsap.to(this.mesh.material.uniforms.progress, {
158 | duration: 1.7,
159 | value: 0,
160 | ease: "power2.inOut",
161 | onUpdate: () => {
162 | this.scroll.shouldRender = true;
163 | },
164 | onComplete: () => {
165 | this.isBeingAnimatedNow = false;
166 | }
167 | });
168 | }
169 | }
170 | }
171 |
172 | // SmoothScroll
173 | class SmoothScroll {
174 | constructor() {
175 | this.shouldRender = false;
176 | // the element
177 | this.DOM = { main: document.querySelector("main") };
178 | // the scrollable element
179 | // we translate this element when scrolling (y-axis)
180 | this.DOM.scrollable = this.DOM.main.querySelector("div[data-scroll]");
181 | // the items on the page
182 | this.items = [];
183 |
184 | this.createItems();
185 |
186 | // here we define which property will change as we scroll the page
187 | // in this case we will be translating on the y-axis
188 | // we interpolate between the previous and current value to achieve the smooth scrolling effect
189 | this.renderedStyles = {
190 | translationY: {
191 | // interpolated value
192 | previous: 0,
193 | // current value
194 | current: 0,
195 | // amount to interpolate
196 | ease: 0.1,
197 | // current value setter
198 | // in this case the value of the translation will be the same like the document scroll
199 | setValue: () => docScroll
200 | }
201 | };
202 | // set the body's height
203 | this.setSize();
204 | // set the initial values
205 | this.update();
206 | // the element's style needs to be modified
207 | this.style();
208 | // init/bind events
209 | this.initEvents();
210 | this.buttons();
211 | // start the render loop
212 | requestAnimationFrame(() => this.render());
213 | }
214 |
215 | buttons() {
216 | let that = this;
217 | let buttons = [...document.querySelectorAll(".js-change")];
218 |
219 | buttons.forEach(b => {
220 | let angle = b.getAttribute("data-angle");
221 | let radians = (angle * Math.PI) / 180;
222 |
223 | b.addEventListener("click", () => {
224 | scene.settings.angle = radians;
225 |
226 | scene.scene.children.forEach(mesh => {
227 | gsap.fromTo(
228 | mesh.material.uniforms.progress,
229 | {
230 | value: 0
231 | },
232 | {
233 | duration: 2,
234 | value: 1,
235 | ease: "power2.out",
236 | onUpdate: () => {
237 | this.shouldRender = true;
238 | }
239 | }
240 | );
241 | });
242 | });
243 | });
244 | }
245 |
246 | update() {
247 | // sets the initial value (no interpolation) - translate the scroll value
248 | for (const key in this.renderedStyles) {
249 | this.renderedStyles[key].current = this.renderedStyles[
250 | key
251 | ].previous = this.renderedStyles[key].setValue();
252 | }
253 | // translate the scrollable element
254 | this.setPosition();
255 | this.shouldRender = true;
256 | }
257 | setPosition() {
258 | // translates the scrollable element
259 | if (
260 | Math.round(this.renderedStyles.translationY.previous) !==
261 | Math.round(this.renderedStyles.translationY.current) ||
262 | this.renderedStyles.translationY.previous < 10
263 | ) {
264 | this.shouldRender = true;
265 | this.DOM.scrollable.style.transform = `translate3d(0,${-1 *
266 | this.renderedStyles.translationY.previous}px,0)`;
267 | // console.log(this.items);
268 | for (const item of this.items) {
269 | // if the item is inside the viewport call it's render function
270 | // this will update the item's inner image translation, based on the document scroll value and the item's position on the viewport
271 | if (item.isVisible || item.isBeingAnimatedNow) {
272 | item.render(this.renderedStyles.translationY.previous);
273 | }
274 | }
275 | }
276 |
277 | if (this.shouldRender) {
278 | this.shouldRender = false;
279 | scene.render();
280 | }
281 | }
282 | setSize() {
283 | // set the heigh of the body in order to keep the scrollbar on the page
284 | // console.log(this.DOM.scrollable.scrollHeight, 'HEIGHT');
285 | body.style.height = `${this.DOM.scrollable.scrollHeight}px`;
286 | }
287 |
288 | createItems() {
289 | IMAGES.forEach(image => {
290 | if (image.img.classList.contains("js-image")) {
291 | this.items.push(new Item(image, this));
292 | }
293 | });
294 | }
295 |
296 | style() {
297 | // the needs to "stick" to the screen and not scroll
298 | // for that we set it to position fixed and overflow hidden
299 | this.DOM.main.style.position = "fixed";
300 | this.DOM.main.style.width = this.DOM.main.style.height = "100%";
301 | this.DOM.main.style.top = this.DOM.main.style.left = 0;
302 | this.DOM.main.style.overflow = "hidden";
303 | }
304 | initEvents() {
305 | // on resize reset the body's height
306 | window.addEventListener("resize", () => this.setSize());
307 | }
308 | render() {
309 | // update the current and interpolated values
310 | for (const key in this.renderedStyles) {
311 | this.renderedStyles[key].current = this.renderedStyles[key].setValue();
312 | this.renderedStyles[key].previous = MathUtils.lerp(
313 | this.renderedStyles[key].previous,
314 | this.renderedStyles[key].current,
315 | this.renderedStyles[key].ease
316 | );
317 | }
318 | // and translate the scrollable element
319 | this.setPosition();
320 |
321 | // loop..
322 | requestAnimationFrame(() => this.render());
323 | }
324 | }
325 |
326 | /***********************************/
327 | /********** Preload stuff **********/
328 |
329 | const fontParalucent = new Promise(resolve => {
330 | new FontFaceObserver("paralucent").load().then(() => {
331 | resolve();
332 | });
333 | });
334 | const fontStarling = new Promise(resolve => {
335 | new FontFaceObserver("starling").load().then(() => {
336 | resolve();
337 | });
338 | });
339 |
340 | // Preload images
341 | const preloadImages = new Promise((resolve, reject) => {
342 | imagesLoaded(document.querySelectorAll("img"), { background: true }, resolve);
343 | });
344 |
345 | preloadImages.then(images => {
346 | IMAGES = images.images;
347 | });
348 |
349 | const preloadEverything = [fontStarling, fontParalucent, preloadImages];
350 |
351 | // And then..
352 | Promise.all(preloadEverything).then(() => {
353 | // Remove the loader
354 | document.body.classList.remove("loading");
355 | document.body.classList.add("loaded");
356 | // Get the scroll position
357 | getPageYScroll();
358 | // Initialize the Smooth Scrolling
359 | new SmoothScroll();
360 | });
361 |
--------------------------------------------------------------------------------
/js/imagesLoaded.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * imagesLoaded PACKAGED v4.1.1
3 | * JavaScript is all like "You images are done yet or what?"
4 | * MIT License
5 | */
6 |
7 | !function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict";"function"==typeof define&&define.amd?define(["ev-emitter/ev-emitter"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("ev-emitter")):t.imagesLoaded=e(t,t.EvEmitter)}(window,function(t,e){function i(t,e){for(var i in e)t[i]=e[i];return t}function n(t){var e=[];if(Array.isArray(t))e=t;else if("number"==typeof t.length)for(var i=0;i imageAspect) {
112 | a1 = (o.width / o.height) * imageAspect;
113 | a2 = 1;
114 | } else {
115 | a1 = 1;
116 | a2 = o.height / o.width / imageAspect;
117 | }
118 | texture.minFilter = THREE.LinearFilter;
119 | material.uniforms.resolution.value.x = o.width;
120 | material.uniforms.resolution.value.y = o.height;
121 | material.uniforms.resolution.value.z = a1;
122 | material.uniforms.resolution.value.w = a2;
123 | material.uniforms.progress.value = 0;
124 | material.uniforms.angle.value = 0.3;
125 |
126 | material.uniforms.texture1.value = texture;
127 | material.uniforms.texture1.value.needsUpdate = true;
128 |
129 | let mesh = new THREE.Mesh(this.geometry, material);
130 |
131 | mesh.scale.set(o.width, o.height, o.width / 2);
132 |
133 | return mesh;
134 | }
135 |
136 | stop() {
137 | this.paused = true;
138 | }
139 |
140 | play() {
141 | this.paused = false;
142 | this.render();
143 | }
144 |
145 | render() {
146 | this.scene.children.forEach(m => {
147 | if (m.material.uniforms) {
148 | m.material.uniforms.angle.value = this.settings.angle;
149 | }
150 | });
151 | this.renderer.render(this.scene, this.camera);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/js/shader/fragment.glsl:
--------------------------------------------------------------------------------
1 | uniform float time;
2 | uniform float progress;
3 | uniform sampler2D texture1;
4 | uniform sampler2D texture2;
5 | uniform vec4 resolution;
6 |
7 | varying vec2 vUv;
8 | varying float vFrontShadow;
9 | // varying float vBackShadow;
10 | // varying float vProgress;
11 |
12 |
13 |
14 |
15 | void main() {
16 | vec2 newUV = (vUv - vec2(0.5))*resolution.zw + vec2(0.5);
17 |
18 | gl_FragColor = texture2D(texture1,newUV);
19 | gl_FragColor.rgb *=vFrontShadow;
20 | gl_FragColor.a = clamp(progress*5.,0.,1.);
21 |
22 | }
--------------------------------------------------------------------------------
/js/shader/vertex.glsl:
--------------------------------------------------------------------------------
1 | uniform float time;
2 | uniform float angle;
3 | uniform float progress;
4 | uniform vec4 resolution;
5 | varying vec2 vUv;
6 | varying float vFrontShadow;
7 | // varying float vBackShadow;
8 | // varying float vProgress;
9 |
10 | uniform sampler2D texture1;
11 | uniform sampler2D texture2;
12 | uniform vec2 pixels;
13 |
14 | const float pi = 3.1415925;
15 |
16 | mat4 rotationMatrix(vec3 axis, float angle) {
17 | axis = normalize(axis);
18 | float s = sin(angle);
19 | float c = cos(angle);
20 | float oc = 1.0 - c;
21 |
22 | return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
23 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
24 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
25 | 0.0, 0.0, 0.0, 1.0);
26 | }
27 | vec3 rotate(vec3 v, vec3 axis, float angle) {
28 | mat4 m = rotationMatrix(axis, angle);
29 | return (m * vec4(v, 1.0)).xyz;
30 | }
31 |
32 |
33 |
34 |
35 | void main() {
36 | vUv = uv;
37 | float pi = 3.14159265359;
38 |
39 |
40 | float finalAngle = angle - 0.*0.3*sin(progress*6.);
41 |
42 | // @todo account for aspect ratio!!!
43 | vec3 newposition = position;
44 |
45 | // float angle = pi/10.;
46 | float rad = 0.1;
47 | float rolls = 8.;
48 | // rot
49 | newposition = rotate(newposition - vec3(-.5,.5,0.), vec3(0.,0.,1.),-finalAngle) + vec3(-.5,.5,0.);
50 |
51 | float offs = (newposition.x + 0.5)/(sin(finalAngle) + cos(finalAngle)) ; // -0.5..0.5 -> 0..1
52 | float tProgress = clamp( (progress - offs*0.99)/0.01 , 0.,1.);
53 |
54 | // shadows
55 | vFrontShadow = clamp((progress - offs*0.95)/0.05,0.7,1.);
56 | // vBackShadow = 1. - clamp(abs((progress - offs*0.9)/0.1),0.,1.);
57 | // vProgress = clamp((progress - offs*0.95)/0.05,0.,1.);
58 |
59 |
60 |
61 | newposition.z = rad + rad*(1. - offs/2.)*sin(-offs*rolls*pi - 0.5*pi);
62 | newposition.x = - 0.5 + rad*(1. - offs/2.)*cos(-offs*rolls*pi + 0.5*pi);
63 | // // rot back
64 | newposition = rotate(newposition - vec3(-.5,.5,0.), vec3(0.,0.,1.),finalAngle) + vec3(-.5,.5,0.);
65 | // unroll
66 | newposition = rotate(newposition - vec3(-.5,0.5,rad), vec3(sin(finalAngle),cos(finalAngle),0.), -pi*progress*rolls);
67 | newposition += vec3(
68 | -.5 + progress*cos(finalAngle)*(sin(finalAngle) + cos(finalAngle)),
69 | 0.5 - progress*sin(finalAngle)*(sin(finalAngle) + cos(finalAngle)),
70 | rad*(1.-progress/2.)
71 | );
72 |
73 | // animation
74 | vec3 finalposition = mix(newposition,position,tProgress);
75 | gl_Position = projectionMatrix * modelViewMatrix * vec4(finalposition, 1.0 );
76 | }
77 |
78 |
79 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "requires": true,
3 | "lockfileVersion": 1,
4 | "dependencies": {
5 | "@choojs/findup": {
6 | "version": "0.2.1",
7 | "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
8 | "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
9 | "dev": true,
10 | "requires": {
11 | "commander": "^2.15.1"
12 | }
13 | },
14 | "commander": {
15 | "version": "2.20.3",
16 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
17 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
18 | "dev": true
19 | },
20 | "core-util-is": {
21 | "version": "1.0.2",
22 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
23 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
24 | "dev": true
25 | },
26 | "dat.gui": {
27 | "version": "0.7.6",
28 | "resolved": "https://registry.npmjs.org/dat.gui/-/dat.gui-0.7.6.tgz",
29 | "integrity": "sha512-9Uqr4aQUvp9q5P2b4y6gK604HXafubOq578OmOS8mjrIkYrBP4EbQ9gz9YRXgyPh7aQi+b9H/jAG7EucmhYpSA=="
30 | },
31 | "ev-emitter": {
32 | "version": "1.1.1",
33 | "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz",
34 | "integrity": "sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q=="
35 | },
36 | "events": {
37 | "version": "1.1.1",
38 | "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
39 | "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
40 | "dev": true
41 | },
42 | "fontfaceobserver": {
43 | "version": "2.1.0",
44 | "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.1.0.tgz",
45 | "integrity": "sha512-ReOsO2F66jUa0jmv2nlM/s1MiutJx/srhAe2+TE8dJCMi02ZZOcCTxTCQFr3Yet+uODUtnr4Mewg+tNQ+4V1Ng=="
46 | },
47 | "glsl-inject-defines": {
48 | "version": "1.0.3",
49 | "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
50 | "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=",
51 | "dev": true,
52 | "requires": {
53 | "glsl-token-inject-block": "^1.0.0",
54 | "glsl-token-string": "^1.0.1",
55 | "glsl-tokenizer": "^2.0.2"
56 | }
57 | },
58 | "glsl-resolve": {
59 | "version": "0.0.1",
60 | "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz",
61 | "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=",
62 | "dev": true,
63 | "requires": {
64 | "resolve": "^0.6.1",
65 | "xtend": "^2.1.2"
66 | },
67 | "dependencies": {
68 | "resolve": {
69 | "version": "0.6.3",
70 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
71 | "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY=",
72 | "dev": true
73 | }
74 | }
75 | },
76 | "glsl-token-assignments": {
77 | "version": "2.0.2",
78 | "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz",
79 | "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8=",
80 | "dev": true
81 | },
82 | "glsl-token-defines": {
83 | "version": "1.0.0",
84 | "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz",
85 | "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=",
86 | "dev": true,
87 | "requires": {
88 | "glsl-tokenizer": "^2.0.0"
89 | }
90 | },
91 | "glsl-token-depth": {
92 | "version": "1.1.2",
93 | "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz",
94 | "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ=",
95 | "dev": true
96 | },
97 | "glsl-token-descope": {
98 | "version": "1.0.2",
99 | "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz",
100 | "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=",
101 | "dev": true,
102 | "requires": {
103 | "glsl-token-assignments": "^2.0.0",
104 | "glsl-token-depth": "^1.1.0",
105 | "glsl-token-properties": "^1.0.0",
106 | "glsl-token-scope": "^1.1.0"
107 | }
108 | },
109 | "glsl-token-inject-block": {
110 | "version": "1.1.0",
111 | "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz",
112 | "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ=",
113 | "dev": true
114 | },
115 | "glsl-token-properties": {
116 | "version": "1.0.1",
117 | "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz",
118 | "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4=",
119 | "dev": true
120 | },
121 | "glsl-token-scope": {
122 | "version": "1.1.2",
123 | "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz",
124 | "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E=",
125 | "dev": true
126 | },
127 | "glsl-token-string": {
128 | "version": "1.0.1",
129 | "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz",
130 | "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw=",
131 | "dev": true
132 | },
133 | "glsl-token-whitespace-trim": {
134 | "version": "1.0.0",
135 | "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz",
136 | "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA=",
137 | "dev": true
138 | },
139 | "glsl-tokenizer": {
140 | "version": "2.1.5",
141 | "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
142 | "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
143 | "dev": true,
144 | "requires": {
145 | "through2": "^0.6.3"
146 | }
147 | },
148 | "glslify-bundle": {
149 | "version": "5.1.1",
150 | "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz",
151 | "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==",
152 | "dev": true,
153 | "requires": {
154 | "glsl-inject-defines": "^1.0.1",
155 | "glsl-token-defines": "^1.0.0",
156 | "glsl-token-depth": "^1.1.1",
157 | "glsl-token-descope": "^1.0.2",
158 | "glsl-token-scope": "^1.1.1",
159 | "glsl-token-string": "^1.0.1",
160 | "glsl-token-whitespace-trim": "^1.0.0",
161 | "glsl-tokenizer": "^2.0.2",
162 | "murmurhash-js": "^1.0.0",
163 | "shallow-copy": "0.0.1"
164 | }
165 | },
166 | "glslify-deps": {
167 | "version": "1.3.1",
168 | "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.1.tgz",
169 | "integrity": "sha512-Ogm179MCazwIRyEqs3g3EOY4Y3XIAa0yl8J5RE9rJC6QH1w8weVOp2RZu0mvnYy/2xIas1w166YR2eZdDkWQxg==",
170 | "dev": true,
171 | "requires": {
172 | "@choojs/findup": "^0.2.0",
173 | "events": "^1.0.2",
174 | "glsl-resolve": "0.0.1",
175 | "glsl-tokenizer": "^2.0.0",
176 | "graceful-fs": "^4.1.2",
177 | "inherits": "^2.0.1",
178 | "map-limit": "0.0.1",
179 | "resolve": "^1.0.0"
180 | }
181 | },
182 | "graceful-fs": {
183 | "version": "4.2.3",
184 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
185 | "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
186 | "dev": true
187 | },
188 | "gsap": {
189 | "version": "3.0.5",
190 | "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.0.5.tgz",
191 | "integrity": "sha512-TpIeFoIU8x+46/RGTD/qY+jUAnxcPgqxwOZtJ0YZSaLoL5JR87tyH8p3X02ZnYrMpE0Tt9cOA3ueiWhCVuRv1A=="
192 | },
193 | "inherits": {
194 | "version": "2.0.4",
195 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
196 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
197 | "dev": true
198 | },
199 | "isarray": {
200 | "version": "0.0.1",
201 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
202 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
203 | "dev": true
204 | },
205 | "map-limit": {
206 | "version": "0.0.1",
207 | "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
208 | "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
209 | "dev": true,
210 | "requires": {
211 | "once": "~1.3.0"
212 | }
213 | },
214 | "murmurhash-js": {
215 | "version": "1.0.0",
216 | "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
217 | "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=",
218 | "dev": true
219 | },
220 | "once": {
221 | "version": "1.3.3",
222 | "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
223 | "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
224 | "dev": true,
225 | "requires": {
226 | "wrappy": "1"
227 | }
228 | },
229 | "path-parse": {
230 | "version": "1.0.6",
231 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
232 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
233 | "dev": true
234 | },
235 | "readable-stream": {
236 | "version": "1.0.34",
237 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
238 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
239 | "dev": true,
240 | "requires": {
241 | "core-util-is": "~1.0.0",
242 | "inherits": "~2.0.1",
243 | "isarray": "0.0.1",
244 | "string_decoder": "~0.10.x"
245 | }
246 | },
247 | "resolve": {
248 | "version": "1.14.2",
249 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz",
250 | "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==",
251 | "dev": true,
252 | "requires": {
253 | "path-parse": "^1.0.6"
254 | }
255 | },
256 | "shallow-copy": {
257 | "version": "0.0.1",
258 | "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
259 | "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA=",
260 | "dev": true
261 | },
262 | "string_decoder": {
263 | "version": "0.10.31",
264 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
265 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
266 | "dev": true
267 | },
268 | "three": {
269 | "version": "0.112.1",
270 | "resolved": "https://registry.npmjs.org/three/-/three-0.112.1.tgz",
271 | "integrity": "sha512-8I0O74hiYtKl3LgDNcPJbBGOlpekbcJ6fJnImmW3mFdeUFJ2H9Y3/UuUSW2sBdjrIlCM0gvOkaTEFlofO900TQ=="
272 | },
273 | "through2": {
274 | "version": "0.6.5",
275 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
276 | "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
277 | "dev": true,
278 | "requires": {
279 | "readable-stream": ">=1.0.33-1 <1.1.0-0",
280 | "xtend": ">=4.0.0 <4.1.0-0"
281 | },
282 | "dependencies": {
283 | "xtend": {
284 | "version": "4.0.2",
285 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
286 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
287 | "dev": true
288 | }
289 | }
290 | },
291 | "wrappy": {
292 | "version": "1.0.2",
293 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
294 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
295 | "dev": true
296 | },
297 | "xtend": {
298 | "version": "2.2.0",
299 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz",
300 | "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak=",
301 | "dev": true
302 | }
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "dat.gui": "^0.7.6",
4 | "ev-emitter": "^1.1.1",
5 | "fontfaceobserver": "^2.1.0",
6 | "gsap": "^3.0.5",
7 | "three": "^0.112.1",
8 | "three-orbit-controls": "^82.1.0"
9 | },
10 | "devDependencies": {
11 | "cssnano": "^4.1.10",
12 | "glslify-bundle": "^5.1.1",
13 | "glslify-deps": "^1.3.1"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------