├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── css
└── base.css
├── favicon.ico
├── img
├── demo1
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ └── 5.jpg
├── demo2
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ └── 5.jpg
├── demo3
│ ├── 1.jpg
│ ├── 10.jpg
│ ├── 11.jpg
│ ├── 12.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ ├── 5.jpg
│ ├── 6.jpg
│ ├── 7.jpg
│ ├── 8.jpg
│ └── 9.jpg
└── demo4
│ ├── 1.jpg
│ ├── 2.jpg
│ ├── 3.jpg
│ ├── 4.jpg
│ └── 5.jpg
├── index.html
├── index2.html
├── index3.html
├── index4.html
└── js
├── index.js
├── index2.js
├── index3.js
├── index4.js
└── utils.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .cache
3 | .parcel-cache
4 | package-lock.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2009 - 2023 [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 | # Fullscreen Clip Effect
2 |
3 | Some inspiration for clip-path animations where a fullscreen image moves into a row/grid of smaller images, morphing its shape along the way.
4 |
5 | 
6 |
7 | [Article on Codrops](https://tympanus.net/codrops/?p=70833)
8 |
9 | [Demo](http://tympanus.net/Development/FullscreenClipEffect/)
10 |
11 |
12 | ## Installation
13 |
14 | Run this demo on a [local server](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Tools_and_setup/set_up_a_local_testing_server).
15 |
16 | ## Credits
17 |
18 | - Images from [Unsplash](https://unsplash.com/)
19 |
20 | ## Misc
21 |
22 | 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/)
23 |
24 | ## License
25 | [MIT](LICENSE)
26 |
27 | Made with :blue_heart: by [Codrops](http://www.codrops.com)
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/css/base.css:
--------------------------------------------------------------------------------
1 | *,
2 | *::after,
3 | *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | :root {
8 | font-size: 14px;
9 | --color-text: #fff;
10 | --color-bg: #eca590;
11 | --color-bg-2: #eabfb1;
12 | --color-link: #fff;
13 | --color-link-hover: #fff;
14 | --font-cover: tenon, sans-serif;
15 | --font-weight-cover: 700;
16 | --font-variation-cover: none;
17 | --font-size-cover: 11vw;
18 | --font-transform-cover: uppercase;
19 | --color-cover: #fff;
20 | --color-bg-button: #fff;
21 | --color-button: #364a54;
22 | }
23 |
24 | .demo-1 {
25 | --font-cover: "aziga", sans-serif;
26 | --font-weight-cover: 400;
27 | }
28 |
29 | .demo-2 {
30 | --color-cover: #fcf8ec;
31 | --color-bg-button: #151c1e;
32 | --color-button: #ece1d4;
33 | --color-bg: #3d4a54;
34 | --color-bg-2: #38454d;
35 | --font-cover: "stadio-now-variable", sans-serif;
36 | --font-variation-cover: "opsz" 1000, "wght" 588;
37 | }
38 |
39 | .demo-3 {
40 | --color-cover: #fff;
41 | --color-bg-button: #fff;
42 | --color-button: #1e2f32;
43 | --color-bg: #548a96;
44 | --color-bg-2: #c4dedd;
45 | --font-cover: "arnika-variable", sans-serif;
46 | --font-variation-cover: "wdth" 100;
47 | }
48 |
49 | .demo-4 {
50 | --color-cover: #fff;
51 | --color-bg-button: #fff;
52 | --color-button: #1e2f32;
53 | --color-bg: #010101;
54 | --color-bg-2: #272b31;
55 | --font-cover: "zeitung-pro-variable", sans-serif;
56 | --font-variation-cover: "opsz" 20, "wght" 556;
57 | }
58 |
59 | body {
60 | margin: 0;
61 | color: var(--color-text);
62 | background-color: var(--color-bg);
63 | background: linear-gradient(0deg, var(--color-bg), var(--color-bg-2), var(--color-bg));
64 | font-family: tenon,-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif;
65 | -webkit-font-smoothing: antialiased;
66 | -moz-osx-font-smoothing: grayscale;
67 | overflow: hidden;
68 | }
69 |
70 | .demo-1 {
71 |
72 | }
73 |
74 | /* Page Loader */
75 | .js .loading::before,
76 | .js .loading::after {
77 | content: '';
78 | position: fixed;
79 | z-index: 1000;
80 | }
81 |
82 | .js .loading::before {
83 | top: 0;
84 | left: 0;
85 | width: 100%;
86 | height: 100%;
87 | background: var(--color-bg);
88 | }
89 |
90 | .js .loading::after {
91 | top: 50%;
92 | left: 50%;
93 | width: 60px;
94 | height: 60px;
95 | margin: -30px 0 0 -30px;
96 | border-radius: 50%;
97 | opacity: 0.4;
98 | background: var(--color-link);
99 | animation: loaderAnim 0.7s linear infinite alternate forwards;
100 |
101 | }
102 |
103 | @keyframes loaderAnim {
104 | to {
105 | opacity: 1;
106 | transform: scale3d(0.5,0.5,1);
107 | }
108 | }
109 |
110 | a {
111 | text-decoration: none;
112 | color: var(--color-link);
113 | outline: none;
114 | cursor: pointer;
115 | }
116 |
117 | a:hover {
118 | color: var(--color-link-hover);
119 | outline: none;
120 | }
121 |
122 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */
123 | a:focus {
124 | /* Provide a fallback style for browsers
125 | that don't support :focus-visible */
126 | outline: none;
127 | background: lightgrey;
128 | }
129 |
130 | a:focus:not(:focus-visible) {
131 | /* Remove the focus indicator on mouse-focus for browsers
132 | that do support :focus-visible */
133 | background: transparent;
134 | }
135 |
136 | a:focus-visible {
137 | /* Draw a very noticeable focus style for
138 | keyboard-focus on browsers that do support
139 | :focus-visible */
140 | outline: 2px solid red;
141 | background: transparent;
142 | }
143 |
144 | .unbutton {
145 | background: none;
146 | border: 0;
147 | padding: 0;
148 | margin: 0;
149 | font: inherit;
150 | cursor: pointer;
151 | color: inherit;
152 | text-transform: uppercase;
153 | }
154 |
155 | .unbutton:focus {
156 | outline: none;
157 | }
158 |
159 | main {
160 | display: grid;
161 | width: 100%;
162 | height: 100vh;
163 | grid-template-columns: 1fr;
164 | grid-template-rows: 1fr;
165 | overflow: hidden;
166 | }
167 |
168 | .frame {
169 | position: fixed;
170 | z-index: 600;
171 | top: 0;
172 | width: 100%;
173 | padding: 1.5rem;
174 | display: grid;
175 | grid-template-columns: auto auto 1fr auto;
176 | grid-template-rows: auto;
177 | grid-template-areas: 'title prev demos sponsor';
178 | justify-content: start;
179 | margin-bottom: 3rem;
180 | grid-gap: 2rem;
181 | }
182 |
183 | .frame a {
184 | pointer-events: auto;
185 | }
186 |
187 | .frame a:not(.frame__demo):not(.frame__title-back) {
188 | white-space: nowrap;
189 | overflow: hidden;
190 | position: relative;
191 | }
192 |
193 | .frame a:not(.frame__demo):not(.frame__title-back)::before {
194 | content: '';
195 | height: 1px;
196 | width: 100%;
197 | background: currentColor;
198 | position: absolute;
199 | top: 90%;
200 | transition: transform 0.3s;
201 | transform-origin: 0% 50%;
202 | }
203 |
204 | .frame a:not(.frame__demo):not(.frame__title-back):hover::before {
205 | transform: scaleX(0);
206 | transform-origin: 100% 50%;
207 | }
208 |
209 | .frame__title {
210 | grid-area: title;
211 | display: flex;
212 | }
213 |
214 | .frame__title-main {
215 | font-size: inherit;
216 | margin: 0;
217 | font-weight: inherit;
218 | }
219 |
220 | .frame__title-back {
221 | position: relative;
222 | display: flex;
223 | }
224 |
225 | .frame__title-back span {
226 | display: none;
227 | }
228 |
229 | .frame__title-back svg {
230 | fill: currentColor;
231 | }
232 |
233 | .frame__prev {
234 | grid-area: prev;
235 | }
236 |
237 | .frame__demos {
238 | grid-area: demos;
239 | display: flex;
240 | }
241 |
242 | a.frame__demo {
243 | margin: 0 0 0 1rem;
244 | text-decoration: none;
245 | }
246 |
247 | .frame__demo--current {
248 | font-weight: bold;
249 | color: var(--color-link-hover);
250 | }
251 |
252 | .slides {
253 | grid-area: 1 / 1 / -1 / -1;
254 | display: grid;
255 | grid-template-columns: repeat(5,22%);
256 | grid-gap: 2.5vw;
257 | height: 100vh;
258 | align-items: center;
259 | justify-content: center;
260 | justify-items: center;
261 | align-content: center;
262 | pointer-events: none;
263 | }
264 |
265 | .slides--columns {
266 | grid-gap: 0;
267 | }
268 |
269 | .slide {
270 | overflow: hidden;
271 | border-radius: 23vw;
272 | height: 56vh;
273 | width: 100%;
274 | display: grid;
275 | opacity: 0;
276 | }
277 |
278 | .slides--grid .slide {
279 | border-radius: 1vw;
280 | }
281 |
282 | .slides--columns .slide {
283 | border-radius: 0;
284 | height: 100vh;
285 | }
286 |
287 | .slide--current {
288 | visibility: hidden;
289 | }
290 |
291 | .slide__img {
292 | background-size: cover;
293 | background-position: 50% 50%;
294 | }
295 |
296 | .cover {
297 | grid-area: 1 / 1 / -1 / -1;
298 | display: grid;
299 | grid-template-rows: 1fr auto;
300 | justify-content: center;
301 | z-index: 100;
302 | }
303 |
304 | .cover__title {
305 | margin: 10vh 0 0 0;
306 | align-self: center;
307 | color: var(--color-cover);
308 | font-family: var(--font-cover);
309 | font-weight: var(--font-weight-cover);
310 | font-size: var(--font-size-cover);
311 | text-transform: var(--font-transform-cover);
312 | font-variation-settings: var(--font-variation-cover);
313 | line-height: 1;
314 | pointer-events: none;
315 | }
316 |
317 | .cover__button {
318 | align-self: end;
319 | padding: 1rem 2rem;
320 | margin-bottom: 3rem;
321 | background: var(--color-bg-button);
322 | color: var(--color-button);
323 | width: min-content;
324 | white-space: nowrap;
325 | justify-self: center;
326 | border-radius: 4rem;
327 | }
328 |
329 | .clip {
330 | grid-area: 1 / 1 / -1 / -1;
331 | display: grid;
332 | }
333 |
334 | .clip__img {
335 | background-size: cover;
336 | background-position: 50% 50%;
337 | }
338 |
339 | @media screen and (min-width: 53em) {
340 |
341 | }
342 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/favicon.ico
--------------------------------------------------------------------------------
/img/demo1/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo1/1.jpg
--------------------------------------------------------------------------------
/img/demo1/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo1/2.jpg
--------------------------------------------------------------------------------
/img/demo1/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo1/3.jpg
--------------------------------------------------------------------------------
/img/demo1/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo1/4.jpg
--------------------------------------------------------------------------------
/img/demo1/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo1/5.jpg
--------------------------------------------------------------------------------
/img/demo2/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo2/1.jpg
--------------------------------------------------------------------------------
/img/demo2/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo2/2.jpg
--------------------------------------------------------------------------------
/img/demo2/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo2/3.jpg
--------------------------------------------------------------------------------
/img/demo2/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo2/4.jpg
--------------------------------------------------------------------------------
/img/demo2/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo2/5.jpg
--------------------------------------------------------------------------------
/img/demo3/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/1.jpg
--------------------------------------------------------------------------------
/img/demo3/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/10.jpg
--------------------------------------------------------------------------------
/img/demo3/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/11.jpg
--------------------------------------------------------------------------------
/img/demo3/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/12.jpg
--------------------------------------------------------------------------------
/img/demo3/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/2.jpg
--------------------------------------------------------------------------------
/img/demo3/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/3.jpg
--------------------------------------------------------------------------------
/img/demo3/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/4.jpg
--------------------------------------------------------------------------------
/img/demo3/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/5.jpg
--------------------------------------------------------------------------------
/img/demo3/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/6.jpg
--------------------------------------------------------------------------------
/img/demo3/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/7.jpg
--------------------------------------------------------------------------------
/img/demo3/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/8.jpg
--------------------------------------------------------------------------------
/img/demo3/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo3/9.jpg
--------------------------------------------------------------------------------
/img/demo4/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo4/1.jpg
--------------------------------------------------------------------------------
/img/demo4/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo4/2.jpg
--------------------------------------------------------------------------------
/img/demo4/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo4/3.jpg
--------------------------------------------------------------------------------
/img/demo4/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo4/4.jpg
--------------------------------------------------------------------------------
/img/demo4/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codrops/FullscreenClipEffect/c0a27bffdeb0ba066babeec46f1231581e15274b/img/demo4/5.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fullscreen Clip-path Transitions | Demo 1 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Moduluxe
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/index2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fullscreen Clip-path Transitions | Demo 2 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Monom
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/index3.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fullscreen Clip-path Transitions | Demo 3 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
La Casa
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/index4.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Fullscreen Clip-path Transitions | Demo 4 | Codrops
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Adventure
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/js/index.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, preloadFonts } from './utils.js';
2 |
3 | Splitting();
4 |
5 | // toggle effect button
6 | const toggleButton = document.querySelector('button.cover__button');
7 | // .clip element
8 | const clipElement = document.querySelector('.clip');
9 | // .clip element > .clip__img
10 | const clipImage = clipElement.querySelector('.clip__img');
11 | // .slide elements (except current)
12 | const slides = document.querySelectorAll('.slide:not(.slide--current)');
13 | // slides parent
14 | const slider = document.querySelector('.slides');
15 | // cover title and chars
16 | const title = document.querySelector('.cover__title');
17 | const titleChars = title.querySelectorAll('.char');
18 | // true if the large image preview is open. Starts open.
19 | let isOpen = true;
20 | // true if animation in progress
21 | let isAnimating = false;
22 |
23 | // toggle effect function
24 | const toggleEffect = () => {
25 |
26 | if ( isAnimating ) return;
27 |
28 | if ( isOpen ) {
29 | showSlider();
30 | }
31 | else {
32 | showPreview();
33 | }
34 |
35 | isOpen = !isOpen;
36 |
37 | };
38 |
39 | // effect for showing the slider
40 | const showSlider = () => {
41 |
42 | isAnimating = true;
43 |
44 | gsap
45 | .timeline({
46 | defaults: {
47 | duration: 1.2,
48 | ease: 'power4.inOut',
49 | },
50 | onComplete: () => isAnimating = false
51 | })
52 | .addLabel('start', 0)
53 |
54 | .set(slider, {perspective: 1000})
55 | .set(clipElement, {willChange: 'clip-path'})
56 | .set(titleChars, {transformOrigin: '50% 100%'})
57 |
58 | // the cip element and its image
59 | .to(clipElement, {
60 | clipPath: 'inset(22% 39% round 23vw)',
61 | }, 'start')
62 | .to(clipImage, {
63 | scale: .8
64 | }, 'start')
65 |
66 | // all the other slides/images
67 | .fromTo(slides, {
68 | opacity: 0,
69 | z: 600
70 | }, {
71 | duration: 1.4,
72 | ease: 'power3.inOut',
73 | stagger: {
74 | amount: 0.15,
75 | from: 'center'
76 | },
77 | opacity: 1,
78 | z: 0,
79 | }, 'start')
80 |
81 | // title chars
82 | .to(titleChars, {
83 | duration: 1,
84 | scaleY: 0,
85 | stagger: {
86 | amount: 0.2,
87 | from: 'center'
88 | }
89 | }, 'start');
90 |
91 | };
92 |
93 | // effect for showing the large preview image
94 | const showPreview = () => {
95 |
96 | isAnimating = true;
97 |
98 | gsap
99 | .timeline({
100 | defaults: {
101 | duration: 1.2,
102 | ease: 'expo.inOut',
103 | },
104 | onComplete: () => isAnimating = false
105 | })
106 | .addLabel('start', 0)
107 | .set(clipElement, {willChange: 'clip-path'})
108 | .set(clipElement, {willChange: 'clip-path'})
109 |
110 | .to(slides, {
111 | stagger: {
112 | amount: 0.1,
113 | from: 'edges'
114 | },
115 | opacity: 0,
116 | z: 600
117 | }, 'start')
118 |
119 | .addLabel('clip', 'start+=0.15')
120 |
121 | .to(clipImage, {
122 | scale: 1
123 | }, 'clip')
124 |
125 | .fromTo(clipElement, {
126 | clipPath: 'inset(22% 39% round 23vw)'
127 | }, {
128 | clipPath: 'inset(0% 0% round 0vw)'
129 | }, 'clip+=0.1')
130 |
131 | // filter
132 | .fromTo(clipImage, {
133 | filter: 'brightness(100%) saturate(100%)'
134 | }, {
135 | duration: 0.4,
136 | ease: 'power1.in',
137 | filter: 'brightness(200%) saturate(200%)'
138 | }, 'clip+=0.1')
139 | .to(clipImage, {
140 | duration: 0.8,
141 | ease: 'power1',
142 | filter: 'brightness(100%) saturate(100%)'
143 | }, 'clip+=0.4')
144 |
145 | // title chars
146 | .to(titleChars, {
147 | duration: 1,
148 | scaleY: 1,
149 | stagger: {
150 | amount: 0.2,
151 | from: 'center'
152 | }
153 | }, 'clip');
154 |
155 | };
156 |
157 | // toggle effect click event
158 | toggleButton.addEventListener('click', () => toggleEffect());
159 |
160 | // Preload images and fonts
161 | Promise.all([preloadImages('.slide__img'), preloadFonts('lui6fbi')]).then(() => {
162 | document.body.classList.remove('loading')
163 | });
--------------------------------------------------------------------------------
/js/index2.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, preloadFonts } from './utils.js';
2 |
3 | Splitting();
4 |
5 | // toggle effect button
6 | const toggleButton = document.querySelector('button.cover__button');
7 | // .clip element
8 | const clipElement = document.querySelector('.clip');
9 | // .clip element > .clip__img
10 | const clipImage = clipElement.querySelector('.clip__img');
11 | // .slide elements (except current)
12 | const slides = document.querySelectorAll('.slide:not(.slide--current)');
13 | // cover title and chars
14 | const title = document.querySelector('.cover__title');
15 | const titleChars = title.querySelectorAll('.char');
16 | gsap.set(titleChars, {
17 | x: (position,_,arr) => (position - Math.floor(arr.length/2)) * 80
18 | })
19 | // true if the large image preview is open. Starts open.
20 | let isOpen = true;
21 | // true if animation in progress
22 | let isAnimating = false;
23 |
24 | // toggle effect function
25 | const toggleEffect = () => {
26 |
27 | if ( isAnimating ) return;
28 |
29 | if ( isOpen ) {
30 | showSlider();
31 | }
32 | else {
33 | showPreview();
34 | }
35 |
36 | isOpen = !isOpen;
37 |
38 | };
39 |
40 | // effect for showing the slider
41 | const showSlider = () => {
42 |
43 | isAnimating = true;
44 |
45 | gsap
46 | .timeline({
47 | defaults: {
48 | duration: 1.2,
49 | ease: 'power4.inOut',
50 | },
51 | onComplete: () => isAnimating = false
52 | })
53 | .addLabel('start', 0)
54 |
55 | .set(clipElement, {willChange: 'clip-path'})
56 |
57 | // the cip element and its image
58 | .to(clipElement, {
59 | clipPath: 'inset(22% 39% round 23vw)',
60 | }, 'start')
61 | .to(clipImage, {
62 | scale: 1.2
63 | }, 'start')
64 |
65 | .set(slides, {
66 | transformOrigin: (position,_,arr) => position < arr.length/2 ? '100% 50%' : '0% 50%',
67 | x: (position,_,arr) => {
68 | let newposition = position < arr.length/2 ? position : position + 1;
69 | return 800*(newposition-arr.length/2);
70 | },
71 | scaleX: 5,
72 | skewX: (position,_,arr) => position < arr.length/2 ? 25 : -25,
73 | }, 'start')
74 | // all the other slides/images
75 | .fromTo(slides, {
76 | opacity: 0,
77 | }, {
78 | duration: 1.4,
79 | stagger: {
80 | amount: 0.1,
81 | from: 'center'
82 | },
83 | opacity: 1,
84 | x: 0,
85 | scaleX: 1,
86 | skewX: 0
87 | }, 'start')
88 |
89 | // title chars
90 | .to(titleChars, {
91 | duration: 1.4,
92 | x: 0,
93 | stagger: {
94 | grid: 'auto',
95 | amount: 0.1,
96 | from: 'center'
97 | }
98 | }, 'start');
99 |
100 | };
101 |
102 | // effect for showing the large preview image
103 | const showPreview = () => {
104 |
105 | isAnimating = true;
106 |
107 | gsap
108 | .timeline({
109 | defaults: {
110 | duration: 1.2,
111 | ease: 'expo.inOut',
112 | },
113 | onComplete: () => isAnimating = false
114 | })
115 | .addLabel('start', 0)
116 | .set(clipElement, {willChange: 'clip-path'})
117 | .set(clipElement, {willChange: 'clip-path'})
118 |
119 | .to(slides, {
120 | stagger: {
121 | amount: 0.1,
122 | from: 'edges'
123 | },
124 | opacity: 0,
125 | x: (position,_,arr) => {
126 | let newposition = position < arr.length/2 ? position : position + 1;
127 | return 800*(newposition-arr.length/2);
128 | },
129 | scaleX: 2,
130 | skewX: (position,_,arr) => position < arr.length/2 ? 25 : -25
131 | }, 'start')
132 |
133 | .to(clipImage, {
134 | scale: 1
135 | }, 'start+=0.15')
136 |
137 | .fromTo(clipElement, {
138 | clipPath: 'inset(22% 39% round 23vw)'
139 | }, {
140 | clipPath: 'inset(0% 0% round 0vw)'
141 | }, 'start+=0.25')
142 |
143 | // filter
144 | .fromTo(clipImage, {
145 | filter: 'brightness(100%) saturate(100%)'
146 | }, {
147 | duration: 0.4,
148 | ease: 'power1.in',
149 | filter: 'brightness(200%) saturate(200%)'
150 | }, 'start+=0.25')
151 | .to(clipImage, {
152 | duration: 0.8,
153 | ease: 'power1',
154 | filter: 'brightness(100%) saturate(100%)'
155 | }, 'start+=0.65')
156 |
157 | // title chars
158 | .to(titleChars, {
159 | duration: 1.4,
160 | x: (position,_,arr) => (position - Math.floor(arr.length/2)) * 80
161 |
162 | }, 'start+=0.15');
163 |
164 | };
165 |
166 | // toggle effect click event
167 | toggleButton.addEventListener('click', () => toggleEffect());
168 |
169 | // Preload images and fonts
170 | Promise.all([preloadImages('.slide__img'), preloadFonts('lui6fbi')]).then(() => {
171 | document.body.classList.remove('loading')
172 | });
--------------------------------------------------------------------------------
/js/index3.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, getInitialPosition, preloadFonts } from './utils.js';
2 |
3 | Splitting();
4 |
5 | // toggle effect button
6 | const toggleButton = document.querySelector('button.cover__button');
7 | // .clip element
8 | const clipElement = document.querySelector('.clip');
9 | // .clip element > .clip__img
10 | const clipImage = clipElement.querySelector('.clip__img');
11 | // .slide elements (except current)
12 | const slides = document.querySelectorAll('.slide:not(.slide--current)');
13 | // cover title and chars
14 | const title = document.querySelector('.cover__title');
15 | const titleChars = title.querySelectorAll('.char');
16 | // true if the large image preview is open. Starts open.
17 | let isOpen = true;
18 | // true if animation in progress
19 | let isAnimating = false;
20 | let gridTimeline, previewTimeline;
21 |
22 | // toggle effect function
23 | const toggleEffect = () => {
24 |
25 | if ( isAnimating ) return;
26 |
27 | if ( isOpen ) {
28 | showSlider();
29 | }
30 | else {
31 | showPreview();
32 | }
33 |
34 | isOpen = !isOpen;
35 |
36 | };
37 |
38 | // effect for showing the slider
39 | const showSlider = () => {
40 |
41 | isAnimating = true;
42 |
43 | gridTimeline = gsap.timeline({
44 | defaults: {
45 | duration: 1.2,
46 | ease: 'power4.inOut',
47 | },
48 | onComplete: () => isAnimating = false
49 | })
50 | .addLabel('start', 0)
51 |
52 | .set(clipElement, {willChange: 'clip-path'})
53 | .set(titleChars, {transformOrigin: '50% 0%'})
54 |
55 | // the cip element and its image
56 | .to(clipElement, {
57 | clipPath: 'inset(22% 39% round 1vw)',
58 | }, 'start')
59 | .to(clipImage, {
60 | scale: 0.85
61 | }, 'start');
62 |
63 | for (const [pos,slide] of slides.entries()) {
64 | // all the other slides/images
65 | const {x,y} = getInitialPosition(slide,4000);
66 |
67 | gridTimeline.fromTo(slide, {
68 | opacity: 0,
69 | x: x,
70 | y: y
71 | }, {
72 | ease: 'power4',
73 | delay: pos*.02,
74 | opacity: 1,
75 | x: 0,
76 | y: 0
77 | }, 'start')
78 | }
79 |
80 | // title chars
81 | gridTimeline
82 | .to(titleChars, {
83 | duration: 0.8,
84 | scaleY: 8,
85 | scaleX: 0.1,
86 | opacity: 0,
87 | stagger: {
88 | amount: 0.25,
89 | from: '0'
90 | }
91 | }, 'start');
92 |
93 | };
94 |
95 | // effect for showing the large preview image
96 | const showPreview = () => {
97 |
98 | isAnimating = true;
99 |
100 | previewTimeline = gsap.timeline({
101 | defaults: {
102 | duration: 1.2,
103 | ease: 'power4.inOut',
104 | },
105 | onComplete: () => isAnimating = false
106 | })
107 | .addLabel('start', 0)
108 | .set(clipElement, {willChange: 'clip-path'})
109 | .set(clipElement, {willChange: 'clip-path'})
110 | .set(titleChars, {transformOrigin: '50% 100%'});
111 |
112 | for (const slide of slides) {
113 | const {x,y} = getInitialPosition(slide,1000);
114 |
115 | previewTimeline.to(slide, {
116 | opacity: 1,
117 | ease: 'power4.inOut',
118 | x: x,
119 | y: y
120 | }, 'start')
121 | };
122 |
123 | previewTimeline
124 | .to(clipImage, {
125 | scale: 1
126 | }, 'start')
127 |
128 | .fromTo(clipElement, {
129 | clipPath: 'inset(22% 39% round 1vw)'
130 | }, {
131 | clipPath: 'inset(0% 0% round 0vw)'
132 | }, 'start')
133 |
134 | // filter
135 | .fromTo(clipImage, {
136 | filter: 'brightness(100%) saturate(100%)'
137 | }, {
138 | duration: 0.4,
139 | ease: 'power1.in',
140 | filter: 'brightness(200%) saturate(200%)'
141 | }, 'start')
142 | .to(clipImage, {
143 | duration: 0.8,
144 | ease: 'power1',
145 | filter: 'brightness(100%) saturate(100%)'
146 | }, 'start+=0.4')
147 |
148 | // title chars
149 | .fromTo(titleChars, {
150 | scaleY: 8,
151 | scaleX: 0.1,
152 | opacity: 0,
153 | }, {
154 | duration: 0.8,
155 | scaleY: 1,
156 | scaleX: 1,
157 | opacity: 1,
158 | stagger: {
159 | amount: 0.25,
160 | from: 'end'
161 | }
162 | }, 'start');
163 |
164 | };
165 |
166 | // toggle effect click event
167 | toggleButton.addEventListener('click', () => toggleEffect());
168 |
169 | // Preload images and fonts
170 | Promise.all([preloadImages('.slide__img'), preloadFonts('lui6fbi')]).then(() => {
171 | document.body.classList.remove('loading')
172 | });
--------------------------------------------------------------------------------
/js/index4.js:
--------------------------------------------------------------------------------
1 | import { preloadImages, preloadFonts } from './utils.js';
2 |
3 | Splitting();
4 |
5 | // toggle effect button
6 | const toggleButton = document.querySelector('button.cover__button');
7 | // .clip element
8 | const clipElement = document.querySelector('.clip');
9 | // .clip element > .clip__img
10 | const clipImage = clipElement.querySelector('.clip__img');
11 | // .slide elements (except current)
12 | const slides = document.querySelectorAll('.slide:not(.slide--current)');
13 | // slides parent
14 | const slider = document.querySelector('.slides');
15 | // cover title and chars
16 | const title = document.querySelector('.cover__title');
17 | const titleChars = title.querySelectorAll('.char');
18 | // true if the large image preview is open. Starts open.
19 | let isOpen = true;
20 | // true if animation in progress
21 | let isAnimating = false;
22 |
23 | // toggle effect function
24 | const toggleEffect = () => {
25 |
26 | if ( isAnimating ) return;
27 |
28 | if ( isOpen ) {
29 | showSlider();
30 | }
31 | else {
32 | showPreview();
33 | }
34 |
35 | isOpen = !isOpen;
36 |
37 | };
38 |
39 | // effect for showing the slider
40 | const showSlider = () => {
41 |
42 | isAnimating = true;
43 |
44 | gsap.timeline({
45 | defaults: {
46 | duration: 1.2,
47 | ease: 'power4.inOut',
48 | },
49 | onComplete: () => isAnimating = false
50 | })
51 | .addLabel('start', 0)
52 |
53 | .set(slider, {perspective: 1000})
54 | .set(clipElement, {willChange: 'clip-path'})
55 | .set(titleChars, {transformOrigin: '0% 50%'})
56 |
57 | // the cip element and its image
58 | .fromTo(clipElement, {
59 | clipPath: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)',
60 | }, {
61 | clipPath: 'polygon(39% 0%, 61% 0%, 61% 100%, 39% 100%)',
62 | }, 'start')
63 | .to(clipImage, {
64 | scale: 1.25
65 | }, 'start')
66 |
67 | .set(slides, {
68 | transformOrigin: (position,_,arr) => position < arr.length/2 ? '100% 50%' : '0% 50%',
69 | rotationY: (position,_,arr) => position < arr.length/2 ? -60 : 60,
70 | scale: 1,
71 | }, 'start')
72 | // all the other slides/images
73 | .to(slides, {
74 | duration: 0.7,
75 | ease: 'expo',
76 | opacity: .7,
77 | rotationY: 0,
78 | stagger: {
79 | amount: 0.2,
80 | from: 'center'
81 | }
82 | }, 'start+=0.45')
83 |
84 | // title chars
85 | .to(titleChars, {
86 | duration: 1,
87 | scaleX: 0,
88 | stagger: {
89 | amount: 0.3,
90 | from: '0'
91 | }
92 | }, 'start');
93 |
94 | };
95 |
96 | // effect for showing the large preview image
97 | const showPreview = () => {
98 |
99 | isAnimating = true;
100 |
101 | gsap.timeline({
102 | defaults: {
103 | duration: 1,
104 | ease: 'expo',
105 | },
106 | onComplete: () => isAnimating = false
107 | })
108 | .addLabel('start', 0)
109 | .set(clipElement, {willChange: 'clip-path'})
110 | .set(clipElement, {willChange: 'clip-path'})
111 |
112 | // all the other slides/images
113 | .to(slides, {
114 | opacity: 0,
115 | scale: .8,
116 | }, 'start')
117 |
118 | .to(clipImage, {
119 | scale: 1
120 | }, 'start')
121 |
122 | .fromTo(clipElement, {
123 | clipPath: 'polygon(39% 0%, 61% 0%, 61% 100%, 39% 100%)'
124 | }, {
125 | clipPath: 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)'
126 | }, 'start+=0.1')
127 |
128 | // filter
129 | .fromTo(clipImage, {
130 | filter: 'brightness(100%) saturate(100%)'
131 | }, {
132 | duration: 0.2,
133 | ease: 'none',
134 | filter: 'brightness(200%) saturate(200%)'
135 | }, 'start+=0.1')
136 | .to(clipImage, {
137 | duration: 0.8,
138 | ease: 'power1',
139 | filter: 'brightness(100%) saturate(100%)'
140 | }, 'start+=0.3')
141 |
142 | // title chars
143 | .to(titleChars, {
144 | scaleX: 1,
145 | stagger: {
146 | amount: 0.15,
147 | from: 'end'
148 | }
149 | }, 'start');
150 |
151 | };
152 |
153 | // toggle effect click event
154 | toggleButton.addEventListener('click', () => toggleEffect());
155 |
156 | // Preload images and fonts
157 | Promise.all([preloadImages('.slide__img'), preloadFonts('lui6fbi')]).then(() => {
158 | document.body.classList.remove('loading')
159 | });
--------------------------------------------------------------------------------
/js/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Preload images
3 | * @param {String} selector - Selector/scope from where images need to be preloaded. Default is 'img'
4 | */
5 | const preloadImages = (selector = 'img') => {
6 | return new Promise((resolve) => {
7 | imagesLoaded(document.querySelectorAll(selector), {background: true}, resolve);
8 | });
9 | };
10 |
11 | /**
12 | * Preload fonts
13 | * @param {String} id
14 | */
15 | const preloadFonts = id => {
16 | return new Promise((resolve) => {
17 | WebFont.load({
18 | typekit: {
19 | id: id
20 | },
21 | active: resolve
22 | });
23 | });
24 | };
25 |
26 | /**
27 | * Calculates the position of an element when the element is [distanceDifference]px more far from the center of the page than it was previously
28 | * @param {Element} element
29 | * @param {Number} distanceDifference - The distance the element will have from the center
30 | * @returns {JSON} the x,y translation values
31 | */
32 | const getInitialPosition = (element, distanceDifference = 400) => {
33 | const winsize = {width: window.innerWidth, height: window.innerHeight};
34 | const elCenter = {x: element.offsetLeft + element.offsetWidth/2, y: element.offsetTop + element.offsetHeight/2};
35 | const angle = Math.atan2(Math.abs(winsize.height/2 - elCenter.y), Math.abs(winsize.width/2 - elCenter.x));
36 |
37 | let x = Math.abs(Math.cos(angle) * distanceDifference);
38 | let y = Math.abs(Math.sin(angle) * distanceDifference);
39 |
40 | return {
41 | x: elCenter.x < winsize.width/2 ? x*-1 : x,
42 | y: elCenter.y < winsize.height/2 ? y*-1 : y
43 | };
44 | };
45 |
46 | export {
47 | preloadImages,
48 | preloadFonts,
49 | getInitialPosition,
50 | };
--------------------------------------------------------------------------------