├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── 1.dc197a9a.jpg
├── 10.5e22fdc3.jpg
├── 11.a56b8aeb.jpg
├── 12.b5a57fd7.jpg
├── 13.3858c623.jpg
├── 14.c732d2b3.jpg
├── 2.3ca6bb44.jpg
├── 3.a433b89d.jpg
├── 4.c6d96be5.jpg
├── 5.689b68fd.jpg
├── 6.e96dcfff.jpg
├── 7.2d66e3ed.jpg
├── 8.f4323fe0.jpg
├── 9.c9233dac.jpg
├── base.98fd6c19.css
├── demo1.151408fb.js
├── demo1.ea7e4db3.css
├── demo2.06b37b5f.css
├── demo2.44794d1a.js
├── favicon.26242483.ico
├── index.html
└── index2.html
├── package.json
└── src
├── css
├── base.css
├── demo1.css
└── demo2.css
├── favicon.ico
├── img
├── 1.jpg
├── 10.jpg
├── 11.jpg
├── 12.jpg
├── 13.jpg
├── 14.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
├── 6.jpg
├── 7.jpg
├── 8.jpg
└── 9.jpg
├── index.html
├── index2.html
└── js
├── cursor.js
├── demo1
├── galleryController.js
├── galleryItem.js
└── index.js
├── demo2
├── galleryController.js
├── galleryItem.js
└── index.js
└── utils.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /.cache
3 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2009 - 2020 [Codrops](https://tympanus.net/codrops)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Stack Intro Animation
2 |
3 | A simple intro animation where an image stack moves to become a grid.
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=52018)
8 |
9 | [Demo](https://tympanus.net/Development/ImageStackGrid/)
10 |
11 |
12 | ## Installation
13 |
14 | Install dependencies:
15 |
16 | ```
17 | npm install
18 | ```
19 |
20 | Compile the code for development and start a local server:
21 |
22 | ```
23 | npm start
24 | ```
25 |
26 | Create the build:
27 |
28 | ```
29 | npm run build
30 | ```
31 |
32 | ## Credits
33 |
34 | - Images by [Unsplash](http://unsplash.com)
35 |
36 |
37 | ## Misc
38 |
39 | 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/)
40 |
41 | ## License
42 | [MIT](LICENSE)
43 |
44 | Made with :blue_heart: by [Codrops](http://www.codrops.com)
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/dist/1.dc197a9a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/1.dc197a9a.jpg
--------------------------------------------------------------------------------
/dist/10.5e22fdc3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/10.5e22fdc3.jpg
--------------------------------------------------------------------------------
/dist/11.a56b8aeb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/11.a56b8aeb.jpg
--------------------------------------------------------------------------------
/dist/12.b5a57fd7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/12.b5a57fd7.jpg
--------------------------------------------------------------------------------
/dist/13.3858c623.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/13.3858c623.jpg
--------------------------------------------------------------------------------
/dist/14.c732d2b3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/14.c732d2b3.jpg
--------------------------------------------------------------------------------
/dist/2.3ca6bb44.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/2.3ca6bb44.jpg
--------------------------------------------------------------------------------
/dist/3.a433b89d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/3.a433b89d.jpg
--------------------------------------------------------------------------------
/dist/4.c6d96be5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/4.c6d96be5.jpg
--------------------------------------------------------------------------------
/dist/5.689b68fd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/5.689b68fd.jpg
--------------------------------------------------------------------------------
/dist/6.e96dcfff.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/6.e96dcfff.jpg
--------------------------------------------------------------------------------
/dist/7.2d66e3ed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/7.2d66e3ed.jpg
--------------------------------------------------------------------------------
/dist/8.f4323fe0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/8.f4323fe0.jpg
--------------------------------------------------------------------------------
/dist/9.c9233dac.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/9.c9233dac.jpg
--------------------------------------------------------------------------------
/dist/base.98fd6c19.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 15px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #f8f6f3;
14 | --color-bg: #0c0c0c;
15 | --color-link: #7d6350;
16 | --color-link-hover: #f8f6f3;
17 | color: var(--color-text);
18 | background-color: var(--color-bg);
19 | --cursor-stroke: none;
20 | --cursor-fill: #c5681c;
21 | --cursor-stroke-width: 1px;
22 | -webkit-font-smoothing: antialiased;
23 | -moz-osx-font-smoothing: grayscale;
24 | font-family: halyard-display, sans-serif;
25 | }
26 |
27 | .noscroll main {
28 | overflow: hidden;
29 | height: 100vh;
30 | }
31 |
32 | /* Page Loader */
33 | .js .loading::before,
34 | .js .loading::after {
35 | content: '';
36 | position: fixed;
37 | z-index: 1000;
38 | }
39 |
40 | .js .loading::before {
41 | top: 0;
42 | left: 0;
43 | width: 100%;
44 | height: 100%;
45 | background: var(--color-bg);
46 | }
47 |
48 | .js .loading::after {
49 | top: 50%;
50 | left: 50%;
51 | width: 60px;
52 | height: 60px;
53 | margin: -30px 0 0 -30px;
54 | border-radius: 50%;
55 | opacity: 0.4;
56 | background: var(--color-link);
57 | animation: loaderAnim 0.7s linear infinite alternate forwards;
58 | }
59 |
60 | @keyframes loaderAnim {
61 | to {
62 | opacity: 1;
63 | transform: scale3d(0.5, 0.5, 1);
64 | }
65 |
66 | }
67 |
68 | a {
69 | text-decoration: underline;
70 | color: var(--color-link);
71 | outline: none;
72 | }
73 |
74 | a:hover,
75 | a:focus {
76 | text-decoration: none;
77 | color: var(--color-link-hover);
78 | outline: none;
79 | }
80 |
81 | main {
82 | display: grid;
83 | grid-template-areas: 'frame' 'content';
84 | }
85 |
86 | .frame {
87 | padding: 1rem;
88 | text-align: center;
89 | position: relative;
90 | z-index: 1000;
91 | grid-area: frame;
92 | font-weight: 300;
93 | }
94 |
95 | .frame__title {
96 | font-size: 1rem;
97 | margin: 0 0 1rem;
98 | font-weight: 500;
99 | position: relative;
100 | padding: 0 3.5rem;
101 | }
102 |
103 | .frame__title::before,
104 | .frame__title::after {
105 | content: '';
106 | position: absolute;
107 | width: 1rem;
108 | height: 1.5rem;
109 | border: 2px solid #c5681c;
110 | left: 0;
111 | top: calc(50% - 0.75rem);
112 | }
113 |
114 | .frame__title::before {
115 | transform: rotate(-18deg);
116 | transform-origin: top right;
117 | }
118 |
119 | .frame__links {
120 | display: inline;
121 | }
122 |
123 | .frame__links a:not(:last-child),
124 | .frame__demos a:not(:last-child) {
125 | margin-right: 1rem;
126 | }
127 |
128 | .frame__demos {
129 | margin: 1rem 0;
130 | }
131 |
132 | .frame__demo--current,
133 | .frame__demo--current:hover {
134 | color: var(--color-text);
135 | text-decoration: none;
136 | }
137 |
138 | .cursor {
139 | display: none;
140 | }
141 |
142 | /* Recommended styles for Splitting */
143 | .splitting .word,
144 | .splitting .char {
145 | display: inline-block;
146 | }
147 |
148 | /* Psuedo-element chars */
149 | .splitting .char {
150 | white-space: nowrap;
151 | position: relative;
152 | }
153 |
154 | @media screen and (min-width:53em) {
155 | :root {
156 | font-size: 18px;
157 | }
158 | main {
159 | grid-template-areas: 'content';
160 | grid-template-rows: 100%;
161 | grid-template-columns: 100%;
162 | width: 100%;
163 | }
164 |
165 | .frame {
166 | padding: 1.5rem 2rem;
167 | top: 0;
168 | left: 0;
169 | width: 100%;
170 | position: absolute;
171 | display: flex;
172 | justify-content: space-between;
173 | align-items: flex-start;
174 | }
175 |
176 | .frame__title,
177 | .frame__demos {
178 | margin: 0;
179 | }
180 |
181 | .frame__demos {
182 | margin: 0 auto 0 4rem;
183 | }
184 |
185 | .frame__links {
186 | padding: 0;
187 | }
188 |
189 | .frame a {
190 | pointer-events: auto;
191 | }
192 |
193 | }
194 |
195 | @media (any-pointer:fine) {
196 | .cursor {
197 | position: fixed;
198 | top: 0;
199 | left: 0;
200 | display: block;
201 | pointer-events: none;
202 | }
203 |
204 | .cursor__inner {
205 | fill: var(--cursor-fill);
206 | stroke: var(--cursor-stroke);
207 | stroke-width: var(--cursor-stroke-width);
208 | opacity: 0.7;
209 | }
210 |
211 | .no-js .cursor {
212 | display: none;
213 | }
214 |
215 | }
216 |
217 | /*! locomotive-scroll v4.0.4 | MIT License | https://github.com/locomotivemtl/locomotive-scroll */
218 | html.has-scroll-smooth {
219 | overflow: hidden;
220 | }
221 |
222 | html.has-scroll-dragging {
223 | -webkit-user-select: none;
224 | -moz-user-select: none;
225 | -ms-user-select: none;
226 | user-select: none;
227 | }
228 |
229 | .has-scroll-smooth body {
230 | overflow: hidden;
231 | }
232 |
233 | .has-scroll-smooth [data-scroll-container] {
234 | min-height: 100vh;
235 | }
236 |
237 | [data-scroll-direction="horizontal"] [data-scroll-container] {
238 | white-space: nowrap;
239 | height: 100vh;
240 | display: inline-block;
241 | white-space: nowrap;
242 | }
243 |
244 | [data-scroll-direction="horizontal"] [data-scroll-section] {
245 | display: inline-block;
246 | vertical-align: top;
247 | white-space: nowrap;
248 | height: 100%;
249 | }
250 |
251 | .c-scrollbar {
252 | position: absolute;
253 | right: 0;
254 | top: 0;
255 | width: 11px;
256 | height: 100%;
257 | transform-origin: center right;
258 | transition: transform 0.3s, opacity 0.3s;
259 | opacity: 0;
260 | }
261 |
262 | .c-scrollbar:hover {
263 | transform: scaleX(1.45);
264 | }
265 |
266 | .c-scrollbar:hover,
267 | .has-scroll-scrolling .c-scrollbar,
268 | .has-scroll-dragging .c-scrollbar {
269 | opacity: 1;
270 | }
271 |
272 | [data-scroll-direction="horizontal"] .c-scrollbar {
273 | width: 100%;
274 | height: 10px;
275 | top: auto;
276 | bottom: 0;
277 | transform: scaleY(1);
278 | }
279 |
280 | [data-scroll-direction="horizontal"] .c-scrollbar:hover {
281 | transform: scaleY(1.3);
282 | }
283 |
284 | .c-scrollbar_thumb {
285 | position: absolute;
286 | top: 0;
287 | right: 0;
288 | background-color: black;
289 | opacity: 0.5;
290 | width: 7px;
291 | border-radius: 10px;
292 | margin: 2px;
293 | cursor: -webkit-grab;
294 | cursor: grab;
295 | }
296 |
297 | .has-scroll-dragging .c-scrollbar_thumb {
298 | cursor: -webkit-grabbing;
299 | cursor: grabbing;
300 | }
301 |
302 | [data-scroll-direction="horizontal"] .c-scrollbar_thumb {
303 | right: auto;
304 | bottom: 0;
305 | }
--------------------------------------------------------------------------------
/dist/demo1.ea7e4db3.css:
--------------------------------------------------------------------------------
1 | .title {
2 | grid-area: content;
3 | font-size: 15vw;
4 | margin: 0;
5 | font-family: ivypresto-headline, serif;
6 | font-weight: 100;
7 | justify-self: center;
8 | align-self: center;
9 | position: relative;
10 | z-index: 1500;
11 | color: #c5681c;
12 | pointer-events: none;
13 | width: 100%;
14 | height: 100vh;
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | align-self: start;
19 | }
20 |
21 | .js .title {
22 | opacity: 0;
23 | }
24 |
25 | .gallery {
26 | grid-area: content;
27 | justify-self: center;
28 | }
29 |
30 | .gallery__item {
31 | position: relative;
32 | margin: 25vh 0;
33 | display: grid;
34 | grid-template-areas: 'gallery-caption' 'gallery-image';
35 | grid-template-columns: 100%;
36 | }
37 |
38 | .gallery__item-img {
39 | grid-area: gallery-image;
40 | width: calc(100vw - 30vw);
41 | max-width: 375px;
42 | overflow: hidden;
43 | position: relative;
44 | will-change: transform, opacity;
45 | cursor: pointer;
46 | }
47 |
48 | .noscroll .gallery__item-img {
49 | cursor: default;
50 | }
51 |
52 | .js .gallery__item-img {
53 | opacity: 0;
54 | }
55 |
56 | .gallery__item-imginner {
57 | background-size: cover;
58 | background-position: 50% 0;
59 | width: 100%;
60 | padding-bottom: 140%;
61 | }
62 |
63 | .gallery__item-caption {
64 | grid-area: gallery-caption;
65 | padding: 0 0 1rem;
66 | }
67 |
68 | .gallery__item-title {
69 | position: absolute;
70 | left: 0;
71 | width: 100%;
72 | text-align: center;
73 | font-size: 10vw;
74 | font-family: ivypresto-headline, serif;
75 | font-weight: 100;
76 | display: flex;
77 | justify-content: center;
78 | margin: 0;
79 | bottom: 15%;
80 | pointer-events: none;
81 | }
82 |
83 | .gallery__item-title span.char {
84 | opacity: 0;
85 | will-change: transform;
86 | }
87 |
88 | .gallery__item-number {
89 | font-family: ivypresto-headline, serif;
90 | font-weight: 100;
91 | font-size: 2.5rem;
92 | border-bottom: 1px solid #494444;
93 | width: 100%;
94 | display: block;
95 | margin-bottom: 1rem;
96 | }
97 |
98 | .gallery__item-text {
99 | margin: 0;
100 | }
101 |
102 | .gallery__item-number,
103 | .gallery__item-text {
104 | opacity: 0;
105 | will-change: transform, opacity;
106 | }
107 |
108 | @media screen and (min-width: 53em) {
109 | .gallery__item {
110 | grid-template-areas: 'gallery-filler gallery-image gallery-caption';
111 | grid-template-columns: 15vw auto 15vw;
112 | }
113 | .gallery__item-caption {
114 | padding: 0 0 0 1rem;
115 | }
116 | }
--------------------------------------------------------------------------------
/dist/demo2.06b37b5f.css:
--------------------------------------------------------------------------------
1 | body {
2 | --color-text: #131212;
3 | --color-bg: #eee7e0;
4 | --color-link: #52433e;
5 | --color-link-hover: #131212;
6 | }
7 |
8 | .content {
9 | padding: 20vh 1rem 4rem;
10 | }
11 |
12 | .content__title {
13 | font-family: ivypresto-headline, serif;
14 | font-weight: 700;
15 | font-size: 3rem;
16 | margin: 0;
17 | }
18 |
19 | .content__text {
20 | font-weight: 300;
21 | max-width: 500px;
22 | }
23 |
24 | .js .content__title,
25 | .js .content__text {
26 | opacity: 0;
27 | }
28 |
29 | .gallery {
30 | display: grid;
31 | grid-template-columns: repeat(3,minmax(min-content,320px));
32 | grid-column-gap: 2rem;
33 | grid-row-gap: 5vh;
34 | padding: 4rem 0 20rem;
35 | }
36 |
37 | .gallery__item {
38 | margin: 0;
39 | display: grid;
40 | grid-template-areas: 'gallery-caption' 'gallery-image';
41 | grid-template-columns: 100%;
42 | }
43 |
44 | .gallery__item-img {
45 | grid-area: gallery-image;
46 | width: 100%;
47 | overflow: hidden;
48 | position: relative;
49 | will-change: transform, opacity;
50 | transform: translateZ(0);
51 | }
52 |
53 | .js .gallery__item-img {
54 | opacity: 0;
55 | }
56 |
57 | .gallery__item-imginner {
58 | background-size: cover;
59 | background-position: 50% 0;
60 | width: 100%;
61 | padding-bottom: 140%;
62 | will-change: transform;
63 | }
64 |
65 | .gallery__item-caption {
66 | grid-area: gallery-caption;
67 | }
68 |
69 | .gallery__item-title {
70 | font-size: 0.9rem;
71 | margin: 1rem 0 0.5rem;
72 | font-weight: 500;
73 | display: inline-block;
74 | }
75 |
76 | .gallery__item-title span.char {
77 | opacity: 0;
78 | will-change: transform;
79 | }
80 |
81 | @media screen and (min-width: 53em) {
82 | .content {
83 | padding: 20vh 5rem 4rem;
84 | }
85 | .gallery__item-title {
86 | font-size: 1.5rem;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/dist/favicon.26242483.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/dist/favicon.26242483.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Stack Intro Animation | Demo 1 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Image Stack Intro Animation
30 |
34 |
39 |
40 | Nouveau
41 |
42 |
43 |
44 |
45 | Sommerset
46 | 01
47 | Amalia Lynn
48 | 1988
49 |
50 |
51 |
52 |
53 |
54 | Newhouse
55 | 02
56 | Kendra Free
57 | 1989
58 |
59 |
60 |
61 |
62 |
63 | Dorrance
64 | 03
65 | Candice Barber
66 | 1990
67 |
68 |
69 |
70 |
71 |
72 | Johansson
73 | 04
74 | Charlotte Wallace
75 | 1992
76 |
77 |
78 |
79 |
80 |
81 | Goldhand
82 | 05
83 | Laura Greenberg
84 | 1993
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/dist/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Stack Intro Animation | Demo 2 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
Image Stack Intro Animation
30 |
34 |
39 |
40 |
41 |
Pleasure City
42 |
It is difficult not to feel that the unconscious aim in the most typical modern pleasure resorts is a return to the womb. For there, too, one was never alone, one never saw daylight, the temperature was always regulated, one did not have to worry about work or food, and one's thoughts, if any, were drowned by a continuous rhythmic throbbing.
43 |
44 |
45 |
46 |
47 | Questrial
48 |
49 |
50 |
51 |
52 |
53 | Coriolis
54 |
55 |
56 |
57 |
58 |
59 | Eclipse
60 |
61 |
62 |
63 |
64 |
65 | Horizon
66 |
67 |
68 |
69 |
70 |
71 | Gibbous
72 |
73 |
74 |
75 |
76 |
77 | Interstellar
78 |
79 |
80 |
81 |
82 |
83 | Neutron
84 |
85 |
86 |
87 |
88 |
89 | Orbital
90 |
91 |
92 |
93 |
94 |
95 | Supernova
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "imagestackgrid",
3 | "version": "1.0.0",
4 | "description": "*How to use this template:*",
5 | "main": "src/js/index.js",
6 | "scripts": {
7 | "start": "parcel src/index.html --open",
8 | "clean": "rm -rf dist/*",
9 | "build:parcel": "parcel build src/index.html --no-content-hash --no-minify --no-source-maps --public-url ./",
10 | "build": "npm run clean && npm run build:parcel"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git://github.com/codrops/%5BNAME%5D.git"
15 | },
16 | "keywords": [],
17 | "author": "Codrops",
18 | "license": "MIT",
19 | "homepage": "http://[HOMEPAGE]",
20 | "bugs": {
21 | "url": "https://github.com/codrops/[NAME]/issues"
22 | },
23 | "dependencies": {
24 | "gsap": "^3.5.1",
25 | "imagesloaded": "^4.1.4",
26 | "locomotive-scroll": "^4.0.4",
27 | "parcel-bundler": "^1.12.4",
28 | "splitting": "^1.0.6"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 15px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #f8f6f3;
14 | --color-bg: #0c0c0c;
15 | --color-link: #7d6350;
16 | --color-link-hover: #f8f6f3;
17 | color: var(--color-text);
18 | background-color: var(--color-bg);
19 | --cursor-stroke: none;
20 | --cursor-fill: #c5681c;
21 | --cursor-stroke-width: 1px;
22 | -webkit-font-smoothing: antialiased;
23 | -moz-osx-font-smoothing: grayscale;
24 | font-family: halyard-display, sans-serif;
25 | }
26 |
27 | .noscroll main {
28 | overflow: hidden;
29 | height: 100vh;
30 | }
31 |
32 | /* Page Loader */
33 | .js .loading::before,
34 | .js .loading::after {
35 | content: '';
36 | position: fixed;
37 | z-index: 1000;
38 | }
39 |
40 | .js .loading::before {
41 | top: 0;
42 | left: 0;
43 | width: 100%;
44 | height: 100%;
45 | background: var(--color-bg);
46 | }
47 |
48 | .js .loading::after {
49 | top: 50%;
50 | left: 50%;
51 | width: 60px;
52 | height: 60px;
53 | margin: -30px 0 0 -30px;
54 | border-radius: 50%;
55 | opacity: 0.4;
56 | background: var(--color-link);
57 | animation: loaderAnim 0.7s linear infinite alternate forwards;
58 | }
59 |
60 | @keyframes loaderAnim {
61 | to {
62 | opacity: 1;
63 | transform: scale3d(0.5, 0.5, 1);
64 | }
65 |
66 | }
67 |
68 | a {
69 | text-decoration: underline;
70 | color: var(--color-link);
71 | outline: none;
72 | }
73 |
74 | a:hover,
75 | a:focus {
76 | text-decoration: none;
77 | color: var(--color-link-hover);
78 | outline: none;
79 | }
80 |
81 | main {
82 | display: grid;
83 | grid-template-areas: 'frame' 'content';
84 | }
85 |
86 | .frame {
87 | padding: 1rem;
88 | text-align: center;
89 | position: relative;
90 | z-index: 1000;
91 | grid-area: frame;
92 | font-weight: 300;
93 | }
94 |
95 | .frame__title {
96 | font-size: 1rem;
97 | margin: 0 0 1rem;
98 | font-weight: 500;
99 | position: relative;
100 | padding: 0 3.5rem;
101 | }
102 |
103 | .frame__title::before,
104 | .frame__title::after {
105 | content: '';
106 | position: absolute;
107 | width: 1rem;
108 | height: 1.5rem;
109 | border: 2px solid #c5681c;
110 | left: 0;
111 | top: calc(50% - 0.75rem);
112 | }
113 |
114 | .frame__title::before {
115 | transform: rotate(-18deg);
116 | transform-origin: top right;
117 | }
118 |
119 | .frame__links {
120 | display: inline;
121 | }
122 |
123 | .frame__links a:not(:last-child),
124 | .frame__demos a:not(:last-child) {
125 | margin-right: 1rem;
126 | }
127 |
128 | .frame__demos {
129 | margin: 1rem 0;
130 | }
131 |
132 | .frame__demo--current,
133 | .frame__demo--current:hover {
134 | color: var(--color-text);
135 | text-decoration: none;
136 | }
137 |
138 | .cursor {
139 | display: none;
140 | }
141 |
142 | /* Recommended styles for Splitting */
143 | .splitting .word,
144 | .splitting .char {
145 | display: inline-block;
146 | }
147 |
148 | /* Psuedo-element chars */
149 | .splitting .char {
150 | white-space: nowrap;
151 | position: relative;
152 | }
153 |
154 | @media screen and (min-width:53em) {
155 | :root {
156 | font-size: 18px;
157 | }
158 | main {
159 | grid-template-areas: 'content';
160 | grid-template-rows: 100%;
161 | grid-template-columns: 100%;
162 | width: 100%;
163 | }
164 |
165 | .frame {
166 | padding: 1.5rem 2rem;
167 | top: 0;
168 | left: 0;
169 | width: 100%;
170 | position: absolute;
171 | display: flex;
172 | justify-content: space-between;
173 | align-items: flex-start;
174 | }
175 |
176 | .frame__title,
177 | .frame__demos {
178 | margin: 0;
179 | }
180 |
181 | .frame__demos {
182 | margin: 0 auto 0 4rem;
183 | }
184 |
185 | .frame__links {
186 | padding: 0;
187 | }
188 |
189 | .frame a {
190 | pointer-events: auto;
191 | }
192 |
193 | }
194 |
195 | @media (any-pointer:fine) {
196 | .cursor {
197 | position: fixed;
198 | top: 0;
199 | left: 0;
200 | display: block;
201 | pointer-events: none;
202 | }
203 |
204 | .cursor__inner {
205 | fill: var(--cursor-fill);
206 | stroke: var(--cursor-stroke);
207 | stroke-width: var(--cursor-stroke-width);
208 | opacity: 0.7;
209 | }
210 |
211 | .no-js .cursor {
212 | display: none;
213 | }
214 |
215 | }
216 |
217 | /*! locomotive-scroll v4.0.4 | MIT License | https://github.com/locomotivemtl/locomotive-scroll */
218 | html.has-scroll-smooth {
219 | overflow: hidden;
220 | }
221 |
222 | html.has-scroll-dragging {
223 | -webkit-user-select: none;
224 | -moz-user-select: none;
225 | -ms-user-select: none;
226 | user-select: none;
227 | }
228 |
229 | .has-scroll-smooth body {
230 | overflow: hidden;
231 | }
232 |
233 | .has-scroll-smooth [data-scroll-container] {
234 | min-height: 100vh;
235 | }
236 |
237 | [data-scroll-direction="horizontal"] [data-scroll-container] {
238 | white-space: nowrap;
239 | height: 100vh;
240 | display: inline-block;
241 | white-space: nowrap;
242 | }
243 |
244 | [data-scroll-direction="horizontal"] [data-scroll-section] {
245 | display: inline-block;
246 | vertical-align: top;
247 | white-space: nowrap;
248 | height: 100%;
249 | }
250 |
251 | .c-scrollbar {
252 | position: absolute;
253 | right: 0;
254 | top: 0;
255 | width: 11px;
256 | height: 100%;
257 | transform-origin: center right;
258 | transition: transform 0.3s, opacity 0.3s;
259 | opacity: 0;
260 | }
261 |
262 | .c-scrollbar:hover {
263 | transform: scaleX(1.45);
264 | }
265 |
266 | .c-scrollbar:hover,
267 | .has-scroll-scrolling .c-scrollbar,
268 | .has-scroll-dragging .c-scrollbar {
269 | opacity: 1;
270 | }
271 |
272 | [data-scroll-direction="horizontal"] .c-scrollbar {
273 | width: 100%;
274 | height: 10px;
275 | top: auto;
276 | bottom: 0;
277 | transform: scaleY(1);
278 | }
279 |
280 | [data-scroll-direction="horizontal"] .c-scrollbar:hover {
281 | transform: scaleY(1.3);
282 | }
283 |
284 | .c-scrollbar_thumb {
285 | position: absolute;
286 | top: 0;
287 | right: 0;
288 | background-color: black;
289 | opacity: 0.5;
290 | width: 7px;
291 | border-radius: 10px;
292 | margin: 2px;
293 | cursor: -webkit-grab;
294 | cursor: grab;
295 | }
296 |
297 | .has-scroll-dragging .c-scrollbar_thumb {
298 | cursor: -webkit-grabbing;
299 | cursor: grabbing;
300 | }
301 |
302 | [data-scroll-direction="horizontal"] .c-scrollbar_thumb {
303 | right: auto;
304 | bottom: 0;
305 | }
--------------------------------------------------------------------------------
/src/css/demo1.css:
--------------------------------------------------------------------------------
1 | .title {
2 | grid-area: content;
3 | font-size: 15vw;
4 | margin: 0;
5 | font-family: ivypresto-headline, serif;
6 | font-weight: 100;
7 | justify-self: center;
8 | align-self: center;
9 | position: relative;
10 | z-index: 1500;
11 | color: #c5681c;
12 | pointer-events: none;
13 | width: 100%;
14 | height: 100vh;
15 | display: flex;
16 | align-items: center;
17 | justify-content: center;
18 | align-self: start;
19 | }
20 |
21 | .js .title {
22 | opacity: 0;
23 | }
24 |
25 | .gallery {
26 | grid-area: content;
27 | justify-self: center;
28 | }
29 |
30 | .gallery__item {
31 | position: relative;
32 | margin: 25vh 0;
33 | display: grid;
34 | grid-template-areas: 'gallery-caption' 'gallery-image';
35 | grid-template-columns: 100%;
36 | }
37 |
38 | .gallery__item-img {
39 | grid-area: gallery-image;
40 | width: calc(100vw - 30vw);
41 | max-width: 375px;
42 | overflow: hidden;
43 | position: relative;
44 | will-change: transform, opacity;
45 | cursor: pointer;
46 | }
47 |
48 | .noscroll .gallery__item-img {
49 | cursor: default;
50 | }
51 |
52 | .js .gallery__item-img {
53 | opacity: 0;
54 | }
55 |
56 | .gallery__item-imginner {
57 | background-size: cover;
58 | background-position: 50% 0;
59 | width: 100%;
60 | padding-bottom: 140%;
61 | }
62 |
63 | .gallery__item-caption {
64 | grid-area: gallery-caption;
65 | padding: 0 0 1rem;
66 | }
67 |
68 | .gallery__item-title {
69 | position: absolute;
70 | left: 0;
71 | width: 100%;
72 | text-align: center;
73 | font-size: 10vw;
74 | font-family: ivypresto-headline, serif;
75 | font-weight: 100;
76 | display: flex;
77 | justify-content: center;
78 | margin: 0;
79 | bottom: 15%;
80 | pointer-events: none;
81 | }
82 |
83 | .gallery__item-title span.char {
84 | opacity: 0;
85 | will-change: transform;
86 | }
87 |
88 | .gallery__item-number {
89 | font-family: ivypresto-headline, serif;
90 | font-weight: 100;
91 | font-size: 2.5rem;
92 | border-bottom: 1px solid #494444;
93 | width: 100%;
94 | display: block;
95 | margin-bottom: 1rem;
96 | }
97 |
98 | .gallery__item-text {
99 | margin: 0;
100 | }
101 |
102 | .gallery__item-number,
103 | .gallery__item-text {
104 | opacity: 0;
105 | will-change: transform, opacity;
106 | }
107 |
108 | @media screen and (min-width: 53em) {
109 | .gallery__item {
110 | grid-template-areas: 'gallery-filler gallery-image gallery-caption';
111 | grid-template-columns: 15vw auto 15vw;
112 | }
113 | .gallery__item-caption {
114 | padding: 0 0 0 1rem;
115 | }
116 | }
--------------------------------------------------------------------------------
/src/css/demo2.css:
--------------------------------------------------------------------------------
1 | body {
2 | --color-text: #131212;
3 | --color-bg: #eee7e0;
4 | --color-link: #52433e;
5 | --color-link-hover: #131212;
6 | }
7 |
8 | .content {
9 | padding: 20vh 1rem 4rem;
10 | }
11 |
12 | .content__title {
13 | font-family: ivypresto-headline, serif;
14 | font-weight: 700;
15 | font-size: 3rem;
16 | margin: 0;
17 | }
18 |
19 | .content__text {
20 | font-weight: 300;
21 | max-width: 500px;
22 | }
23 |
24 | .js .content__title,
25 | .js .content__text {
26 | opacity: 0;
27 | }
28 |
29 | .gallery {
30 | display: grid;
31 | grid-template-columns: repeat(3,minmax(min-content,320px));
32 | grid-column-gap: 2rem;
33 | grid-row-gap: 5vh;
34 | padding: 4rem 0 20rem;
35 | }
36 |
37 | .gallery__item {
38 | margin: 0;
39 | display: grid;
40 | grid-template-areas: 'gallery-caption' 'gallery-image';
41 | grid-template-columns: 100%;
42 | }
43 |
44 | .gallery__item-img {
45 | grid-area: gallery-image;
46 | width: 100%;
47 | overflow: hidden;
48 | position: relative;
49 | will-change: transform, opacity;
50 | transform: translateZ(0);
51 | }
52 |
53 | .js .gallery__item-img {
54 | opacity: 0;
55 | }
56 |
57 | .gallery__item-imginner {
58 | background-size: cover;
59 | background-position: 50% 0;
60 | width: 100%;
61 | padding-bottom: 140%;
62 | will-change: transform;
63 | }
64 |
65 | .gallery__item-caption {
66 | grid-area: gallery-caption;
67 | }
68 |
69 | .gallery__item-title {
70 | font-size: 0.9rem;
71 | margin: 1rem 0 0.5rem;
72 | font-weight: 500;
73 | display: inline-block;
74 | }
75 |
76 | .gallery__item-title span.char {
77 | opacity: 0;
78 | will-change: transform;
79 | }
80 |
81 | @media screen and (min-width: 53em) {
82 | .content {
83 | padding: 20vh 5rem 4rem;
84 | }
85 | .gallery__item-title {
86 | font-size: 1.5rem;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/favicon.ico
--------------------------------------------------------------------------------
/src/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/1.jpg
--------------------------------------------------------------------------------
/src/img/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/10.jpg
--------------------------------------------------------------------------------
/src/img/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/11.jpg
--------------------------------------------------------------------------------
/src/img/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/12.jpg
--------------------------------------------------------------------------------
/src/img/13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/13.jpg
--------------------------------------------------------------------------------
/src/img/14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/14.jpg
--------------------------------------------------------------------------------
/src/img/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/2.jpg
--------------------------------------------------------------------------------
/src/img/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/3.jpg
--------------------------------------------------------------------------------
/src/img/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/4.jpg
--------------------------------------------------------------------------------
/src/img/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/5.jpg
--------------------------------------------------------------------------------
/src/img/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/6.jpg
--------------------------------------------------------------------------------
/src/img/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/7.jpg
--------------------------------------------------------------------------------
/src/img/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/8.jpg
--------------------------------------------------------------------------------
/src/img/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/ImageStackGrid/0113fac4877a3a6b2db33a7f118faef2df979078/src/img/9.jpg
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Stack Intro Animation | Demo 1 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
Image Stack Intro Animation
22 |
26 |
31 |
32 | Nouveau
33 |
34 |
35 |
36 |
37 | Sommerset
38 | 01
39 | Amalia Lynn
40 | 1988
41 |
42 |
43 |
44 |
45 |
46 | Newhouse
47 | 02
48 | Kendra Free
49 | 1989
50 |
51 |
52 |
53 |
54 |
55 | Dorrance
56 | 03
57 | Candice Barber
58 | 1990
59 |
60 |
61 |
62 |
63 |
64 | Johansson
65 | 04
66 | Charlotte Wallace
67 | 1992
68 |
69 |
70 |
71 |
72 |
73 | Goldhand
74 | 05
75 | Laura Greenberg
76 | 1993
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Stack Intro Animation | Demo 2 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
Image Stack Intro Animation
22 |
26 |
31 |
32 |
33 |
Pleasure City
34 |
It is difficult not to feel that the unconscious aim in the most typical modern pleasure resorts is a return to the womb. For there, too, one was never alone, one never saw daylight, the temperature was always regulated, one did not have to worry about work or food, and one's thoughts, if any, were drowned by a continuous rhythmic throbbing.
35 |
36 |
37 |
38 |
39 | Questrial
40 |
41 |
42 |
43 |
44 |
45 | Coriolis
46 |
47 |
48 |
49 |
50 |
51 | Eclipse
52 |
53 |
54 |
55 |
56 |
57 | Horizon
58 |
59 |
60 |
61 |
62 |
63 | Gibbous
64 |
65 |
66 |
67 |
68 |
69 | Interstellar
70 |
71 |
72 |
73 |
74 |
75 | Neutron
76 |
77 |
78 |
79 |
80 |
81 | Orbital
82 |
83 |
84 |
85 |
86 |
87 | Supernova
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/src/js/cursor.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 | import { lerp, getMousePos } from './utils';
3 |
4 | // Track the mouse position
5 | let mouse = {x: 0, y: 0};
6 | window.addEventListener('mousemove', ev => mouse = getMousePos(ev));
7 |
8 | export default class Cursor {
9 | constructor(el) {
10 | this.DOM = {el: el};
11 | this.DOM.el.style.opacity = 0;
12 |
13 | this.bounds = this.DOM.el.getBoundingClientRect();
14 |
15 | this.renderedStyles = {
16 | tx: {previous: 0, current: 0, amt: 0.2},
17 | ty: {previous: 0, current: 0, amt: 0.2},
18 | scale: {previous: 1, current: 1, amt: 0.15},
19 | //opacity: {previous: 1, current: 1, amt: 0.1}
20 | };
21 |
22 | this.onMouseMoveEv = () => {
23 | this.renderedStyles.tx.previous = this.renderedStyles.tx.current = mouse.x - this.bounds.width/2;
24 | this.renderedStyles.ty.previous = this.renderedStyles.ty.previous = mouse.y - this.bounds.height/2;
25 | gsap.to(this.DOM.el, {duration: 0.9, ease: 'Power3.easeOut', opacity: 1});
26 | requestAnimationFrame(() => this.render());
27 | window.removeEventListener('mousemove', this.onMouseMoveEv);
28 | };
29 | window.addEventListener('mousemove', this.onMouseMoveEv);
30 | }
31 | enter() {
32 | this.renderedStyles['scale'].current = 2.5;
33 | //this.renderedStyles['opacity'].current = 0.5;
34 | }
35 | leave() {
36 | this.renderedStyles['scale'].current = 1;
37 | //this.renderedStyles['opacity'].current = 1;
38 | }
39 | render() {
40 | this.renderedStyles['tx'].current = mouse.x - this.bounds.width/2;
41 | this.renderedStyles['ty'].current = mouse.y - this.bounds.height/2;
42 |
43 | for (const key in this.renderedStyles ) {
44 | this.renderedStyles[key].previous = lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);
45 | }
46 |
47 | this.DOM.el.style.transform = `translateX(${(this.renderedStyles['tx'].previous)}px) translateY(${this.renderedStyles['ty'].previous}px) scale(${this.renderedStyles['scale'].previous})`;
48 | //this.DOM.el.style.opacity = this.renderedStyles['opacity'].previous;
49 |
50 | requestAnimationFrame(() => this.render());
51 | }
52 | }
--------------------------------------------------------------------------------
/src/js/demo1/galleryController.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 | import { calcWinsize, getRandomInteger } from '../utils';
3 | import GalleryItem from './galleryItem';
4 | import LocomotiveScroll from 'locomotive-scroll';
5 | import Splitting from "splitting";
6 |
7 | // Call the splittingjs to transform the data-splitting texts to spans of chars
8 | Splitting();
9 |
10 | // Initialize the Locomotive scroll
11 | const scroll = new LocomotiveScroll({
12 | el: document.querySelector('[data-scroll-container]'),
13 | smooth: true
14 | });
15 |
16 | // Calculate the viewport size
17 | let winsize = calcWinsize();
18 | window.addEventListener('resize', () => winsize = calcWinsize());
19 |
20 | export default class GalleryController {
21 | constructor(galleryEl) {
22 | this.DOM = {
23 | galleryEl: galleryEl,
24 | title: document.querySelector('.title')
25 | };
26 | this.DOM.titleChars = this.DOM.title.querySelectorAll('.char');
27 | this.titleCharsTotal = this.DOM.titleChars.length;
28 | this.DOM.galleryItemElems = [...this.DOM.galleryEl.querySelectorAll('.gallery__item')];
29 | this.galleryItems = [];
30 | this.DOM.galleryItemElems.forEach(el => this.galleryItems.push(new GalleryItem(el)));
31 | this.itemsTotal = this.galleryItems.length;
32 |
33 | this.intro();
34 | }
35 | intro() {
36 | // create the timeline
37 | // let's start by animating the main intro text
38 | const timeline = gsap.timeline().to(this.DOM.title, {
39 | duration: 1,
40 | ease: 'expo',
41 | startAt: {y: '10%'},
42 | y: '0%',
43 | opacity: 1
44 | }, 0);
45 |
46 | // now let's center the images (stack)
47 | for (const [pos, item] of this.galleryItems.entries()) {
48 | timeline.set(item.DOM.img, {
49 | x: winsize.width/2 - item.imgRect.left - item.imgRect.width/2,
50 | y: winsize.height/2 - item.imgRect.top - item.imgRect.height/2,
51 | scale: 0.6,
52 | rotation: getRandomInteger(-10,10),
53 | opacity: 1,
54 | delay: 0.2*pos
55 | }, 0);
56 |
57 | // for the first image, we set a high scale for the inner image element
58 | // later we will animate this scale value together with the scale value of the outer image
59 | if ( pos === 0 ) {
60 | timeline.set(item.DOM.imgInner, {
61 | scale: 1.8
62 | }, 0);
63 | }
64 | }
65 |
66 | // access the first and other images in the stack
67 | const [firstImage, ...otherImages] = this.galleryItems.map(el => el.DOM.img);
68 |
69 | timeline
70 | .addLabel('startAnimation', '+=0')
71 | // allow scroll and update the locomotive scroll
72 | .add(() => {
73 | document.body.classList.remove('noscroll');
74 | scroll.update();
75 | }, 'startAnimation')
76 |
77 | // animate the main title characters out and fade them out too
78 | .to(this.DOM.titleChars, {
79 | duration: 1,
80 | ease: 'expo',
81 | x: (pos, target) => {
82 | return -40*(Math.floor(this.titleCharsTotal/2)-pos);
83 | },
84 | opacity: 0,
85 | stagger: {from: 'center'}
86 | }, 'startAnimation')
87 |
88 | // the other images in the stack will animate its translation values randomly
89 | .to(otherImages, {
90 | duration: 1,
91 | ease: 'power3',
92 | x: () => '+='+getRandomInteger(-200,200)+'%',
93 | y: () => '+='+getRandomInteger(-200,200)+'%',
94 | opacity: 0,
95 | rotation: () => getRandomInteger(-20,20)
96 | }, 'startAnimation')
97 | // and then we make them appear in their final position in the grid
98 | .to(otherImages, {
99 | duration: 0.5,
100 | ease: 'expo',
101 | startAt: {
102 | x: 0,
103 | y: 0,
104 | rotation: 0,
105 | scale: 0.8
106 | },
107 | scale: 1,
108 | opacity: 1
109 | })
110 |
111 | // the first image will now animate to it's final position
112 | .to(firstImage, {
113 | duration: 1.2,
114 | ease: 'expo',
115 | x: 0,
116 | y: 0,
117 | scale: 1,
118 | rotation: 0,
119 | opacity: 1
120 | }, 'startAnimation')
121 | // both the image and inner image animate the scale value to achieve the "reveal effect"
122 | .to(this.galleryItems[0].DOM.imgInner, {
123 | duration: 1.2,
124 | ease: 'expo',
125 | scale: 1
126 | }, 'startAnimation');
127 |
128 | // finally, animate the gallery item's content elements (title, number and texts)
129 | for (const [pos, item] of this.galleryItems.entries()) {
130 | timeline
131 | .add( () => item.inStack = false, 'startAnimation+=1' )
132 | .to(item.DOM.captionChars, {
133 | duration: 2,
134 | ease: 'expo',
135 | startAt: {
136 | opacity: 0,
137 | x: pos => -40*(Math.floor(item.captionCharsTotal/2)-pos)
138 | },
139 | x: 0,
140 | opacity: 1,
141 | stagger: {from: 'center'}
142 | }, 'startAnimation')
143 | .to([item.DOM.caption.number,item.DOM.caption.texts], {
144 | duration: 1,
145 | ease: 'power1',
146 | startAt: {opacity: 0},
147 | opacity: 1,
148 | stagger: 0.04
149 | }, 'startAnimation+=0.3')
150 | }
151 | }
152 | }
--------------------------------------------------------------------------------
/src/js/demo1/galleryItem.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 |
3 | export default class GalleryItem {
4 | constructor(el) {
5 | this.DOM = {el: el};
6 | this.DOM.img = this.DOM.el.querySelector('.gallery__item-img');
7 | this.DOM.imgInner = this.DOM.img.querySelector('.gallery__item-imginner');
8 | this.DOM.caption = {
9 | title: this.DOM.el.querySelector('.gallery__item-title'),
10 | number: this.DOM.el.querySelector('.gallery__item-number'),
11 | texts: this.DOM.el.querySelectorAll('.gallery__item-text')
12 | };
13 | this.DOM.captionChars = this.DOM.caption.title.querySelectorAll('.char');
14 | this.captionCharsTotal = this.DOM.captionChars.length;
15 | this.imgRect = this.DOM.img.getBoundingClientRect();
16 | // part of the stack
17 | this.inStack = true;
18 | this.initEvents();
19 | }
20 | initEvents() {
21 | // on hover, scale in/out the image and inner image elements and also the caption titles
22 | this.onMouseEnterFn = () => {
23 | if ( this.inStack ) return false;
24 | gsap
25 | .timeline({defaults: {duration: 1, ease: 'expo'}})
26 | .to(this.DOM.img, {scale: 0.95})
27 | .to(this.DOM.imgInner, {scale: 1.2}, 0)
28 | .to(this.DOM.captionChars, {
29 | x: pos => -10*(Math.floor(this.captionCharsTotal/2)-pos),
30 | stagger: {from: 'center'}
31 | }, 0);
32 | };
33 | this.onMouseLeaveFn = () => {
34 | if ( this.inStack ) return false;
35 | gsap
36 | .timeline({defaults: {duration: 1, ease: 'expo'}})
37 | .to([this.DOM.img, this.DOM.imgInner], {scale: 1})
38 | .to(this.DOM.captionChars, {x: 0}, 0);
39 | };
40 | this.DOM.img.addEventListener('mouseenter', this.onMouseEnterFn);
41 | this.DOM.img.addEventListener('mouseleave', this.onMouseLeaveFn);
42 | }
43 | }
--------------------------------------------------------------------------------
/src/js/demo1/index.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, preloadFonts } from '../utils';
2 | import Cursor from '../cursor';
3 | import GalleryController from './galleryController';
4 |
5 | // Preload images and fonts
6 | Promise.all([preloadImages('.gallery__item-imginner'), preloadFonts('lty4rfv')]).then(() => {
7 | // Remove loader (loading class)
8 | document.body.classList.remove('loading');
9 |
10 | // Initialize custom cursor
11 | const cursor = new Cursor(document.querySelector('.cursor'));
12 |
13 | // Initialize the GalleryController
14 | new GalleryController(document.querySelector('.gallery'));
15 |
16 | // Mouse effects on all links and others
17 | [...document.querySelectorAll('a, .gallery__item-img')].forEach(link => {
18 | link.addEventListener('mouseenter', () => cursor.enter());
19 | link.addEventListener('mouseleave', () => cursor.leave());
20 | });
21 | });
--------------------------------------------------------------------------------
/src/js/demo2/galleryController.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 | import { calcWinsize, getRandomInteger } from '../utils';
3 | import GalleryItem from './galleryItem';
4 | import LocomotiveScroll from 'locomotive-scroll';
5 | import Splitting from "splitting";
6 |
7 | Splitting();
8 |
9 | // Initialize the Locomotive scroll
10 | const scroll = new LocomotiveScroll({
11 | el: document.querySelector('[data-scroll-container]'),
12 | smooth: true
13 | });
14 |
15 | // Calculate the viewport size
16 | let winsize = calcWinsize();
17 | window.addEventListener('resize', () => winsize = calcWinsize());
18 |
19 | export default class GalleryController {
20 | constructor(galleryEl) {
21 | this.DOM = {
22 | galleryEl: galleryEl,
23 | title: document.querySelector('.content__title'),
24 | text: document.querySelector('.content__text'),
25 | };
26 | this.DOM.galleryItemElems = [...this.DOM.galleryEl.querySelectorAll('.gallery__item')];
27 | this.galleryItems = [];
28 | this.DOM.galleryItemElems.forEach(el => this.galleryItems.push(new GalleryItem(el)));
29 | this.itemsTotal = this.galleryItems.length;
30 |
31 | this.intro();
32 | }
33 | intro() {
34 | for (const [pos, item] of this.galleryItems.entries()) {
35 | gsap.set(item.DOM.el, {zIndex: this.itemsTotal-1-pos});
36 | }
37 |
38 | gsap.to(this.DOM.galleryEl, {
39 | duration: 1.2,
40 | ease: 'expo',
41 | startAt: {y: '4%'},
42 | y: '0%'
43 | });
44 |
45 | // access the first and other images in the stack
46 | const [firstImage,secondImage,thirdImage, ...otherImages] = this.galleryItems.map(el => el.DOM.img);
47 | this.galleryItems.reverse();
48 |
49 | const timeline = gsap.timeline();
50 |
51 | // first let's center the images
52 | for (const [pos, item] of this.galleryItems.entries()) {
53 | timeline.set(item.DOM.img, {
54 | x: winsize.width/2 - item.imgRect.left - item.imgRect.width/2,
55 | y: winsize.height/2 - item.imgRect.top - item.imgRect.height/2,
56 | scale: 0.6,
57 | rotation: getRandomInteger(-10,10),
58 | opacity: 1,
59 | delay: 0.1*pos
60 | }, 0);
61 |
62 | if ( pos >= this.itemsTotal-3 ) {
63 | timeline.set(item.DOM.imgInner, {
64 | scale: 1.8
65 | }, 0);
66 | }
67 | else {
68 | timeline.set(item.DOM.img, {
69 | opacity: 0,
70 | delay: 0.1*pos
71 | }, 0.3);
72 | }
73 | }
74 |
75 | timeline
76 | .addLabel('startAnimation', '+=0.1')
77 | .add(() => {
78 | document.body.classList.remove('noscroll');
79 | scroll.update();
80 | }, 'startAnimation')
81 |
82 | .to([firstImage,secondImage,thirdImage], {
83 | duration: 1.2,
84 | ease: 'expo',
85 | x: 0,
86 | y: 0,
87 | scale: 1,
88 | rotation: 0,
89 | opacity: 1
90 | }, 'startAnimation')
91 | .to(this.galleryItems.filter((_,pos) => pos >= this.itemsTotal-3).map(item => item.DOM.imgInner), {
92 | duration: 1.2,
93 | ease: 'expo',
94 | scale: 1
95 | }, 'startAnimation')
96 |
97 | .to(otherImages, {
98 | duration: 1.2,
99 | ease: 'expo',
100 | startAt: {
101 | x: 0,
102 | y: 0,
103 | rotation: 0,
104 | scale: 0.8
105 | },
106 | scale: 1,
107 | opacity: 1
108 | }, 'startAnimation');
109 |
110 | for (const item of this.galleryItems) {
111 | timeline
112 | .add( () => item.inStack = false, 'startAnimation+=1.2' )
113 | .to(item.DOM.captionChars, {
114 | duration: 1,
115 | ease: 'expo',
116 | startAt: {
117 | opacity: 0,
118 | y: '40%'
119 | },
120 | y: '0%',
121 | opacity: 1,
122 | stagger: 0.03
123 | }, 'startAnimation+=0.5')
124 | }
125 |
126 | timeline.to([this.DOM.title, this.DOM.text], {
127 | duration: 1.2,
128 | ease: 'expo',
129 | startAt: {
130 | y: '50%'
131 | },
132 | y: '0%',
133 | opacity: 1,
134 | stagger: 0.04
135 | }, 'startAnimation+=0.2');
136 | }
137 | }
--------------------------------------------------------------------------------
/src/js/demo2/galleryItem.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 |
3 | export default class GalleryItem {
4 | constructor(el) {
5 | this.DOM = {el: el};
6 | this.DOM.img = this.DOM.el.querySelector('.gallery__item-img');
7 | this.DOM.imgInner = this.DOM.img.querySelector('.gallery__item-imginner');
8 | this.DOM.caption = {
9 | title: this.DOM.el.querySelector('.gallery__item-caption > .gallery__item-title'),
10 | };
11 | this.DOM.captionChars = this.DOM.caption.title.querySelectorAll('.char');
12 | this.captionCharsTotal = this.DOM.captionChars.length;
13 | this.imgRect = this.DOM.img.getBoundingClientRect();
14 | this.captionRect = this.DOM.caption.title.getBoundingClientRect();
15 | // part of the stack
16 | this.inStack = true;
17 | this.initEvents();
18 | }
19 | initEvents() {
20 | this.onMouseEnterFn = () => {
21 | gsap.killTweensOf(this.DOM.captionChars);
22 | if ( this.inStack ) return false;
23 | gsap
24 | .timeline({defaults: {duration: 1, ease: 'expo'}})
25 | .to(this.DOM.img, {scale: 0.95})
26 | .to(this.DOM.imgInner, {scale: 1.2}, 0)
27 | .to(this.DOM.captionChars, {
28 | x: pos => this.imgRect.width-this.captionRect.width*1.1,
29 | stagger: -0.02
30 | }, 0);
31 | };
32 | this.onMouseLeaveFn = () => {
33 | gsap.killTweensOf(this.DOM.captionChars);
34 | if ( this.inStack ) return false;
35 | gsap
36 | .timeline({defaults: {duration: 1, ease: 'expo'}})
37 | .to([this.DOM.img, this.DOM.imgInner], {scale: 1})
38 | .to(this.DOM.captionChars, {x: 0}, 0);
39 | };
40 | this.DOM.img.addEventListener('mouseenter', this.onMouseEnterFn);
41 | this.DOM.img.addEventListener('mouseleave', this.onMouseLeaveFn);
42 | }
43 | }
--------------------------------------------------------------------------------
/src/js/demo2/index.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, preloadFonts } from '../utils';
2 | import Cursor from '../cursor';
3 | import GalleryController from './galleryController';
4 |
5 | // Preload images and fonts
6 | Promise.all([preloadImages('.gallery__item-imginner'), preloadFonts('lty4rfv')]).then(() => {
7 | // Remove loader (loading class)
8 | document.body.classList.remove('loading');
9 |
10 | // Initialize custom cursor
11 | const cursor = new Cursor(document.querySelector('.cursor'));
12 |
13 | // Initialize the GalleryController
14 | new GalleryController(document.querySelector('.gallery'));
15 |
16 | // Mouse effects on all links and others
17 | [...document.querySelectorAll('a, .gallery__item-img')].forEach(link => {
18 | link.addEventListener('mouseenter', () => cursor.enter());
19 | link.addEventListener('mouseleave', () => cursor.leave());
20 | });
21 | });
--------------------------------------------------------------------------------
/src/js/utils.js:
--------------------------------------------------------------------------------
1 | // Map number x from range [a, b] to [c, d]
2 | const imagesLoaded = require('imagesloaded');
3 |
4 | // Linear interpolation
5 | const lerp = (a, b, n) => (1 - n) * a + n * b;
6 |
7 | // Gets the mouse position
8 | const getMousePos = e => {
9 | return {
10 | x : e.clientX,
11 | y : e.clientY
12 | };
13 | };
14 |
15 | const calcWinsize = () => {
16 | return {width: window.innerWidth, height: window.innerHeight};
17 | };
18 |
19 | const getRandomInteger = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
20 |
21 | // Preload images
22 | const preloadImages = (selector = 'img') => {
23 | return new Promise((resolve) => {
24 | imagesLoaded(document.querySelectorAll(selector), {background: true}, resolve);
25 | });
26 | };
27 |
28 | // Preload fonts
29 | const preloadFonts = (id) => {
30 | return new Promise((resolve) => {
31 | WebFont.load({
32 | typekit: {
33 | id: id
34 | },
35 | active: resolve
36 | });
37 | });
38 | };
39 |
40 | export {
41 | lerp,
42 | getMousePos,
43 | calcWinsize,
44 | getRandomInteger,
45 | preloadImages,
46 | preloadFonts
47 | };
--------------------------------------------------------------------------------