├── .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 | ![Image Title](https://tympanus.net/codrops/wp-content/uploads/2023/03/ClipAnimation_featured.jpg) 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 |
22 |
23 |

Fullscreen Clip Animation

24 | 25 | Back to the article 26 | 27 | 28 | 29 |
30 | Previous demo 31 | 38 |
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 |
22 |
23 |

Fullscreen Clip Animation

24 | 25 | Back to the article 26 | 27 | 28 | 29 |
30 | Previous demo 31 | 38 |
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 |
22 |
23 |

Fullscreen Clip Animation

24 | 25 | Back to the article 26 | 27 | 28 | 29 |
30 | Previous demo 31 | 38 |
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 |
22 |
23 |

Fullscreen Clip Animation

24 | 25 | Back to the article 26 | 27 | 28 | 29 |
30 | Previous demo 31 | 38 |
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 | }; --------------------------------------------------------------------------------