act( )rs
35 |Blinded by screens, in a narrative cage,
36 | But there's a call beyond, igniting our rage.
37 | Every show, every story, molding the pics in our head,
38 | Time to shatter the frames, live by our own thread.
├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── css └── base.css ├── favicon.ico ├── img ├── 1.jpg ├── 10.jpg ├── 11.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg └── star.svg ├── index.html ├── index2.html ├── index3.html ├── index4.html ├── index5.html ├── index6.html └── js ├── content.js ├── demo1 └── index.js ├── demo2 └── index.js ├── demo3 └── index.js ├── demo4 └── index.js ├── demo5 └── index.js ├── demo6 └── index.js ├── gsap.min.js ├── imagesloaded.pkgd.min.js ├── item.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 - 2022 [Codrops](https://tympanus.net/codrops) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image Layer Animations with Clip-Path 2 | 3 | Some ideas for speedy page transition animations with layered images using clip-path. 4 | 5 |  6 | 7 | [Article on Codrops](https://tympanus.net/codrops/?p=74227) 8 | 9 | [Demo](http://tympanus.net/Development/LayersAnimation/) 10 | 11 | ## Installation 12 | 13 | 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). 14 | 15 | ## Credits 16 | 17 | - Images generated with [Midjourney](https://midjourney.com) 18 | 19 | ## Misc 20 | 21 | 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/) 22 | 23 | ## License 24 | [MIT](LICENSE) 25 | 26 | Made with :blue_heart: by [Codrops](http://www.codrops.com) 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /css/base.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::after, 3 | *::before { 4 | box-sizing: border-box; 5 | } 6 | 7 | :root { 8 | font-size: 14px; 9 | --color-text: #000; 10 | --color-bg: #f07547; 11 | --color-link: rgb(0 0 0 / 70%); 12 | --color-link-hover: #000; 13 | --layer-width: 100vw; 14 | --layer-height: 100vh; 15 | --layer-radius: 0; 16 | --page-padding: 1rem; 17 | } 18 | 19 | .demo-2 { 20 | --color-bg: #94c3c1; 21 | } 22 | 23 | .demo-3 { 24 | --layer-width: 98vw; 25 | --layer-height: 96vh; 26 | --layer-radius: 20px; 27 | --color-bg: #f4b3b9; 28 | } 29 | 30 | .demo-4 { 31 | --layer-width: 98vw; 32 | --layer-height: 96vh; 33 | --color-bg: #b3f4d7; 34 | } 35 | 36 | .demo-5 { 37 | --color-bg: #d4b3f4; 38 | } 39 | 40 | .demo-6 { 41 | --color-bg: #f4f3b3; 42 | } 43 | 44 | .demo-7 { 45 | --color-bg: #f1b454; 46 | --layer-width: 100vw; 47 | --layer-height: 100vh; 48 | } 49 | 50 | body { 51 | margin: 0; 52 | color: var(--color-text); 53 | background-color: var(--color-bg); 54 | font-family: "alfabet",-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif; 55 | -webkit-font-smoothing: antialiased; 56 | -moz-osx-font-smoothing: grayscale; 57 | width: 100%; 58 | min-height: 100vh; 59 | min-height: -webkit-fill-available; 60 | overflow: hidden; 61 | } 62 | 63 | /* Page Loader */ 64 | .js .loading::before, 65 | .js .loading::after { 66 | content: ''; 67 | position: fixed; 68 | z-index: 1000; 69 | } 70 | 71 | .js .loading::before { 72 | top: 0; 73 | left: 0; 74 | width: 100%; 75 | height: 100%; 76 | background: var(--color-bg); 77 | } 78 | 79 | .js .loading::after { 80 | top: 50%; 81 | left: 50%; 82 | width: 60px; 83 | height: 60px; 84 | margin: -30px 0 0 -30px; 85 | border-radius: 50%; 86 | opacity: 0.4; 87 | background: var(--color-link); 88 | animation: loaderAnim 0.7s linear infinite alternate forwards; 89 | 90 | } 91 | 92 | @keyframes loaderAnim { 93 | to { 94 | opacity: 1; 95 | transform: scale3d(0.5,0.5,1); 96 | } 97 | } 98 | 99 | a { 100 | text-decoration: none; 101 | color: var(--color-link); 102 | outline: none; 103 | cursor: pointer; 104 | } 105 | 106 | a:hover { 107 | color: var(--color-link-hover); 108 | outline: none; 109 | } 110 | 111 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */ 112 | a:focus { 113 | /* Provide a fallback style for browsers 114 | that don't support :focus-visible */ 115 | outline: none; 116 | background: lightgrey; 117 | } 118 | 119 | a:focus:not(:focus-visible) { 120 | /* Remove the focus indicator on mouse-focus for browsers 121 | that do support :focus-visible */ 122 | background: transparent; 123 | } 124 | 125 | a:focus-visible { 126 | /* Draw a very noticeable focus style for 127 | keyboard-focus on browsers that do support 128 | :focus-visible */ 129 | outline: 2px solid red; 130 | background: transparent; 131 | } 132 | 133 | main { 134 | display: grid; 135 | grid-template-areas: 'main'; 136 | width: 100%; 137 | height: 100vh; 138 | } 139 | 140 | main::before { 141 | position: fixed; 142 | top: 30vh; 143 | right: 4vw; 144 | content: 'Click da body'; 145 | background: url(../img/star.svg) no-repeat 50% 50%; 146 | background-size: 100%; 147 | font-size: 2vmax; 148 | padding: 10vmax; 149 | width: 30vmax; 150 | height: 30vmax; 151 | text-align: center; 152 | line-height: 0.8; 153 | display: grid; 154 | place-items: center; 155 | transform: rotate(12.5deg); 156 | font-family: "gabriella", sans-serif; 157 | text-transform: uppercase; 158 | font-weight: 900; 159 | } 160 | 161 | .unbutton { 162 | background: none; 163 | border: 0; 164 | padding: 0; 165 | margin: 0; 166 | font: inherit; 167 | cursor: pointer; 168 | } 169 | 170 | .unbutton:focus { 171 | outline: none; 172 | } 173 | 174 | .frame { 175 | grid-area: main; 176 | position: relative; 177 | padding: var(--page-padding); 178 | display: grid; 179 | z-index: 1000; 180 | grid-template-columns: auto 1fr; 181 | grid-template-areas: 'title title' 'prev back' 'sponsor sponsor' 'demos demos'; 182 | grid-row-gap: 1rem; 183 | grid-column-gap: 2rem; 184 | pointer-events: none; 185 | align-content: start; 186 | } 187 | 188 | .frame a { 189 | pointer-events: auto; 190 | } 191 | 192 | .frame__title { 193 | grid-area: title; 194 | font-size: inherit; 195 | margin: 0; 196 | } 197 | 198 | .frame__back { 199 | grid-area: back; 200 | justify-self: start; 201 | } 202 | 203 | .frame__prev { 204 | grid-area: prev; 205 | justify-self: start; 206 | } 207 | 208 | .frame__demos { 209 | grid-area: demos; 210 | position: relative; 211 | overflow: hidden; 212 | display: flex; 213 | align-items: center; 214 | gap: 0.5rem; 215 | align-self: start; 216 | justify-self: start; 217 | flex-wrap: wrap; 218 | } 219 | 220 | .frame__demos span { 221 | margin-right: 1rem; 222 | width: 100%; 223 | } 224 | 225 | .frame__demo { 226 | width: 3rem; 227 | aspect-ratio: 1; 228 | height: auto; 229 | display: block; 230 | flex: none; 231 | display: grid; 232 | place-items: center; 233 | } 234 | 235 | .frame__demo--current, 236 | .frame__demo--current:focus, 237 | .frame__demo--current:hover { 238 | background: var(--color-link-hover); 239 | color: var(--color-bg); 240 | } 241 | 242 | body #cdawrap { 243 | justify-self: start; 244 | } 245 | 246 | .content { 247 | grid-area: main; 248 | display: grid; 249 | width: 100vw; 250 | height: 100vh; 251 | position: relative; 252 | grid-template-areas: 'content'; 253 | } 254 | 255 | .content__inner { 256 | grid-area: content; 257 | width: 100%; 258 | display: grid; 259 | grid-template-areas: 'text' 'headline'; 260 | padding: 2rem var(--page-padding) var(--page-padding); 261 | grid-template-rows: 1fr auto; 262 | will-change: transform; 263 | } 264 | 265 | .hidden { 266 | visibility: hidden; 267 | pointer-events: none; 268 | } 269 | 270 | .content__inner h2 { 271 | grid-area: headline; 272 | font-size: clamp(2rem, 10vw,13rem); 273 | font-family: "gabriella", sans-serif; 274 | text-transform: uppercase; 275 | font-weight: 900; 276 | margin: 0; 277 | line-height: 1; 278 | } 279 | 280 | .content__inner p { 281 | grid-area: text; 282 | font-size: clamp(1rem,3vw,2.5rem); 283 | line-height: 1.2; 284 | font-weight: 300; 285 | align-self: end; 286 | } 287 | 288 | .layers { 289 | grid-area: content; 290 | align-self: center; 291 | justify-self: center; 292 | flex: none; 293 | position: relative; 294 | width: var(--layer-width); 295 | height: var(--layer-height); 296 | } 297 | 298 | .layers__item, 299 | .layers__item-img { 300 | position: absolute; 301 | width: 100%; 302 | height: 100%; 303 | } 304 | 305 | .layers__item { 306 | overflow: hidden; 307 | opacity: 0; 308 | will-change: clip-path; 309 | border-radius: var(--layer-radius); 310 | } 311 | 312 | .layers__item-img { 313 | background-size: cover; 314 | background-position: 50% 50%; 315 | } 316 | 317 | @media screen and (min-width: 53em) { 318 | :root { 319 | --page-padding: 3rem; 320 | } 321 | .frame { 322 | height: 100vh; 323 | width: 100%; 324 | grid-template-columns: auto auto auto 1fr; 325 | grid-template-rows: auto auto; 326 | grid-template-areas: 'title prev back sponsor' 'demos demos demos demos'; 327 | align-content: space-between; 328 | justify-items: start; 329 | grid-gap: 2rem; 330 | } 331 | .frame__demos { 332 | padding-left: 2rem; 333 | border: 1px solid; 334 | border-radius: 2rem; 335 | } 336 | .frame__demos span { 337 | width: auto; 338 | } 339 | .content { 340 | height: 100vh; 341 | } 342 | .content__inner { 343 | padding: 2rem var(--page-padding) calc(var(--page-padding) + 15vh); 344 | } 345 | main::before { 346 | top: 4vh; 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/favicon.ico -------------------------------------------------------------------------------- /img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/1.jpg -------------------------------------------------------------------------------- /img/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/10.jpg -------------------------------------------------------------------------------- /img/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/11.jpg -------------------------------------------------------------------------------- /img/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/2.jpg -------------------------------------------------------------------------------- /img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/3.jpg -------------------------------------------------------------------------------- /img/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/4.jpg -------------------------------------------------------------------------------- /img/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/5.jpg -------------------------------------------------------------------------------- /img/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/6.jpg -------------------------------------------------------------------------------- /img/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/7.jpg -------------------------------------------------------------------------------- /img/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/8.jpg -------------------------------------------------------------------------------- /img/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/LayersAnimation/fec671735be6aa68949a5fabcda1967658afc717/img/9.jpg -------------------------------------------------------------------------------- /img/star.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |Blinded by screens, in a narrative cage,
36 | But there's a call beyond, igniting our rage.
37 | Every show, every story, molding the pics in our head,
38 | Time to shatter the frames, live by our own thread.
Masses walkin' blind, in a world of haze,
36 | But there's a call beyond, a beacon that blazes.
37 | It's the voice of freedom, the heart of the wild,
38 | Urging us to see, through the eyes of a child.
In the shadows, they dwell, with power to lease,
36 | Society’s strings, they pull in peace.
37 | In the grand show, they silently bark orders,
38 | While we walk the path, within set borders.
Concrete battles, where silence is gold,
36 | But a ripple in the pond, tales of old.
37 | To shatter the chains, to chart our own way,
38 | To light up the path, bring forth the day.
It ain't about chaos, but clarity so pure,
36 | With every act of defiance, the intent's sure.
37 | In this land of illusion, where truths are undone,
38 | We've got a duty to awaken, and become one.
In the city's trance, where perceptions are rehearsed,
36 | With conviction and clarity, their illusions we'll burst.
37 | Brother may craft the tales, but we pen the last list,
38 | With eyes wide open, in truth, we'll exist.
.
9 | };
10 |
11 | /**
12 | * Constructor for the Content class. Initializes the instance, sets up DOM references, and binds events.
13 | * @param {HTMLElement} DOM_el - The main DOM element for the Content
14 | */
15 | constructor(DOM_el) {
16 | // Assign the provided DOM element to the 'el' property of the 'DOM' object.
17 | this.DOM.el = DOM_el;
18 | this.DOM.title = this.DOM.el.querySelector('h2');
19 | this.DOM.description = this.DOM.el.querySelector('p');
20 | }
21 | }
--------------------------------------------------------------------------------
/js/demo1/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
10 | const DOMItems = [...document.querySelectorAll('.layers__item')];
11 | const items = []; // Array to store instances of the Item class
12 |
13 | // Creating new instances of Item for each selected DOM element
14 | DOMItems.forEach(item => {
15 | items.push(new Item(item)); // Initializing a new object for each item
16 | });
17 |
18 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
19 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
20 | const contents = []; // Array to store instances of the Content class
21 |
22 | // Creating new instances of Content for each selected DOM element
23 | DOMContentSections.forEach(content => {
24 | contents.push(new Content(content)); // Initializing a new object for each content
25 | });
26 |
27 | // Toggle the "hidden" class between two content elements
28 | const toggleContent = () => {
29 | // Assuming there are only two content elements
30 | const [content1, content2] = contents;
31 |
32 | // Toggle the 'hidden' class on the first content element
33 | if (content1.DOM.el.classList.contains('hidden')) {
34 | content1.DOM.el.classList.remove('hidden');
35 | content2.DOM.el.classList.add('hidden');
36 | } else {
37 | content1.DOM.el.classList.add('hidden');
38 | content2.DOM.el.classList.remove('hidden');
39 | }
40 | };
41 |
42 | // GSAP timeline
43 | let tl = null;
44 |
45 | // Setting up the animation properties
46 | const animationSettings = {
47 | duration: 1.4, // Duration of the animation
48 | ease: 'power3.inOut', // Type of easing to use for the animation transition
49 | delayFactor: 0.2 // Delay between each item's animation
50 | };
51 |
52 | // Event listener for click events on the document
53 | document.addEventListener('click', event => {
54 | // Check if the timeline is currently active (running)
55 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
56 | return false; // Don't start a new animation
57 | }
58 |
59 | // The currently active content element
60 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
61 |
62 | // Assuming there are only two content elements
63 | const contentInactive = contents.find(content => content !== contentActive);
64 |
65 | // Mapping each Item object to its actual DOM element for the animation
66 | const allItems = items.map(item => item.DOM.el);
67 |
68 | // Isolating the last item's DOM element for a separate animation effect
69 | const lastItem = items[items.length - 1].DOM.el;
70 |
71 | // Mapping each Item object to its 'inner' property (inner image)
72 | const allInnerItems = items.map(item => item.DOM.inner);
73 |
74 | // Creating a new GSAP timeline for managing a sequence of animations
75 | tl = gsap.timeline({
76 | paused: true, // Create the timeline in a paused state
77 | defaults: { // Default settings applied to all animations within this timeline
78 | duration: animationSettings.duration,
79 | ease: animationSettings.ease,
80 | }
81 | })
82 | .fromTo(allItems, { // Initial animation state
83 | opacity: 1, // Fully visible
84 | 'clip-path': 'polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)' // CSS clip-path shape
85 | }, { // Animation target state
86 | stagger: { // Settings for staggering animations for each item
87 | each: animationSettings.delayFactor, // Time between each item's animation
88 | onComplete: function() { // Callback after each item finishes animating
89 | const targetElement = this.targets()[0]; // The element that just finished animating
90 | // Determining the index of the animated element within the original DOM NodeList
91 | const index = DOMItems.indexOf(targetElement);
92 | if ( index ) { // If the element is not the first one (index 0)
93 | // Set the opacity of the previous element to 0
94 | gsap.set(items[index-1].DOM.el, {opacity: 0});
95 | }
96 | },
97 | },
98 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)', // Target shape of the clip-path
99 | }, 0)
100 | .fromTo(allInnerItems, { // Starting state for 'inner' elements' animation
101 | yPercent: 0,
102 | filter: 'brightness(30%)' // CSS filters to adjust color
103 | }, { // Animation target state
104 | stagger: animationSettings.delayFactor, // Stagger settings similar to above
105 | filter: 'brightness(100%)' // Full brightness
106 | }, 0)
107 | .to([contentActive.DOM.title, contentActive.DOM.description], {
108 | startAt: {yPercent: 0},
109 | stagger: -0.04,
110 | yPercent: -200
111 | }, 0)
112 | .add((() => toggleContent()))
113 | .fromTo([contentInactive.DOM.title, contentInactive.DOM.description], {
114 | yPercent: 200
115 | }, {
116 | ease: 'expo', // Different easing effect
117 | stagger: -0.15,
118 | yPercent: 0
119 | })
120 | .to(lastItem, { // Animation for the last item
121 | duration: 1,
122 | ease: 'power4', // Different easing effect
123 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 0%, 0% 0%)', // Animating clip-path to different shape
124 | onComplete: () => gsap.set(lastItem, {opacity: 0}) // After animation, hide the last item
125 | }, '<');
126 |
127 | // Start the animation
128 | tl.play();
129 |
130 | });
131 |
132 | // Preloading all images specified by the selector
133 | preloadImages('.layers__item-img').then(() => {
134 | // Once images are preloaded, remove the 'loading' indicator/class from the body
135 | document.body.classList.remove('loading');
136 | });
137 |
--------------------------------------------------------------------------------
/js/demo2/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting the element with class 'layers'
10 | const DOMlayers = document.querySelector('.layers');
11 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
12 | const DOMItems = [...DOMlayers.querySelectorAll('.layers__item')];
13 | const items = []; // Array to store instances of the Item class
14 |
15 | // Creating new instances of Item for each selected DOM element
16 | DOMItems.forEach(item => {
17 | items.push(new Item(item)); // Initializing a new object for each item
18 | });
19 |
20 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
21 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
22 | const contents = []; // Array to store instances of the Content class
23 |
24 | // Creating new instances of Content for each selected DOM element
25 | DOMContentSections.forEach(content => {
26 | contents.push(new Content(content)); // Initializing a new object for each content
27 | });
28 |
29 | // Toggle the "hidden" class between two content elements
30 | const toggleContent = () => {
31 | // Assuming there are only two content elements
32 | const [content1, content2] = contents;
33 |
34 | // Toggle the 'hidden' class on the first content element
35 | if (content1.DOM.el.classList.contains('hidden')) {
36 | content1.DOM.el.classList.remove('hidden');
37 | content2.DOM.el.classList.add('hidden');
38 | } else {
39 | content1.DOM.el.classList.add('hidden');
40 | content2.DOM.el.classList.remove('hidden');
41 | }
42 | };
43 |
44 | // GSAP timeline
45 | let tl = null;
46 |
47 | // Setting up the animation properties
48 | const animationSettings = {
49 | duration: 0.5, // Duration of the animation
50 | ease: 'power3.inOut', // Type of easing to use for the animation transition
51 | delayFactor: 0.13 // Delay between each item's animation
52 | };
53 |
54 | // Event listener for click events on the document
55 | document.addEventListener('click', event => {
56 | // Check if the timeline is currently active (running)
57 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
58 | return false; // Don't start a new animation
59 | }
60 |
61 | // The currently active content element
62 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
63 |
64 | // Assuming there are only two content elements
65 | const contentInactive = contents.find(content => content !== contentActive);
66 |
67 | // Mapping each Item object to its actual DOM element for the animation
68 | const allItems = items.map(item => item.DOM.el);
69 |
70 | // Creating a new GSAP timeline for managing a sequence of animations
71 | tl = gsap.timeline({
72 | paused: true, // Create the timeline in a paused state
73 | defaults: { // Default settings applied to all animations within this timeline
74 | duration: animationSettings.duration,
75 | ease: animationSettings.ease,
76 | }
77 | })
78 | .fromTo(allItems, { // Initial animation state
79 | opacity: 1, // Fully visible
80 | 'clip-path': 'polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%)', // CSS clip-path shape
81 | }, { // Animation target state
82 | ease: 'power3.in',
83 | stagger: animationSettings.delayFactor, // Time between each item's animation
84 | 'clip-path': 'polygon(50% 0%, 83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%)', // Target shape of the clip-path
85 | }, 0)
86 | .to(allItems, { // Animation target state
87 | ease: 'power3',
88 | stagger: animationSettings.delayFactor, // Time between each item's animation
89 | 'clip-path': 'polygon(50% 0%, 100% 0%, 100% 43%, 100% 100%, 68% 100%, 32% 100%, 0% 100%, 0% 43%, 0% 0%)', // Target shape of the clip-path
90 | }, 0.5)
91 | .to(contentActive.DOM.el, {
92 | duration: 1,
93 | startAt: {scale: 1},
94 | scale: 1.2,
95 | opacity: 0
96 | }, 0)
97 | .add((() => toggleContent()))
98 | .to(allItems, { // Animation for the last item
99 | ease: 'power3.in',
100 | stagger: -1*animationSettings.delayFactor, // Time between each item's animation
101 | 'clip-path': 'polygon(50% 0%, 83% 12%, 100% 43%, 94% 78%, 68% 100%, 32% 100%, 6% 78%, 0% 43%, 17% 12%)', // Animating clip-path to different shape
102 | }, '<')
103 | .to(allItems, { // Animation for the last item
104 | ease: 'power3',
105 | stagger: -1*animationSettings.delayFactor, // Time between each item's animation
106 | 'clip-path': 'polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%, 50% 50%)', // Animating clip-path to different shape
107 | }, '<+=0.5')
108 | .fromTo(contentInactive.DOM.el, {
109 | scale: 1.8,
110 | opacity: 1
111 | }, {
112 | duration: 1,
113 | ease: 'expo',
114 | scale: 1
115 | }, '<+=0.5');
116 |
117 | // Start the animation
118 | tl.play();
119 |
120 | });
121 |
122 | // Preloading all images specified by the selector
123 | preloadImages('.layers__item-img').then(() => {
124 | // Once images are preloaded, remove the 'loading' indicator/class from the body
125 | document.body.classList.remove('loading');
126 | });
127 |
--------------------------------------------------------------------------------
/js/demo3/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting the element with class 'layers'
10 | const DOMlayers = document.querySelector('.layers');
11 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
12 | const DOMItems = [...DOMlayers.querySelectorAll('.layers__item')];
13 | const items = []; // Array to store instances of the Item class
14 |
15 | // Creating new instances of Item for each selected DOM element
16 | DOMItems.forEach(item => {
17 | items.push(new Item(item)); // Initializing a new object for each item
18 | });
19 |
20 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
21 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
22 | const contents = []; // Array to store instances of the Content class
23 |
24 | // Creating new instances of Content for each selected DOM element
25 | DOMContentSections.forEach(content => {
26 | contents.push(new Content(content)); // Initializing a new object for each content
27 | });
28 |
29 | // Toggle the "hidden" class between two content elements
30 | const toggleContent = () => {
31 | // Assuming there are only two content elements
32 | const [content1, content2] = contents;
33 |
34 | // Toggle the 'hidden' class on the first content element
35 | if (content1.DOM.el.classList.contains('hidden')) {
36 | content1.DOM.el.classList.remove('hidden');
37 | content2.DOM.el.classList.add('hidden');
38 | } else {
39 | content1.DOM.el.classList.add('hidden');
40 | content2.DOM.el.classList.remove('hidden');
41 | }
42 | };
43 |
44 | // GSAP timeline
45 | let tl = null;
46 |
47 | // Setting up the animation properties
48 | const animationSettings = {
49 | duration: 1.4, // Duration of the animation
50 | ease: 'power3.inOut', // Type of easing to use for the animation transition
51 | delayFactor: 0.2 // Delay between each item's animation
52 | };
53 |
54 | // Event listener for click events on the document
55 | document.addEventListener('click', event => {
56 | // Check if the timeline is currently active (running)
57 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
58 | return false; // Don't start a new animation
59 | }
60 |
61 | // The currently active content element
62 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
63 |
64 | // Assuming there are only two content elements
65 | const contentInactive = contents.find(content => content !== contentActive);
66 |
67 | // Mapping each Item object to its actual DOM element for the animation
68 | const allItems = items.map(item => item.DOM.el);
69 |
70 | // Isolating the last item's DOM element for a separate animation effect
71 | const lastItem = items[items.length - 1].DOM.el;
72 |
73 | // Mapping each Item object to its 'inner' property (inner image)
74 | const allInnerItems = items.map(item => item.DOM.inner);
75 |
76 | const lastInner = items[items.length - 1].DOM.inner;
77 |
78 | // Creating a new GSAP timeline for managing a sequence of animations
79 | tl = gsap.timeline({
80 | paused: true, // Create the timeline in a paused state
81 | defaults: { // Default settings applied to all animations within this timeline
82 | duration: animationSettings.duration,
83 | ease: animationSettings.ease,
84 | }
85 | })
86 | .fromTo(DOMlayers, {
87 | scale: 0.9
88 | }, {
89 | duration: animationSettings.duration + animationSettings.delayFactor*(items.length-1) ,
90 | ease: 'linear',
91 | scale: 1.1
92 | }, 0)
93 | .fromTo(allItems, { // Initial animation state
94 | opacity: 1, // Fully visible
95 | 'clip-path': 'polygon(100% 0%, 100% 0%, 100% 100%, 100% 100%)' // CSS clip-path shape
96 | }, { // Animation target state
97 | stagger: { // Settings for staggering animations for each item
98 | each: animationSettings.delayFactor, // Time between each item's animation
99 | onComplete: function() { // Callback after each item finishes animating
100 | const targetElement = this.targets()[0]; // The element that just finished animating
101 | // Determining the index of the animated element within the original DOM NodeList
102 | const index = DOMItems.indexOf(targetElement);
103 | if ( index ) { // If the element is not the first one (index 0)
104 | // Set the opacity of the previous element to 0
105 | gsap.set(items[index-1].DOM.el, {opacity: 0});
106 | }
107 | }
108 | },
109 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)', // Target shape of the clip-path
110 | }, 0)
111 | .fromTo(allInnerItems, { // Starting state for 'inner' elements' animation
112 | xPercent: 0,
113 | filter: 'brightness(15%)' // CSS filters to adjust color
114 | }, { // Animation target state
115 | stagger: animationSettings.delayFactor, // Stagger settings similar to above
116 | filter: 'brightness(120%)' // Full brightness
117 | }, 0)
118 | .to([contentActive.DOM.title, contentActive.DOM.description], {
119 | startAt: {xPercent: 0, opacity: 1},
120 | stagger: 0.08,
121 | xPercent: -100,
122 | opacity: 0
123 | }, 0)
124 | .addLabel('reveal', animationSettings.duration + animationSettings.delayFactor*(items.length-1))
125 | .add(() => toggleContent(), 'reveal')
126 | .to(lastItem, { // Animation for the last item
127 | duration: 1,
128 | ease: 'power3', // Different easing effect
129 | 'clip-path': 'polygon(0% 0%, 0% 0%, 0% 100%, 0% 100%)', // Animating clip-path to different shape
130 | onComplete: () => gsap.set(lastItem, {opacity: 0}) // After animation, hide the last item
131 | }, 'reveal')
132 | .to(lastInner, {
133 | duration: 1,
134 | ease: 'power3', // Different easing effect
135 | xPercent: -20,
136 | filter: 'brightness(50%)',
137 | }, '<')
138 | .fromTo([contentInactive.DOM.title, contentInactive.DOM.description], {
139 | xPercent: 100,
140 | opacity: 0
141 | }, {
142 | duration: .8,
143 | ease: 'power3', // Different easing effect
144 | stagger: -0.15,
145 | opacity: 1,
146 | xPercent: 0
147 | }, '<')
148 |
149 | // Start the animation
150 | tl.play();
151 |
152 | });
153 |
154 | // Preloading all images specified by the selector
155 | preloadImages('.layers__item-img').then(() => {
156 | // Once images are preloaded, remove the 'loading' indicator/class from the body
157 | document.body.classList.remove('loading');
158 | });
159 |
--------------------------------------------------------------------------------
/js/demo4/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
10 | const DOMItems = [...document.querySelectorAll('.layers__item')];
11 | const items = []; // Array to store instances of the Item class
12 |
13 | // Creating new instances of Item for each selected DOM element
14 | DOMItems.forEach(item => {
15 | items.push(new Item(item)); // Initializing a new object for each item
16 | });
17 |
18 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
19 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
20 | const contents = []; // Array to store instances of the Content class
21 |
22 | // Creating new instances of Content for each selected DOM element
23 | DOMContentSections.forEach(content => {
24 | contents.push(new Content(content)); // Initializing a new object for each content
25 | });
26 |
27 | // Toggle the "hidden" class between two content elements
28 | const toggleContent = () => {
29 | // Assuming there are only two content elements
30 | const [content1, content2] = contents;
31 |
32 | // Toggle the 'hidden' class on the first content element
33 | if (content1.DOM.el.classList.contains('hidden')) {
34 | content1.DOM.el.classList.remove('hidden');
35 | content2.DOM.el.classList.add('hidden');
36 | } else {
37 | content1.DOM.el.classList.add('hidden');
38 | content2.DOM.el.classList.remove('hidden');
39 | }
40 | };
41 |
42 | // GSAP timeline
43 | let tl = null;
44 |
45 | // Setting up the animation properties
46 | const animationSettings = {
47 | duration: 1.2, // Duration of the animation
48 | ease: 'power3.inOut', // Type of easing to use for the animation transition
49 | delayFactor: 0.1 // Delay between each item's animation
50 | };
51 |
52 | // Event listener for click events on the document
53 | document.addEventListener('click', event => {
54 | // Check if the timeline is currently active (running)
55 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
56 | return false; // Don't start a new animation
57 | }
58 |
59 | // The currently active content element
60 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
61 |
62 | // Assuming there are only two content elements
63 | const contentInactive = contents.find(content => content !== contentActive);
64 |
65 | // Mapping each Item object to its actual DOM element for the animation
66 | const allItems = items.map(item => item.DOM.el);
67 |
68 | // Creating a new GSAP timeline for managing a sequence of animations
69 | tl = gsap.timeline({
70 | paused: true, // Create the timeline in a paused state
71 | defaults: { // Default settings applied to all animations within this timeline
72 | duration: animationSettings.duration,
73 | ease: animationSettings.ease,
74 | }
75 | })
76 | .fromTo(allItems, { // Initial animation state
77 | opacity: 1, // Fully visible
78 | 'clip-path': 'polygon(100% 100%, 100% 0%, 100% 100%, 0% 100%)' // CSS clip-path shape
79 | }, { // Animation target state
80 | stagger: animationSettings.delayFactor, // Time between each item's animation
81 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)', // Target shape of the clip-path
82 | }, 0)
83 | .to(contentActive.DOM.el, {
84 | startAt: {xPercent: 0, opacity: 1},
85 | xPercent: -20,
86 | yPercent: -50,
87 | opacity: 0
88 | }, 0)
89 | .add(() => toggleContent())
90 | .to(allItems, { // Animation for the last item
91 | duration: 1,
92 | stagger: -1*animationSettings.delayFactor, // Time between each item's animation
93 | 'clip-path': 'polygon(0% 0%, 0% 100%, 100% 100%, 0% 100%)', // Animating clip-path to different shape
94 | }, '>')
95 | .fromTo(contentInactive.DOM.el, {
96 | xPercent: 20,
97 | yPercent: -50,
98 | opacity: 0
99 | }, {
100 | duration: 1,
101 | opacity: 1,
102 | xPercent: 0,
103 | yPercent: 0
104 | }, `>-=${.8*animationSettings.duration}`)
105 |
106 | // Start the animation
107 | tl.play();
108 |
109 | });
110 |
111 | // Preloading all images specified by the selector
112 | preloadImages('.layers__item-img').then(() => {
113 | // Once images are preloaded, remove the 'loading' indicator/class from the body
114 | document.body.classList.remove('loading');
115 | });
116 |
--------------------------------------------------------------------------------
/js/demo5/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
10 | const DOMItems = [...document.querySelectorAll('.layers__item')];
11 | const items = []; // Array to store instances of the Item class
12 |
13 | // Creating new instances of Item for each selected DOM element
14 | DOMItems.forEach(item => {
15 | items.push(new Item(item)); // Initializing a new object for each item
16 | });
17 |
18 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
19 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
20 | const contents = []; // Array to store instances of the Content class
21 |
22 | // Creating new instances of Content for each selected DOM element
23 | DOMContentSections.forEach(content => {
24 | contents.push(new Content(content)); // Initializing a new object for each content
25 | });
26 |
27 | // Toggle the "hidden" class between two content elements
28 | const toggleContent = () => {
29 | // Assuming there are only two content elements
30 | const [content1, content2] = contents;
31 |
32 | // Toggle the 'hidden' class on the first content element
33 | if (content1.DOM.el.classList.contains('hidden')) {
34 | content1.DOM.el.classList.remove('hidden');
35 | content2.DOM.el.classList.add('hidden');
36 | } else {
37 | content1.DOM.el.classList.add('hidden');
38 | content2.DOM.el.classList.remove('hidden');
39 | }
40 | };
41 |
42 | // GSAP timeline
43 | let tl = null;
44 |
45 | // Setting up the animation properties
46 | const animationSettings = {
47 | duration: 1.4, // Duration of the animation
48 | ease: 'power3.inOut', // Type of easing to use for the animation transition
49 | delayFactor: 0.14 // Delay between each item's animation
50 | };
51 |
52 | // Event listener for click events on the document
53 | document.addEventListener('click', event => {
54 | // Check if the timeline is currently active (running)
55 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
56 | return false; // Don't start a new animation
57 | }
58 |
59 | // The currently active content element
60 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
61 |
62 | // Assuming there are only two content elements
63 | const contentInactive = contents.find(content => content !== contentActive);
64 |
65 | // Mapping each Item object to its actual DOM element for the animation
66 | const allItems = items.map(item => item.DOM.el);
67 |
68 | // Isolating the last item's DOM element for a separate animation effect
69 | const lastItem = items[items.length - 1].DOM.el;
70 |
71 | // Mapping each Item object to its 'inner' property (inner image)
72 | const allInnerItems = items.map(item => item.DOM.inner);
73 |
74 | const lastInner = items[items.length - 1].DOM.inner;
75 |
76 | // Creating a new GSAP timeline for managing a sequence of animations
77 | tl = gsap.timeline({
78 | paused: true, // Create the timeline in a paused state
79 | defaults: { // Default settings applied to all animations within this timeline
80 | duration: animationSettings.duration,
81 | ease: animationSettings.ease,
82 | }
83 | })
84 | .fromTo(allItems, { // Initial animation state
85 | opacity: 1, // Fully visible
86 | 'clip-path': 'polygon(45% 50%, 55% 50%, 50% 50%, 50% 50%)' // CSS clip-path shape
87 | }, { // Animation target state
88 | stagger: { // Settings for staggering animations for each item
89 | each: animationSettings.delayFactor, // Time between each item's animation
90 | onComplete: function() { // Callback after each item finishes animating
91 | const targetElement = this.targets()[0]; // The element that just finished animating
92 | // Determining the index of the animated element within the original DOM NodeList
93 | const index = DOMItems.indexOf(targetElement);
94 | if ( index ) { // If the element is not the first one (index 0)
95 | // Set the opacity of the previous element to 0
96 | gsap.set(items[index-1].DOM.el, {opacity: 0});
97 | }
98 | },
99 | },
100 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)', // Target shape of the clip-path
101 | }, 0)
102 | .fromTo(allInnerItems, { // Starting state for 'inner' elements' animation
103 | scale: 1.2,
104 | filter: 'brightness(30%)' // CSS filters to adjust color
105 | }, { // Animation target state
106 | stagger: animationSettings.delayFactor, // Stagger settings similar to above
107 | filter: 'brightness(100%)' // Full brightness
108 | }, 0)
109 | .to(contentActive.DOM.el, {
110 | startAt: {scale: 1},
111 | scale: 1.4,
112 | onComplete: () => gsap.to(contentActive.DOM.el, {scale: 1})
113 | }, 0)
114 | .add((() => toggleContent()))
115 | .fromTo([contentInactive.DOM.title, contentInactive.DOM.description], {
116 | yPercent: -100
117 | }, {
118 | ease: 'expo', // Different easing effect
119 | stagger: 0.05,
120 | yPercent: 0
121 | })
122 | .to(lastItem, { // Animation for the last item
123 | duration: 1,
124 | ease: 'expo',
125 | 'clip-path': 'polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%)', // Animating clip-path to different shape
126 | onComplete: () => gsap.set(lastItem, {opacity: 0}) // After animation, hide the last item
127 | }, '<')
128 | .to(lastInner, {
129 | duration: 1.4,
130 | ease: 'expo',
131 | scale: 2
132 | }, '<');
133 |
134 | // Start the animation
135 | tl.play();
136 |
137 | });
138 |
139 | // Preloading all images specified by the selector
140 | preloadImages('.layers__item-img').then(() => {
141 | // Once images are preloaded, remove the 'loading' indicator/class from the body
142 | document.body.classList.remove('loading');
143 | });
144 |
--------------------------------------------------------------------------------
/js/demo6/index.js:
--------------------------------------------------------------------------------
1 | // Importing necessary functions and classes from other files
2 | import { preloadImages } from '../utils.js'; // Utility function for preloading images
3 | import { Item } from '../item.js'; // Item class
4 | import { Content } from '../content.js'; // Content class
5 |
6 | // frame element
7 | const frameElement = document.querySelector('.frame');
8 |
9 | // Selecting the element with class 'layers'
10 | const DOMlayers = document.querySelector('.layers');
11 | // Selecting all elements with class 'layers__item' and converting NodeList to an array
12 | const DOMItems = [...DOMlayers.querySelectorAll('.layers__item')];
13 | const items = []; // Array to store instances of the Item class
14 |
15 | // Creating new instances of Item for each selected DOM element
16 | DOMItems.forEach(item => {
17 | items.push(new Item(item)); // Initializing a new object for each item
18 | });
19 |
20 | // Selecting all elements with class 'content__inner' and converting NodeList to an array
21 | const DOMContentSections = [...document.querySelectorAll('.content > .content__inner')];
22 | const contents = []; // Array to store instances of the Content class
23 |
24 | // Creating new instances of Content for each selected DOM element
25 | DOMContentSections.forEach(content => {
26 | contents.push(new Content(content)); // Initializing a new object for each content
27 | });
28 |
29 | // Toggle the "hidden" class between two content elements
30 | const toggleContent = () => {
31 | // Assuming there are only two content elements
32 | const [content1, content2] = contents;
33 |
34 | // Toggle the 'hidden' class on the first content element
35 | if (content1.DOM.el.classList.contains('hidden')) {
36 | content1.DOM.el.classList.remove('hidden');
37 | content2.DOM.el.classList.add('hidden');
38 | } else {
39 | content1.DOM.el.classList.add('hidden');
40 | content2.DOM.el.classList.remove('hidden');
41 | }
42 | };
43 |
44 | // GSAP timeline
45 | let tl = null;
46 |
47 | // Setting up the animation properties
48 | const animationSettings = {
49 | duration: 0.9, // Duration of the animation
50 | ease: 'power2.inOut', // Type of easing to use for the animation transition
51 | delayFactor: 0.08 // Delay between each item's animation
52 | };
53 |
54 | // Event listener for click events on the document
55 | document.addEventListener('click', event => {
56 | // Check if the timeline is currently active (running)
57 | if (tl && tl.isActive() || frameElement.contains(event.target)) {
58 | return false; // Don't start a new animation
59 | }
60 |
61 | // The currently active content element
62 | const contentActive = contents.find(content => !content.DOM.el.classList.contains('hidden'));
63 |
64 | // Assuming there are only two content elements
65 | const contentInactive = contents.find(content => content !== contentActive);
66 |
67 | // Mapping each Item object to its actual DOM element for the animation
68 | const allItems = items.map(item => item.DOM.el);
69 |
70 | // Creating a new GSAP timeline for managing a sequence of animations
71 | tl = gsap.timeline({
72 | paused: true, // Create the timeline in a paused state
73 | defaults: { // Default settings applied to all animations within this timeline
74 | duration: animationSettings.duration,
75 | ease: animationSettings.ease,
76 | }
77 | })
78 | .fromTo(DOMlayers, {
79 | scale: 0.8
80 | }, {
81 | duration: animationSettings.duration + animationSettings.delayFactor*items.length,
82 | scale: 1
83 | }, 0)
84 | .fromTo(allItems, { // Initial animation state
85 | opacity: 1, // Fully visible
86 | 'clip-path': 'polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)' // CSS clip-path shape
87 | }, { // Animation target state
88 | stagger: animationSettings.delayFactor,
89 | 'clip-path': 'polygon(25% 0%, 75% 0%, 75% 100%, 25% 100%)', // Target shape of the clip-path
90 | }, 0)
91 | .to(contentActive.DOM.el, {
92 | startAt: {scale: 1, opacity: 1},
93 | stagger: -0.04,
94 | scale: 1.2,
95 | opacity: 0
96 | }, 0)
97 | .add((() => toggleContent()))
98 | .to(allItems, { // Animation target state
99 | ease: 'power3',
100 | stagger: animationSettings.delayFactor,
101 | 'clip-path': 'polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%)', // Target shape of the clip-path
102 | }, '>')
103 | .to(allItems, { // Animation target state
104 | stagger: -1*animationSettings.delayFactor*.7,
105 | 'clip-path': 'polygon(50% 50%, 50% 50%, 50% 50%, 50% 50%)', // Target shape of the clip-path
106 | }, '>-=0.2')
107 | .fromTo(contentInactive.DOM.el, {
108 | scale: 1.8,
109 | opacity: 0
110 | }, {
111 | duration: 1.2,
112 | ease: 'elastic.out(.4)', // Different easing effect
113 | scale: 1,
114 | opacity: 1,
115 | }, `>-=${.6*animationSettings.duration}`)
116 |
117 | // Start the animation
118 | tl.play();
119 | });
120 |
121 | // Preloading all images specified by the selector
122 | preloadImages('.layers__item-img').then(() => {
123 | // Once images are preloaded, remove the 'loading' indicator/class from the body
124 | document.body.classList.remove('loading');
125 | });
126 |
--------------------------------------------------------------------------------
/js/gsap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * GSAP 3.12.2
3 | * https://greensock.com
4 | *
5 | * @license Copyright 2023, GreenSock. All rights reserved.
6 | * Subject to the terms at https://greensock.com/standard-license or for Club GreenSock members, the agreement issued with that membership.
7 | * @author: Jack Doyle, jack@greensock.com
8 | */
9 |
10 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function P(t){return(i=yt(t,ot))&&Ee}function Q(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function R(t,e){return!e&&console.warn(t)}function S(t,e){return t&&(ot[t]=e)&&i&&(i[t]=e)||ot}function T(){return 0}function ea(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=gt.length;r--&&!gt[r].targetTest(i););e=gt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new Vt(t[r],e)))||t.splice(r,1);return t}function fa(t){return t._gsap||ea(Ot(t))[0]._gsap}function ga(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ha(t,e){return(t=t.split(",")).forEach(e)||t}function ia(t){return Math.round(1e5*t)/1e5||0}function ja(t){return Math.round(1e7*t)/1e7||0}function ka(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function la(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++i