├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── 1.dc197a9a.jpg
├── 10.5e22fdc3.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
├── favicon.26242483.ico
├── index.html
└── js.00a46daa.js
├── package.json
└── src
├── css
└── base.css
├── favicon.ico
├── img
├── 1.jpg
├── 10.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
├── 6.jpg
├── 7.jpg
├── 8.jpg
└── 9.jpg
├── index.html
└── js
├── cursor.js
├── index.js
├── revealer.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
4 | .DS_Store
--------------------------------------------------------------------------------
/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 | # Rapid Image Layers Animation
2 |
3 | A rapid layers animation of images as a splash intro or page transition on a website.
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=48939)
8 |
9 | [Demo](https://tympanus.net/Development/RapidLayersAnimation/)
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 [Minh Ngọc](https://unsplash.com/@ngocntmn)
35 |
36 | ## Misc
37 |
38 | 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/)
39 |
40 | ## License
41 | [MIT](LICENSE)
42 |
43 | Made with :blue_heart: by [Codrops](http://www.codrops.com)
44 |
45 |
46 |
--------------------------------------------------------------------------------
/dist/1.dc197a9a.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/1.dc197a9a.jpg
--------------------------------------------------------------------------------
/dist/10.5e22fdc3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/10.5e22fdc3.jpg
--------------------------------------------------------------------------------
/dist/2.3ca6bb44.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/2.3ca6bb44.jpg
--------------------------------------------------------------------------------
/dist/3.a433b89d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/3.a433b89d.jpg
--------------------------------------------------------------------------------
/dist/4.c6d96be5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/4.c6d96be5.jpg
--------------------------------------------------------------------------------
/dist/5.689b68fd.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/5.689b68fd.jpg
--------------------------------------------------------------------------------
/dist/6.e96dcfff.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/6.e96dcfff.jpg
--------------------------------------------------------------------------------
/dist/7.2d66e3ed.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/7.2d66e3ed.jpg
--------------------------------------------------------------------------------
/dist/8.f4323fe0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/8.f4323fe0.jpg
--------------------------------------------------------------------------------
/dist/9.c9233dac.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/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: 14px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #644B47;
14 | --color-bg: #0F0E0E;
15 | --color-link: #fff;
16 | --color-link-hover: #3d3637;
17 | color: var(--color-text);
18 | background-color: var(--color-bg);
19 | font-family: soleil, sans-serif;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | /* Cursor styles */
23 | --cursor-stroke: #644B47;
24 | --cursor-fill: none;
25 | --cursor-stroke-width: 1px;
26 | }
27 |
28 | /* Page Loader */
29 | .js .loading::before,
30 | .js .loading::after {
31 | content: '';
32 | position: fixed;
33 | z-index: 1000;
34 | }
35 |
36 | .js .loading::before {
37 | top: 0;
38 | left: 0;
39 | width: 100%;
40 | height: 100%;
41 | background: var(--color-bg);
42 | }
43 |
44 | .js .loading::after {
45 | top: 50%;
46 | left: 50%;
47 | width: 60px;
48 | height: 60px;
49 | margin: -30px 0 0 -30px;
50 | border-radius: 50%;
51 | opacity: 0.4;
52 | background: var(--color-link);
53 | animation: loaderAnim 0.7s linear infinite alternate forwards;
54 |
55 | }
56 |
57 | @keyframes loaderAnim {
58 | to {
59 | opacity: 1;
60 | transform: scale3d(0.5, 0.5, 1);
61 | }
62 | }
63 |
64 | a {
65 | text-decoration: none;
66 | color: var(--color-link);
67 | outline: none;
68 | }
69 |
70 | a:hover,
71 | a:focus {
72 | color: var(--color-link-hover);
73 | outline: none;
74 | }
75 |
76 | .message {
77 | background: var(--color-text);
78 | color: var(--color-bg);
79 | padding: 1rem;
80 | text-align: center;
81 | }
82 |
83 | .frame {
84 | padding: 3rem 5vw;
85 | text-align: center;
86 | position: relative;
87 | z-index: 1000;
88 | }
89 |
90 | .frame__title {
91 | font-size: 1rem;
92 | margin: 0 0 1rem;
93 | font-weight: 300;
94 | }
95 |
96 | .frame__links {
97 | display: inline;
98 | }
99 |
100 | .frame__links a:not(:last-child) {
101 | margin-right: 1rem;
102 | }
103 |
104 | .frame__button {
105 | all: unset;
106 | }
107 |
108 | .content {
109 | width: 100%;
110 | height: 400px;
111 | display: grid;
112 | justify-content: center;
113 | overflow: hidden;
114 | }
115 |
116 | .grid {
117 | grid-area: 1 / 1 / 2 / 2;
118 | width: 100%;
119 | height: 93%;
120 | margin: auto;
121 | display: grid;
122 | grid-template-columns: repeat(13,1fr);
123 | grid-template-rows: repeat(8,1fr);
124 | grid-gap: 1vw;
125 | position: relative;
126 | }
127 |
128 | .grid::after {
129 | content: '';
130 | height: 6rem;
131 | width: 1px;
132 | background: #fff;
133 | position: absolute;
134 | bottom: 2rem;
135 | left: 50%;
136 | }
137 |
138 | .intro .grid::after {
139 | display: none;
140 | }
141 |
142 | .grid__item {
143 | background-size: cover;
144 | background-position: center;
145 | opacity: 0;
146 | -webkit-filter: brightness(0.7);
147 | filter: brightness(0.7);
148 | }
149 |
150 | .grid__item--a {
151 | grid-area: 2 / 1 / 5 / 3;
152 | }
153 |
154 | .grid__item--b {
155 | grid-area: 6 / 2 / 9 / 5;
156 | }
157 |
158 | .grid__item--c {
159 | grid-area: 3 / 4 / 7 / 7;
160 | }
161 |
162 | .grid__item--d {
163 | grid-area: 1 / 8 / 4 / 10;
164 | }
165 |
166 | .grid__item--e {
167 | grid-area: 4 / 10 / 7 / 14;
168 | }
169 |
170 | .grid__item--f {
171 | grid-area: 6 / 9 / 9 / 12;
172 | }
173 |
174 | .menu {
175 | grid-area: 1 / 1 / 2 / 2;
176 | display: flex;
177 | width: 130vw;
178 | justify-content: space-between;
179 | align-self: center;
180 | justify-self: start;
181 | position: relative;
182 | }
183 |
184 | .menu__item {
185 | --color-title: #3c3a3b;
186 | --color-subtitle: #3e3c3d;
187 | --title-font-style: italic;
188 | --title-font-weight: 400;
189 | --title-font-size: 7vw;
190 | --title-display: none;
191 | --subtitle-display: none;
192 | pointer-events: none;
193 | }
194 |
195 | .menu__item-title {
196 | font-size: var(--title-font-size);
197 | font-family: meno-banner, serif;
198 | font-style: var(--title-font-style);
199 | font-weight: var(--title-font-weight);
200 | color: var(--color-title);
201 | margin: 0;
202 | display: var(--title-display);
203 | }
204 |
205 | .menu__item-subtitle {
206 | font-family: meno-banner, serif;
207 | color: var(--color-subtitle);
208 | text-align: right;
209 | margin: 0 -3rem 0 0;
210 | font-size: 1.5vw;
211 | display: var(--subtitle-display);
212 | }
213 |
214 | .menu__item--current {
215 | --color-title: #fff;
216 | --color-subtitle: #644B47;
217 | --title-font-style: italic;
218 | --title-font-weight: 900;
219 | cursor: pointer;
220 | --title-display: block;
221 | --title-font-size: 9vw;
222 | }
223 |
224 | .intro .menu__item--current::before {
225 | content: '01';
226 | position: absolute;
227 | bottom: 110%;
228 | color: var(--color-title);
229 | }
230 |
231 | .intro .menu__item--current:hover {
232 | --color-title: #fff;
233 | --color-subtitle: #88524a;
234 | }
235 |
236 | .intro .menu__item--current {
237 | --color-title: #e5e2e2;
238 | --title-font-style: normal;
239 | --title-font-size: 7vw;
240 | pointer-events: auto;
241 | }
242 |
243 | .intro .menu__item {
244 | --title-font-size: 5vw;
245 | --subtitle-display: block;
246 | --title-display: block;
247 | }
248 |
249 | .layers {
250 | top: 0;
251 | width: 100vw;
252 | height: 100vh;
253 | position: absolute;
254 | overflow: hidden;
255 | z-index: 2000;
256 | pointer-events: none;
257 | }
258 |
259 | .layers__item {
260 | position: absolute;
261 | width: 100%;
262 | height: 100%;
263 | overflow: hidden;
264 | transform: translate3d(0, 101%, 0);
265 | }
266 |
267 | .layers__item-img {
268 | width: 100%;
269 | height: 100%;
270 | background-size: cover;
271 | background-position: 50% 50%;
272 | transform: translate3d(0, -101%, 0);
273 | }
274 |
275 | .cursor {
276 | display: none;
277 | }
278 |
279 | @media screen and (min-width: 53em) {
280 | .message {
281 | display: none;
282 | }
283 |
284 | .frame {
285 | display: grid;
286 | position: absolute;
287 | width: 100%;
288 | padding: 4rem;
289 | pointer-events: none;
290 | grid-template-columns: auto auto 1fr;
291 | grid-template-rows: auto;
292 | }
293 |
294 | .frame__title {
295 | margin: 0 4rem 0 0;
296 | }
297 |
298 | .frame__info {
299 | margin: 0;
300 | }
301 |
302 | .frame__links {
303 | padding: 0;
304 | }
305 |
306 | .frame__info,
307 | .frame__button {
308 | justify-self: end;
309 | }
310 |
311 | .frame__button {
312 | margin: 0 0 0 10vw;
313 | }
314 |
315 | .frame a {
316 | pointer-events: auto;
317 | }
318 | .content {
319 | height: 100vh;
320 | }
321 | .grid {
322 | width: 80vw;
323 | }
324 | .menu {
325 | width: 110vw;
326 | }
327 | }
328 |
329 | @media (any-pointer: fine) {
330 | .cursor {
331 | position: fixed;
332 | top: 0;
333 | left: 0;
334 | display: block;
335 | pointer-events: none;
336 | opacity: 0;
337 | }
338 | .cursor__inner {
339 | fill: var(--cursor-fill);
340 | stroke: var(--cursor-stroke);
341 | stroke-width: var(--cursor-stroke-width);
342 | }
343 | }
--------------------------------------------------------------------------------
/dist/favicon.26242483.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/dist/favicon.26242483.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rapid Image Layers Animation | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
22 |
23 |
24 |
25 |
26 |
Rapid image layers animation
27 |
31 |
Click the middle menu item to see the effect.
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
56 |
57 |
58 |
61 |
64 |
67 |
70 |
73 |
76 |
79 |
82 |
85 |
88 |
89 |
90 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "RapidLayersAnimation",
3 | "version": "1.0.0",
4 | "description": "A rapid layers animation of images as a splash intro or page transition on a website.",
5 | "main": "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/RapidLayersAnimation.git"
15 | },
16 | "keywords": [],
17 | "author": "Codrops",
18 | "license": "MIT",
19 | "homepage": "[HOMEPAGE]",
20 | "bugs": {
21 | "url": "https://github.com/codrops/RapidLayersAnimation/issues"
22 | },
23 | "dependencies": {
24 | "gsap": "^3.2.6",
25 | "imagesloaded": "^4.1.4",
26 | "parcel": "^1.12.4"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 14px;
9 | }
10 |
11 | body {
12 | margin: 0;
13 | --color-text: #644B47;
14 | --color-bg: #0F0E0E;
15 | --color-link: #fff;
16 | --color-link-hover: #3d3637;
17 | color: var(--color-text);
18 | background-color: var(--color-bg);
19 | font-family: soleil, sans-serif;
20 | -webkit-font-smoothing: antialiased;
21 | -moz-osx-font-smoothing: grayscale;
22 | /* Cursor styles */
23 | --cursor-stroke: #644B47;
24 | --cursor-fill: none;
25 | --cursor-stroke-width: 1px;
26 | }
27 |
28 | /* Page Loader */
29 | .js .loading::before,
30 | .js .loading::after {
31 | content: '';
32 | position: fixed;
33 | z-index: 1000;
34 | }
35 |
36 | .js .loading::before {
37 | top: 0;
38 | left: 0;
39 | width: 100%;
40 | height: 100%;
41 | background: var(--color-bg);
42 | }
43 |
44 | .js .loading::after {
45 | top: 50%;
46 | left: 50%;
47 | width: 60px;
48 | height: 60px;
49 | margin: -30px 0 0 -30px;
50 | border-radius: 50%;
51 | opacity: 0.4;
52 | background: var(--color-link);
53 | animation: loaderAnim 0.7s linear infinite alternate forwards;
54 |
55 | }
56 |
57 | @keyframes loaderAnim {
58 | to {
59 | opacity: 1;
60 | transform: scale3d(0.5, 0.5, 1);
61 | }
62 | }
63 |
64 | a {
65 | text-decoration: none;
66 | color: var(--color-link);
67 | outline: none;
68 | }
69 |
70 | a:hover,
71 | a:focus {
72 | color: var(--color-link-hover);
73 | outline: none;
74 | }
75 |
76 | .message {
77 | background: var(--color-text);
78 | color: var(--color-bg);
79 | padding: 1rem;
80 | text-align: center;
81 | }
82 |
83 | .frame {
84 | padding: 3rem 5vw;
85 | text-align: center;
86 | position: relative;
87 | z-index: 1000;
88 | }
89 |
90 | .frame__title {
91 | font-size: 1rem;
92 | margin: 0 0 1rem;
93 | font-weight: 300;
94 | }
95 |
96 | .frame__links {
97 | display: inline;
98 | }
99 |
100 | .frame__links a:not(:last-child) {
101 | margin-right: 1rem;
102 | }
103 |
104 | .frame__button {
105 | all: unset;
106 | }
107 |
108 | .content {
109 | width: 100%;
110 | height: 400px;
111 | display: grid;
112 | justify-content: center;
113 | overflow: hidden;
114 | }
115 |
116 | .grid {
117 | grid-area: 1 / 1 / 2 / 2;
118 | width: 100%;
119 | height: 93%;
120 | margin: auto;
121 | display: grid;
122 | grid-template-columns: repeat(13,1fr);
123 | grid-template-rows: repeat(8,1fr);
124 | grid-gap: 1vw;
125 | position: relative;
126 | }
127 |
128 | .grid::after {
129 | content: '';
130 | height: 6rem;
131 | width: 1px;
132 | background: #fff;
133 | position: absolute;
134 | bottom: 2rem;
135 | left: 50%;
136 | }
137 |
138 | .intro .grid::after {
139 | display: none;
140 | }
141 |
142 | .grid__item {
143 | background-size: cover;
144 | background-position: center;
145 | opacity: 0;
146 | -webkit-filter: brightness(0.7);
147 | filter: brightness(0.7);
148 | }
149 |
150 | .grid__item--a {
151 | grid-area: 2 / 1 / 5 / 3;
152 | }
153 |
154 | .grid__item--b {
155 | grid-area: 6 / 2 / 9 / 5;
156 | }
157 |
158 | .grid__item--c {
159 | grid-area: 3 / 4 / 7 / 7;
160 | }
161 |
162 | .grid__item--d {
163 | grid-area: 1 / 8 / 4 / 10;
164 | }
165 |
166 | .grid__item--e {
167 | grid-area: 4 / 10 / 7 / 14;
168 | }
169 |
170 | .grid__item--f {
171 | grid-area: 6 / 9 / 9 / 12;
172 | }
173 |
174 | .menu {
175 | grid-area: 1 / 1 / 2 / 2;
176 | display: flex;
177 | width: 130vw;
178 | justify-content: space-between;
179 | align-self: center;
180 | justify-self: start;
181 | position: relative;
182 | }
183 |
184 | .menu__item {
185 | --color-title: #3c3a3b;
186 | --color-subtitle: #3e3c3d;
187 | --title-font-style: italic;
188 | --title-font-weight: 400;
189 | --title-font-size: 7vw;
190 | --title-display: none;
191 | --subtitle-display: none;
192 | pointer-events: none;
193 | }
194 |
195 | .menu__item-title {
196 | font-size: var(--title-font-size);
197 | font-family: meno-banner, serif;
198 | font-style: var(--title-font-style);
199 | font-weight: var(--title-font-weight);
200 | color: var(--color-title);
201 | margin: 0;
202 | display: var(--title-display);
203 | }
204 |
205 | .menu__item-subtitle {
206 | font-family: meno-banner, serif;
207 | color: var(--color-subtitle);
208 | text-align: right;
209 | margin: 0 -3rem 0 0;
210 | font-size: 1.5vw;
211 | display: var(--subtitle-display);
212 | }
213 |
214 | .menu__item--current {
215 | --color-title: #fff;
216 | --color-subtitle: #644B47;
217 | --title-font-style: italic;
218 | --title-font-weight: 900;
219 | cursor: pointer;
220 | --title-display: block;
221 | --title-font-size: 9vw;
222 | }
223 |
224 | .intro .menu__item--current::before {
225 | content: '01';
226 | position: absolute;
227 | bottom: 110%;
228 | color: var(--color-title);
229 | }
230 |
231 | .intro .menu__item--current:hover {
232 | --color-title: #fff;
233 | --color-subtitle: #88524a;
234 | }
235 |
236 | .intro .menu__item--current {
237 | --color-title: #e5e2e2;
238 | --title-font-style: normal;
239 | --title-font-size: 7vw;
240 | pointer-events: auto;
241 | }
242 |
243 | .intro .menu__item {
244 | --title-font-size: 5vw;
245 | --subtitle-display: block;
246 | --title-display: block;
247 | }
248 |
249 | .layers {
250 | top: 0;
251 | width: 100vw;
252 | height: 100vh;
253 | position: absolute;
254 | overflow: hidden;
255 | z-index: 2000;
256 | pointer-events: none;
257 | }
258 |
259 | .layers__item {
260 | position: absolute;
261 | width: 100%;
262 | height: 100%;
263 | overflow: hidden;
264 | transform: translate3d(0, 101%, 0);
265 | }
266 |
267 | .layers__item-img {
268 | width: 100%;
269 | height: 100%;
270 | background-size: cover;
271 | background-position: 50% 50%;
272 | transform: translate3d(0, -101%, 0);
273 | }
274 |
275 | .cursor {
276 | display: none;
277 | }
278 |
279 | @media screen and (min-width: 53em) {
280 | .message {
281 | display: none;
282 | }
283 |
284 | .frame {
285 | display: grid;
286 | position: absolute;
287 | width: 100%;
288 | padding: 4rem;
289 | pointer-events: none;
290 | grid-template-columns: auto auto 1fr;
291 | grid-template-rows: auto;
292 | }
293 |
294 | .frame__title {
295 | margin: 0 4rem 0 0;
296 | }
297 |
298 | .frame__info {
299 | margin: 0;
300 | }
301 |
302 | .frame__links {
303 | padding: 0;
304 | }
305 |
306 | .frame__info,
307 | .frame__button {
308 | justify-self: end;
309 | }
310 |
311 | .frame__button {
312 | margin: 0 0 0 10vw;
313 | }
314 |
315 | .frame a {
316 | pointer-events: auto;
317 | }
318 | .content {
319 | height: 100vh;
320 | }
321 | .grid {
322 | width: 80vw;
323 | }
324 | .menu {
325 | width: 110vw;
326 | }
327 | }
328 |
329 | @media (any-pointer: fine) {
330 | .cursor {
331 | position: fixed;
332 | top: 0;
333 | left: 0;
334 | display: block;
335 | pointer-events: none;
336 | opacity: 0;
337 | }
338 | .cursor__inner {
339 | fill: var(--cursor-fill);
340 | stroke: var(--cursor-stroke);
341 | stroke-width: var(--cursor-stroke-width);
342 | }
343 | }
--------------------------------------------------------------------------------
/src/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/favicon.ico
--------------------------------------------------------------------------------
/src/img/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/1.jpg
--------------------------------------------------------------------------------
/src/img/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/10.jpg
--------------------------------------------------------------------------------
/src/img/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/2.jpg
--------------------------------------------------------------------------------
/src/img/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/3.jpg
--------------------------------------------------------------------------------
/src/img/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/4.jpg
--------------------------------------------------------------------------------
/src/img/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/5.jpg
--------------------------------------------------------------------------------
/src/img/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/6.jpg
--------------------------------------------------------------------------------
/src/img/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/7.jpg
--------------------------------------------------------------------------------
/src/img/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/8.jpg
--------------------------------------------------------------------------------
/src/img/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/RapidLayersAnimation/ea8afcaebd6d4bbf1e757c340b50f9116e25b856/src/img/9.jpg
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rapid Image Layers Animation | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Rapid image layers animation
19 |
23 |
Click the middle menu item to see the effect.
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
48 |
49 |
50 |
53 |
56 |
59 |
62 |
65 |
68 |
71 |
74 |
77 |
80 |
81 |
82 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/js/cursor.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 | import { map, lerp, calcWinsize, getMousePos } from './utils';
3 | import { EventEmitter } from 'events';
4 |
5 | // Calculate the viewport size
6 | let winsize = calcWinsize();
7 | window.addEventListener('resize', () => {
8 | winsize = calcWinsize();
9 | });
10 |
11 | // Track the mouse position
12 | let mouse = {x: 0, y: 0};
13 | window.addEventListener('mousemove', ev => mouse = getMousePos(ev));
14 |
15 | export default class Cursor extends EventEmitter {
16 | constructor(el) {
17 | super();
18 | this.DOM = {el: el};
19 | this.DOM.el.style.opacity = 0;
20 | this.DOM.circleInner = this.DOM.el.querySelector('.cursor__inner');
21 |
22 | this.filterId = '#filter-1';
23 | this.DOM.feTurbulence = document.querySelector(`${this.filterId} > feTurbulence`);
24 |
25 | this.primitiveValues = {turbulence: 0};
26 |
27 | this.createTimeline();
28 |
29 | this.bounds = this.DOM.el.getBoundingClientRect();
30 |
31 | this.renderedStyles = {
32 | tx: {previous: 0, current: 0, amt: 0.2},
33 | ty: {previous: 0, current: 0, amt: 0.2},
34 | radius: {previous: 60, current: 60, amt: 0.2}
35 | };
36 |
37 | this.listen();
38 |
39 | this.onMouseMoveEv = () => {
40 | this.renderedStyles.tx.previous = this.renderedStyles.tx.current = mouse.x - this.bounds.width/2;
41 | this.renderedStyles.ty.previous = this.renderedStyles.ty.previous = mouse.y - this.bounds.height/2;
42 | gsap.to(this.DOM.el, {duration: 0.9, ease: 'Power3.easeOut', opacity: 1});
43 | requestAnimationFrame(() => this.render());
44 | window.removeEventListener('mousemove', this.onMouseMoveEv);
45 | };
46 | window.addEventListener('mousemove', this.onMouseMoveEv);
47 | }
48 | render() {
49 | this.renderedStyles['tx'].current = mouse.x - this.bounds.width/2;
50 | this.renderedStyles['ty'].current = mouse.y - this.bounds.height/2;
51 |
52 | for (const key in this.renderedStyles ) {
53 | this.renderedStyles[key].previous = lerp(this.renderedStyles[key].previous, this.renderedStyles[key].current, this.renderedStyles[key].amt);
54 | }
55 |
56 | this.DOM.el.style.transform = `translateX(${(this.renderedStyles['tx'].previous)}px) translateY(${this.renderedStyles['ty'].previous}px)`;
57 | this.DOM.circleInner.setAttribute('r', this.renderedStyles['radius'].previous);
58 |
59 | requestAnimationFrame(() => this.render());
60 | }
61 | createTimeline() {
62 | // init timeline
63 | this.tl = gsap.timeline({
64 | paused: true,
65 | onStart: () => {
66 | this.DOM.circleInner.style.filter = `url(${this.filterId}`;
67 | },
68 | onUpdate: () => {
69 | this.DOM.feTurbulence.setAttribute('baseFrequency', this.primitiveValues.turbulence);
70 | },
71 | onComplete: () => {
72 | this.DOM.circleInner.style.filter = 'none';
73 | }
74 | })
75 | .to(this.primitiveValues, {
76 | duration: 0.4,
77 | startAt: {turbulence: 0.09},
78 | turbulence: 0
79 | });
80 | }
81 | enter() {
82 | this.renderedStyles['radius'].current = 100;
83 | this.tl.restart();
84 | }
85 | leave() {
86 | this.renderedStyles['radius'].current = 60;
87 | this.tl.progress(1).kill();
88 | }
89 | listen() {
90 | this.on('enter', () => this.enter());
91 | this.on('leave', () => this.leave());
92 | }
93 | }
--------------------------------------------------------------------------------
/src/js/index.js:
--------------------------------------------------------------------------------
1 | import Cursor from './cursor';
2 | import Revealer from './revealer';
3 | import { preloadImages } from './utils';
4 |
5 | // Preload images
6 | preloadImages('.layers__item-img').then(() => {
7 | document.body.classList.remove('loading');
8 |
9 | const revealer = new Revealer();
10 | document.querySelector('.menu__item--current').addEventListener('click', () => {
11 | revealer.reveal();
12 | });
13 | });
14 |
15 | const cursor = new Cursor(document.querySelector('.cursor'));
16 |
17 | [...document.querySelectorAll('a')].forEach(el => {
18 | el.addEventListener('mouseenter', () => cursor.emit('enter'));
19 | el.addEventListener('mouseleave', () => cursor.emit('leave'));
20 | });
--------------------------------------------------------------------------------
/src/js/revealer.js:
--------------------------------------------------------------------------------
1 | import { gsap } from 'gsap';
2 | import { randomFloat } from './utils';
3 |
4 | class ImageLayer {
5 | constructor(el) {
6 | this.DOM = {el: el};
7 | this.DOM.image = this.DOM.el.querySelector('.layers__item-img');
8 | }
9 | }
10 |
11 | export default class Revealer {
12 | constructor() {
13 | this.DOM = {main: document.querySelector('main')};
14 | this.layers = [];
15 | [...document.querySelectorAll('.layers__item')].forEach(item => this.layers.push(new ImageLayer(item)));
16 | this.layersTotal = this.layers.length;
17 | this.gridItems = [...document.querySelectorAll('.grid__item')];
18 | this.options = {
19 | duration: 1,
20 | panelDelay: 0.15
21 | };
22 | this.createTimeline();
23 | }
24 | createTimeline() {
25 | this.tl = gsap.timeline({paused: true});
26 |
27 | // animate the Image layers
28 | for (let i = 0, len = this.layersTotal; i <= len-1; ++i) {
29 | this.tl.to([this.layers[i].DOM.el, this.layers[i].DOM.image], {
30 | duration: this.options.duration,
31 | ease: 'Power2.easeInOut',
32 | y: 0
33 | }, this.options.panelDelay*i);
34 | }
35 |
36 | this.tl.addLabel('halfway', this.options.panelDelay*(this.layersTotal-1) + this.options.duration)
37 | .call(() => {
38 | // hide all Image layers except the last one (at this point the last Image layer is visible fullscreen)
39 | this.layers.filter((_, pos) => pos != this.layers.length-1).forEach((panel,pos) => {
40 | gsap.set(panel.DOM.el, {opacity: 0});
41 | });
42 | // also remove intro class from the main element (this will apply the new CSS layout styles for the next section)
43 | this.DOM.main.classList.remove('intro');
44 | }, this, 'halfway')
45 | // now hide the last Image Layer
46 | .to([this.layers[this.layersTotal-1].DOM.el, this.layers[this.layersTotal-1].DOM.image], {
47 | duration: this.options.duration,
48 | ease: 'Expo.easeInOut',
49 | y: (index) => index ? '101%' : '-101%'
50 | }, 'halfway')
51 | // show grid items
52 | .fromTo(this.gridItems, {y: () => randomFloat(100,500)}, {
53 | duration: this.options.duration*2,
54 | ease: 'Expo.easeOut',
55 | y: 0,
56 | opacity: 1
57 | }, 'halfway');
58 | }
59 | reveal() {
60 | this.tl.restart();
61 | }
62 | }
--------------------------------------------------------------------------------
/src/js/utils.js:
--------------------------------------------------------------------------------
1 | const imagesLoaded = require('imagesloaded');
2 |
3 | // Map number x from range [a, b] to [c, d]
4 | const map = (x, a, b, c, d) => (x - a) * (d - c) / (b - a) + c;
5 |
6 | // Linear interpolation
7 | const lerp = (a, b, n) => (1 - n) * a + n * b;
8 |
9 | const calcWinsize = () => {
10 | return {width: window.innerWidth, height: window.innerHeight};
11 | };
12 |
13 | // Gets the mouse position
14 | const getMousePos = (e) => {
15 | let posx = 0;
16 | let posy = 0;
17 | if (!e) e = window.event;
18 | if (e.pageX || e.pageY) {
19 | posx = e.pageX;
20 | posy = e.pageY;
21 | }
22 | else if (e.clientX || e.clientY) {
23 | posx = e.clientX + body.scrollLeft + document.documentElement.scrollLeft;
24 | posy = e.clientY + body.scrollTop + document.documentElement.scrollTop;
25 | }
26 |
27 | return { x : posx, y : posy }
28 | };
29 |
30 | const randomFloat = (min,max) => parseFloat(Math.min(min + (Math.random() * (max - min)), max).toFixed(2));
31 |
32 | // Preload images
33 | const preloadImages = (selector) => {
34 | return new Promise((resolve, _) => {
35 | imagesLoaded(document.querySelectorAll(selector), {background: true}, resolve);
36 | });
37 | };
38 |
39 | export { map, lerp, calcWinsize, getMousePos, randomFloat, preloadImages };
--------------------------------------------------------------------------------