├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── css ├── base.css ├── splitting-cells.css └── splitting.css ├── favicon.ico ├── img ├── img1.jpg ├── img2.jpg ├── img3.jpg ├── img4.jpg ├── img5.jpg ├── img6.jpg ├── img7.jpg ├── img8.jpg └── img9.jpg ├── index.html └── js ├── card.js ├── gsap.min.js ├── imagesloaded.pkgd.min.js ├── index.js ├── splitting.min.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 | # Sliced Image Hover Effect from Quai Network 2 | 3 | A similar hover animation to the one seen on the website of [Quai Network](https://qu.ai/). 4 | 5 | ![Sliced hover](https://tympanus.net/codrops/wp-content/uploads/2023/06/cliphoverslices_featured.gif) 6 | 7 | [Tutorial on Codrops](https://tympanus.net/codrops/?p=72588) 8 | 9 | [Demo](https://tympanus.net/Tutorials/ClipHoverEffect/) 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 made 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: 12px; 9 | --color-text: rgba(255,255,255,0.95); 10 | --color-bg: #0c0b10; 11 | --color-link: rgb(124 20 244 / 90%); 12 | --color-bg-date: rgb(96 56 178 / 48%); 13 | --color-link-hover: rgb(94 54 176 / 75%); 14 | --color-border: rgba(177,177,177,0.3); 15 | } 16 | 17 | body { 18 | margin: 0; 19 | color: var(--color-text); 20 | background-color: var(--color-bg); 21 | font-family: "ocr-a-std", monospace; 22 | text-transform: uppercase; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | /* Page Loader */ 28 | .js .loading::before, 29 | .js .loading::after { 30 | content: ''; 31 | position: fixed; 32 | z-index: 1000; 33 | } 34 | 35 | .js .loading::before { 36 | top: 0; 37 | left: 0; 38 | width: 100%; 39 | height: 100%; 40 | background: var(--color-bg); 41 | } 42 | 43 | .js .loading::after { 44 | top: 50%; 45 | left: 50%; 46 | width: 60px; 47 | height: 60px; 48 | margin: -30px 0 0 -30px; 49 | border-radius: 50%; 50 | opacity: 0.4; 51 | background: var(--color-link); 52 | animation: loaderAnim 0.7s linear infinite alternate forwards; 53 | 54 | } 55 | 56 | @keyframes loaderAnim { 57 | to { 58 | opacity: 1; 59 | transform: scale3d(0.5,0.5,1); 60 | } 61 | } 62 | 63 | a { 64 | text-decoration: none; 65 | color: var(--color-link); 66 | outline: none; 67 | cursor: pointer; 68 | } 69 | 70 | a:hover { 71 | color: var(--color-link-hover); 72 | outline: none; 73 | } 74 | 75 | /* Better focus styles from https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible */ 76 | a:focus { 77 | /* Provide a fallback style for browsers 78 | that don't support :focus-visible */ 79 | outline: none; 80 | background: lightgrey; 81 | } 82 | 83 | a:focus:not(:focus-visible) { 84 | /* Remove the focus indicator on mouse-focus for browsers 85 | that do support :focus-visible */ 86 | background: transparent; 87 | } 88 | 89 | a:focus-visible { 90 | /* Draw a very noticeable focus style for 91 | keyboard-focus on browsers that do support 92 | :focus-visible */ 93 | outline: 2px solid red; 94 | background: transparent; 95 | } 96 | 97 | .unbutton { 98 | background: none; 99 | border: 0; 100 | padding: 0; 101 | margin: 0; 102 | font: inherit; 103 | cursor: pointer; 104 | } 105 | 106 | .unbutton:focus { 107 | outline: none; 108 | } 109 | 110 | .frame { 111 | z-index: 1000; 112 | position: relative; 113 | width: 100%; 114 | display: grid; 115 | grid-template-columns: 100%; 116 | grid-template-areas: 'title' 'back' 'prev' 'sponsor'; 117 | grid-gap: 1rem; 118 | justify-items: start; 119 | align-self: start; 120 | justify-self: start; 121 | pointer-events: none; 122 | align-items: center; 123 | padding: 2rem; 124 | } 125 | 126 | body #cdawrap { 127 | justify-self: start; 128 | } 129 | 130 | .frame a { 131 | pointer-events: auto; 132 | } 133 | 134 | .frame__title { 135 | grid-area: title; 136 | font-size: 1.25rem; 137 | margin: 0; 138 | font-weight: inherit; 139 | } 140 | 141 | .frame__back { 142 | grid-area: back; 143 | } 144 | 145 | .frame__prev { 146 | grid-area: prev; 147 | } 148 | 149 | .frame__demos { 150 | grid-area: demos; 151 | display: flex; 152 | gap: 1rem; 153 | } 154 | 155 | .title { 156 | font-weight: 400; 157 | font-size: clamp(2rem,8.5vw,8rem); 158 | text-align: center; 159 | margin: 14vh auto 5vh; 160 | display: flex; 161 | justify-content: center; 162 | width: min-content; 163 | border-bottom: 1px solid var(--color-border); 164 | } 165 | 166 | .title span::first-letter { 167 | opacity: 0.5; 168 | } 169 | 170 | .subtitle { 171 | text-align: center; 172 | } 173 | 174 | .card-grid { 175 | display: grid; 176 | margin: 10vh 0; 177 | grid-template-columns: 1fr; 178 | border-top: 1px solid var(--color-border); 179 | } 180 | 181 | .card:nth-child(3n - 1) { 182 | border-left: 1px solid var(--color-border); 183 | border-right: 1px solid var(--color-border); 184 | } 185 | 186 | .card { 187 | display: grid; 188 | grid-template-rows: auto 1fr auto; 189 | cursor: pointer; 190 | position: relative; 191 | min-height: 60vh; 192 | padding: 4vw; 193 | overflow: hidden; 194 | border-bottom: 1px solid var(--color-border); 195 | } 196 | 197 | .card__img, 198 | .card__img-wrap, 199 | .card__img-inner { 200 | position: absolute; 201 | top: 0; 202 | left: 0; 203 | width: 100%; 204 | height: 100%; 205 | } 206 | 207 | .card__img, 208 | .card__img-inner { 209 | background-size: cover; 210 | background-position: 50% 50%; 211 | } 212 | 213 | .card__img, 214 | .card__img-wrap { 215 | overflow: hidden; 216 | } 217 | 218 | .card__img { 219 | z-index: -1; 220 | pointer-events: none; 221 | --columns: 0; 222 | --rows: 0; 223 | } 224 | 225 | .js .card__img { 226 | opacity: 0; 227 | background-image: none !important; 228 | } 229 | 230 | .card__img-inner { 231 | filter: brightness(0.6); 232 | width: calc(100% + (var(--columns) - 1) * 1px); 233 | height: calc(100% + (var(--rows) - 1) * 1px); 234 | } 235 | 236 | .card__date { 237 | display: flex; 238 | align-content: center; 239 | align-items: center; 240 | line-height: 1; 241 | position: relative; 242 | } 243 | 244 | .card__date::before { 245 | content: ''; 246 | width: 15px; 247 | height: 15px; 248 | border: 1px solid var(--color-link); 249 | background: var(--color-bg-date); 250 | margin: 0 10px 4px 0; 251 | } 252 | 253 | .card__title { 254 | font-weight: 400; 255 | font-size: clamp(1.5rem,5vw,2.5rem); 256 | } 257 | 258 | .card__link { 259 | position: relative; 260 | } 261 | 262 | .card__link::before { 263 | content: '+'; 264 | margin-right: 10px; 265 | } 266 | 267 | @media screen and (min-width: 33em) { 268 | .grid { 269 | grid-template-columns: repeat(2,1fr); 270 | } 271 | } 272 | 273 | @media screen and (min-width: 60em) { 274 | .frame { 275 | grid-template-columns: auto 1fr 1fr; 276 | grid-template-rows: auto auto auto; 277 | grid-template-areas: 'title title sponsor' 'back prev ...'; 278 | align-content: space-between; 279 | justify-items: start; 280 | grid-row-gap: 1.5rem; 281 | } 282 | .card-grid { 283 | grid-template-columns: repeat(3,1fr); 284 | } 285 | body #cdawrap { 286 | justify-self: end; 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /css/splitting-cells.css: -------------------------------------------------------------------------------- 1 | .splitting.cells img { width: 100%; display: block; } 2 | 3 | @supports ( display: grid ) { 4 | .splitting.cells { 5 | position: relative; 6 | overflow: hidden; 7 | background-size: cover; 8 | visibility: hidden; 9 | } 10 | 11 | .splitting .cell-grid { 12 | background: inherit; 13 | position: absolute; 14 | top: 0; 15 | left: 0; 16 | width: 100%; 17 | height: 100%; 18 | display: grid; 19 | grid-template: repeat( var(--row-total), 1fr ) / repeat( var(--col-total), 1fr ); 20 | } 21 | 22 | .splitting .cell { 23 | background: inherit; 24 | position: relative; 25 | overflow: hidden; 26 | } 27 | 28 | .splitting .cell-inner { 29 | background: inherit; 30 | position: absolute; 31 | visibility: visible; 32 | /* Size to fit the whole container size */ 33 | width: calc(100% * var(--col-total)); 34 | height: calc(100% * var(--row-total)); 35 | /* Position properly */ 36 | left: calc(-100% * var(--col-index)); 37 | top: calc(-100% * var(--row-index)); 38 | } 39 | 40 | /* Helper variables for advanced effects */ 41 | .splitting .cell { 42 | --center-x: calc((var(--col-total) - 1) / 2); 43 | --center-y: calc((var(--row-total) - 1) / 2); 44 | 45 | /* Offset from center, positive & negative */ 46 | --offset-x: calc(var(--col-index) - var(--center-x)); 47 | --offset-y: calc(var(--row-index) - var(--center-y)); 48 | 49 | /* Absolute distance from center, only positive */ 50 | --distance-x: calc( (var(--offset-x) * var(--offset-x)) / var(--center-x) ); 51 | 52 | /* Absolute distance from center, only positive */ 53 | --distance-y: calc( (var(--offset-y) * var(--offset-y)) / var(--center-y) ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /css/splitting.css: -------------------------------------------------------------------------------- 1 | /* Recommended styles for Splitting */ 2 | .splitting .word, 3 | .splitting .char { 4 | display: inline-block; 5 | } 6 | 7 | /* Psuedo-element chars */ 8 | .splitting .char { 9 | position: relative; 10 | } 11 | 12 | /** 13 | * Populate the psuedo elements with the character to allow for expanded effects 14 | * Set to `display: none` by default; just add `display: block` when you want 15 | * to use the psuedo elements 16 | */ 17 | .splitting .char::before, 18 | .splitting .char::after { 19 | content: attr(data-char); 20 | position: absolute; 21 | top: 0; 22 | left: 0; 23 | visibility: hidden; 24 | transition: inherit; 25 | user-select: none; 26 | } 27 | 28 | /* Expanded CSS Variables */ 29 | 30 | .splitting { 31 | /* The center word index */ 32 | --word-center: calc((var(--word-total) - 1) / 2); 33 | 34 | /* The center character index */ 35 | --char-center: calc((var(--char-total) - 1) / 2); 36 | 37 | /* The center character index */ 38 | --line-center: calc((var(--line-total) - 1) / 2); 39 | } 40 | 41 | .splitting .word { 42 | /* Pecent (0-1) of the word's position */ 43 | --word-percent: calc(var(--word-index) / var(--word-total)); 44 | 45 | /* Pecent (0-1) of the line's position */ 46 | --line-percent: calc(var(--line-index) / var(--line-total)); 47 | } 48 | 49 | .splitting .char { 50 | /* Percent (0-1) of the char's position */ 51 | --char-percent: calc(var(--char-index) / var(--char-total)); 52 | 53 | /* Offset from center, positive & negative */ 54 | --char-offset: calc(var(--char-index) - var(--char-center)); 55 | 56 | /* Absolute distance from center, only positive */ 57 | --distance: calc( 58 | (var(--char-offset) * var(--char-offset)) / var(--char-center) 59 | ); 60 | 61 | /* Distance from center where -1 is the far left, 0 is center, 1 is far right */ 62 | --distance-sine: calc(var(--char-offset) / var(--char-center)); 63 | 64 | /* Distance from center where 1 is far left/far right, 0 is center */ 65 | --distance-percent: calc((var(--distance) / var(--char-center))); 66 | } -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/favicon.ico -------------------------------------------------------------------------------- /img/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img1.jpg -------------------------------------------------------------------------------- /img/img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img2.jpg -------------------------------------------------------------------------------- /img/img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img3.jpg -------------------------------------------------------------------------------- /img/img4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img4.jpg -------------------------------------------------------------------------------- /img/img5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img5.jpg -------------------------------------------------------------------------------- /img/img6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img6.jpg -------------------------------------------------------------------------------- /img/img7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img7.jpg -------------------------------------------------------------------------------- /img/img8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img8.jpg -------------------------------------------------------------------------------- /img/img9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codrops/ClipHoverEffect/57b5cc1b6cc375e69dd915bdd307f2645e1dabb7/img/img9.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Clip-path Hover Effect from Quai Network | Codrops 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |

Clip-path Hover Effect from Quai Network

22 | //Read the tutorial 23 | //Previous demo 24 |
25 |
Station Updates
26 |

++ Each row of cards has a different image slice configuration ++

27 |
28 |
29 |
30 | 02/18/2074 31 |

Code CR-4519: Anomaly Detection in Array

32 | Read the article 33 |
34 | 35 |
36 |
37 | 02/20/2074 38 |

Case Log 3X-782: Malfunction Analysis of Drone Units

39 | Read the article 40 |
41 | 42 |
43 |
44 | 03/17/2074 45 |

Code CR-3037: Data Flow Optimization for Transmission

46 | Read the article 47 |
48 | 49 |
50 |
51 | 05/05/2074 52 |

Code CR-9892: Investigation of Neural Network Anomalies

53 | Read the article 54 |
55 | 56 |
57 |
58 | 06/22/2074 59 |

Maintenance Report 4B-678: Robotic Limb Performance Enhancement

60 | Read the article 61 |
62 | 63 |
64 |
65 | 07/02/2074 66 |

Maintenance Report 9A-814: Diagnostics and Repair of Com Link 3C-12

67 | Read the article 68 |
69 | 70 |
71 |
72 | 08/14/2074 73 |

Maintenance Report 7Y-246: Optimal Efficiency of ES Cells

74 | Read the article 75 |
76 | 77 |
78 |
79 | 09/30/2074 80 |

Case Log 2Z-115: Cybersecurity Breach Detection

81 | Read the article 82 |
83 | 84 |
85 |
86 | 12/08/2074 87 |

Case Log 6W-421: Resolving Power Surge Incidents in Subsystems

88 | Read the article 89 |
90 | 91 |
92 |
93 | 01/01/2075 94 |

Incidence Report 2X-112: Station Uproar

95 | Read the article 96 |
97 | 98 |
99 |
100 | 01/26/2075 101 |

Case Log 7S-336: Malfunction Analysis of Harvest Units

102 | Read the article 103 |
104 | 105 |
106 |
107 | 12/08/2074 108 |

Case Log 2M-446: Solar Relay Optimization

109 | Read the article 110 |
111 | 112 |
113 |
114 |

Clip-path Hover Effect from Quai Network

115 | //Read the tutorial 116 | //Previous demo 117 |
118 |
119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /js/card.js: -------------------------------------------------------------------------------- 1 | import { lettersAndSymbols } from './utils.js'; 2 | 3 | // Class representing a Card 4 | export class Card { 5 | // Initialize DOM and style related properties 6 | DOM = { 7 | // main DOM element 8 | el: null, 9 | // .card__img element 10 | img: null, 11 | // .card__img-wrap element (dynamically created in the layout function) 12 | imgWrap: null, 13 | // .card__img-inner "slice" elements (dynamically created in the layout function) 14 | slices: null, 15 | // .card__date element 16 | date: null, 17 | // .card__title element 18 | title: null, 19 | // .card__link element 20 | link: null, 21 | }; 22 | // Card image url 23 | imageURL; 24 | // Settings 25 | settings = { 26 | // vertical || horizontal alignment 27 | orientation: 'vertical', 28 | // Total number of slices for the inner images (clip paths) 29 | slicesTotal: 5, 30 | // Animation values 31 | animation: { 32 | duration: 0.5, 33 | ease: 'power3.inOut' 34 | } 35 | }; 36 | 37 | /** 38 | * Sets up the necessary elements, data, and event listeners for a Card instance. 39 | * @param {HTMLElement} DOM_el - The DOM element that represents the card. 40 | * @param {Object} options - The options for customizing the card. These options will override the default settings. 41 | */ 42 | constructor(DOM_el, options) { 43 | // Merge settings and options. 44 | this.settings = Object.assign({}, this.settings, options); 45 | 46 | this.DOM.el = DOM_el; 47 | this.DOM.img = this.DOM.el.querySelector('.card__img'); 48 | this.DOM.date = this.DOM.el.querySelector('.card__date'); 49 | this.DOM.title = this.DOM.el.querySelector('.card__title'); 50 | this.DOM.link = this.DOM.el.querySelector('.card__link'); 51 | 52 | // Splitting chars for date, title and link 53 | this.chars = { 54 | date: [...this.DOM.date.querySelectorAll('.char')], 55 | title: [...this.DOM.title.querySelectorAll('.char')], 56 | link: [...this.DOM.link.querySelectorAll('.char')] 57 | }; 58 | 59 | // Save those initial char values 60 | [...this.chars.date, ...this.chars.title, ...this.chars.link].forEach(char => { 61 | char.dataset.initial = char.innerHTML; 62 | }); 63 | 64 | // Extracts the image URL from the style of the DOM image element 65 | this.imageURL = this.DOM.img.getAttribute('style').match(/url\((['"])?(.*?)\1\)/)[2]; 66 | 67 | // Calls the `layout` function to create the necessary structure for the card 68 | this.layout(); 69 | 70 | // Initialize the events 71 | this.initEvents(); 72 | } 73 | 74 | /** 75 | * Modifies the layout of the card image, slicing it into multiple sections. 76 | * 77 | * This function creates a structure with multiple image slices within the .card__img element. 78 | * The number of slices is determined by the `slicesTotal` setting. 79 | * Each slice is an element with the class .card__img-inner and contains the same background image as the .card__img. 80 | * These slices are placed within a wrapper element .card__img-wrap, which is appended to the .card__img. 81 | * 82 | * After creating the slices, the function sets the appropriate CSS variable to match `slicesTotal` (either --columns or --rows). 83 | * Finally, it calls `setClipPath` to apply a clip-path to each slice. 84 | */ 85 | layout() { 86 | /** 87 | * Create the following structure for the .card__img element with X slices (card__img-inner) 88 | * 89 | *
90 | *
91 | *
92 | *
93 | *
94 | *
95 | *
96 | *
97 | */ 98 | 99 | this.DOM.imgWrap = document.createElement('div'); 100 | this.DOM.imgWrap.classList = 'card__img-wrap'; 101 | let slicesStr = ''; 102 | for (let i = 0; i < this.settings.slicesTotal; ++i) { 103 | slicesStr += `
`; 104 | } 105 | this.DOM.imgWrap.innerHTML = slicesStr; 106 | this.DOM.slices = this.DOM.imgWrap.querySelectorAll('.card__img-inner'); 107 | // append the new wrap element to the card img element 108 | this.DOM.img.appendChild(this.DOM.imgWrap); 109 | // Set the --columns or --rows CSS variable value to be the same as the settings.slicesTotal 110 | this.DOM.img.style.setProperty(this.settings.orientation === 'vertical' ? '--columns' : '--rows', this.settings.slicesTotal); 111 | 112 | // Set the clip paths of each slice 113 | this.setClipPath(); 114 | } 115 | 116 | /** 117 | * Sets a clip-path CSS property for each slice in this object, creating a slicing effect. 118 | * 119 | * The function divides each slice along either the vertical or horizontal axis depending on 120 | * the `orientation` setting. The slicing is based on the `slicesTotal` setting. 121 | * It also offsets each slice slightly to solve gap issues. 122 | */ 123 | setClipPath() { 124 | this.DOM.slices.forEach((slice, position) => { 125 | let a1 = position*100/this.settings.slicesTotal; 126 | let b1 = position*100/this.settings.slicesTotal + 100/this.settings.slicesTotal; 127 | 128 | gsap.set(slice, { 129 | clipPath: this.settings.orientation === 'vertical' ? 130 | `polygon(${a1}% 0%, ${b1}% 0%, ${b1}% 100%, ${a1}% 100%)` : 131 | `polygon(0% ${a1}%, 100% ${a1}%, 100% ${b1}%, 0% ${b1}%)` 132 | }); 133 | const isVertical = this.settings.orientation === 'vertical'; 134 | gsap.set(slice, { [isVertical ? 'left' : 'top']: position*-1 }); 135 | }); 136 | } 137 | 138 | /** 139 | * Initializes event listeners for mouseenter and mouseleave events. 140 | */ 141 | initEvents() { 142 | this.onMouseenterFn = () => this.mouseEnter(); 143 | this.onMouseleaveFn = () => this.mouseLeave(); 144 | 145 | this.DOM.el.addEventListener('mouseenter', this.onMouseenterFn); 146 | this.DOM.el.addEventListener('mouseleave', this.onMouseleaveFn); 147 | } 148 | 149 | /** 150 | * Triggers when the mouse enters the element. 151 | * 152 | * This function shuffles the characters of the date, title, and link properties of this object, 153 | * and then animates the image and image wrapper elements from an offset position into view. 154 | * The direction of the animation (vertical or horizontal) depends on the `orientation` setting. 155 | * The slices of the image also animate from a random offset position into view. 156 | */ 157 | mouseEnter() { 158 | const isVertical = this.settings.orientation === 'vertical'; 159 | 160 | this.shuffleChars(this.chars.date); 161 | this.shuffleChars(this.chars.title); 162 | this.shuffleChars(this.chars.link); 163 | 164 | gsap 165 | .timeline({ 166 | defaults: { 167 | duration: this.settings.animation.duration, 168 | ease: this.settings.animation.ease 169 | } 170 | }) 171 | .addLabel('start', 0) 172 | .fromTo(this.DOM.img, { 173 | [isVertical ? 'yPercent' : 'xPercent']: 100, 174 | opacity: 0 175 | }, { 176 | [isVertical ? 'yPercent' : 'xPercent']: 0, 177 | opacity: 1 178 | }, 'start') 179 | .fromTo(this.DOM.imgWrap, { 180 | [isVertical ? 'yPercent' : 'xPercent']: -100 181 | }, { 182 | [isVertical ? 'yPercent' : 'xPercent']: 0 183 | }, 'start') 184 | .fromTo(this.DOM.slices, { 185 | [isVertical ? 'yPercent' : 'xPercent']: pos => pos % 2 ? gsap.utils.random(-75, -25) : gsap.utils.random(25, 75) 186 | }, { 187 | [isVertical ? 'yPercent' : 'xPercent']: 0 188 | }, 'start'); 189 | } 190 | 191 | /** 192 | * Triggers when the mouse leaves the element. 193 | * 194 | * This function animates the image, the image wrapper, and the slices out of view. 195 | * The direction of the animation (vertical or horizontal) depends on the `orientation` setting. 196 | */ 197 | mouseLeave() { 198 | const isVertical = this.settings.orientation === 'vertical'; 199 | 200 | gsap 201 | .timeline({ 202 | defaults: { 203 | duration: this.settings.animation.duration, 204 | ease: this.settings.animation.ease 205 | } 206 | }) 207 | .addLabel('start', 0) 208 | .to(this.DOM.img, { 209 | [isVertical ? 'yPercent' : 'xPercent']: 100, 210 | opacity: 0 211 | }, 'start') 212 | .to(this.DOM.imgWrap, { 213 | [isVertical ? 'yPercent' : 'xPercent']: -100 214 | }, 'start') 215 | .to(this.DOM.slices, { 216 | [isVertical ? 'yPercent' : 'xPercent']: pos => pos % 2 ? gsap.utils.random(-75, 25) : gsap.utils.random(25, 75) 217 | }, 'start') 218 | } 219 | 220 | /** 221 | * Shuffles the inner HTML of each character in the array, randomizing it 3 times 222 | * and eventually setting it back to its initial value. 223 | * 224 | * @param {Element[]} arr - An array of DOM elements representing individual characters. 225 | * Each element must have a 'data-initial' attribute that stores its initial value. 226 | */ 227 | shuffleChars(arr) { 228 | arr.forEach((char, position) => { 229 | gsap.killTweensOf(char); 230 | gsap.fromTo(char, { 231 | opacity: 0 232 | }, { 233 | duration: 0.03, 234 | innerHTML: () => lettersAndSymbols[Math.floor(Math.random() * lettersAndSymbols.length)], 235 | repeat: 3, 236 | repeatRefresh: true, 237 | opacity: 1, 238 | repeatDelay: 0.05, 239 | onComplete: () => gsap.set(char, {innerHTML: char.dataset.initial, delay: 0.03}), 240 | }) 241 | }); 242 | } 243 | } -------------------------------------------------------------------------------- /js/gsap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * GSAP 3.11.5 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))&&Pe}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 qt(t[r],e)))||t.splice(r,1);return t}function fa(t){return t._gsap||ea(Mt(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&&++ia;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function ya(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function za(t,e){!t.parent||e&&!t.parent.autoRemoveChildren||t.parent.remove(t),t._act=0}function Aa(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Ca(t,e,r,i){return t._startAt&&(B?t._startAt.revert(ht):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ea(t){return t._repeat?Tt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ga(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ha(t){return t._end=ja(t._start+(t._tDur/Math.abs(t._ts||t._rts||X)||0))}function Ia(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=ja(r._time-(0X)&&e.render(r,!0)),Aa(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur(n=Math.abs(n))&&(a=i,o=n);return a}function tb(t){return za(t),t.scrollTrigger&&t.scrollTrigger.kill(!!B),t.progress()<1&&St(t,"onInterrupt"),t}function wb(t){if(x()){var e=(t=!t.name&&t.default||t).name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:T,render:fe,add:Qt,kill:_e,modifier:pe,rawVars:0},a={targetTest:0,get:0,getSetter:re,aliases:{},register:0};if(Ft(),t!==i){if(pt[e])return;qa(i,qa(ua(t,n),a)),yt(i.prototype,yt(n,ua(t,a))),pt[i.prop=e]=i,t.targetTest&&(gt.push(i),ft[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}S(e,i),t.register&&t.register(Pe,i,ge)}else Ct.push(t)}function zb(t,e,r){return(6*(t+=t<0?1:1>16,e>>8&Pt,e&Pt]:0:Dt.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),Dt[e])p=Dt[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&Pt,p&Pt,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&Pt,e&Pt]}else if("hsl"===e.substr(0,3))if(p=d=e.match(tt),r){if(~e.indexOf("="))return p=e.match(et),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3=U?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*(Z(i)?i[0]:i).totalDuration()),1=r&&te)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0=i&&(a instanceof Jt?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(ya(this,t),t===this._recent&&(this._recent=this._last),Aa(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=ja(Rt.time-(0r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=xt(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=Jt.to(i,qa({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||X,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ra(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,qa({startAt:{time:xt(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),rb(this,xt(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+X)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);for(var i,n=this._first,a=this.labels;n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Aa(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Aa(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=U;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Ka(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=r/a._ts,a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ra(a,a===L&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(L._ts&&(na(L,Ga(t,L)),f=Rt.frame),Rt.frame>=mt){mt+=V.autoSleep||120;var e=L._first;if((!e||!e._ts)&&V.autoSleep&&Rt._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||Rt.sleep()}}},Timeline}(Ut);qa(Xt.prototype,{_lock:0,_hasPause:0,_forcing:0});function ac(t,e,i,n,a,o){var u,h,l,f;if(pt[t]&&!1!==(u=new pt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Gt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||Z(t)||J(t))return r(t)?Gt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Gt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new ge(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==c))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function gc(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if(Z(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Nt,Wt,Qt=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var c,d=t[e],p="get"!==i?i:s(d)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():d,_=s(d)?l?ee:te:Zt;if(r(n)&&(~n.indexOf("random(")&&(n=ob(n)),"="===n.charAt(1)&&(!(c=ka(p,n)+(Ya(p)||0))&&0!==c||(n=c))),!f||p!==n||Wt)return isNaN(p*n)||""===n?(d||e in t||Q(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,c,d,p,_=new ge(this._pt,t,e,0,1,le,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(d=~(i+="").indexOf("random("))&&(i=ob(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(it)||[];o=it.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(c=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:c,c:"="===l.charAt(1)?ka(c,l)-c:parseFloat(l)-c,m:h&&h<4?Math.round:0},m=it.lastIndex);return _.c=m")}),s.duration();else{for(l in u={},x)"ease"===l||"easeEach"===l||gc(l,x[l],u,x.easeEach);for(l in u)for(C=u[l].sort(function(t,e){return t.t-e.t}),o=E=0;o=t._tDur||e<0)&&t.ratio===u&&(u&&za(t,1),r||B||(St(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),z.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i){d||Rt.wake(),this._ts||this.play();var n,a=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||Kt(this,a),n=this._ease(a/this._dur),function _updatePropTweens(t,e,r,i,n,a,s){var o,u,h,l,f=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!f)for(f=t._ptCache[e]=[],h=t._ptLookup,l=t._targets.length;l--;){if((o=h[l][e])&&o.d&&o.d._pt)for(o=o.d._pt;o&&o.p!==e&&o.fp!==e;)o=o._next;if(!o)return Wt=1,t.vars[e]="+=0",Kt(t,s),Wt=0,1;f.push(o)}for(l=f.length;l--;)(o=(u=f[l])._pt||u).s=!i&&0!==i||n?o.s+(i||0)+a*o.c:i,o.c=r-o.s,u.e&&(u.e=ia(r)+Ya(u.e)),u.b&&(u.b=o.s+Ya(u.b))}(this,t,e,r,i,n,a)?this.resetTo(t,e,r,i):(Ia(this,0),this.parent||xa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?tb(this):this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Nt&&!0!==Nt.vars.overwrite)._first||tb(this),this.parent&&i!==this.timeline.totalDuration()&&Ra(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,c=t?Mt(t):f,d=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,c))return"all"===e&&(this._pt=0),tb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ha(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?fa(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=yt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~c.indexOf(f[l]))for(u in a=d[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||ya(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&tb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Va(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Va(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return L.killTweensOf(t,e,r)},Tween}(Ut);qa(Jt.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ha("staggerTo,staggerFrom,staggerFromTo",function(r){Jt[r]=function(){var t=new Xt,e=kt.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function oc(t,e,r){return t.setAttribute(e,r)}function wc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var Zt=function _setterPlain(t,e,r){return t[e]=r},te=function _setterFunc(t,e,r){return t[e](r)},ee=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},re=function _getSetter(t,e){return s(t[e])?te:u(t[e])&&t.setAttribute?oc:Zt},se=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},oe=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},le=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},fe=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},pe=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},_e=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?ya(this,i,"_pt"):i.dep||(e=1),i=r;return!e},me=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},ge=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=wc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||se,this.d=s||this,this.set=o||Zt,this.pr=u||0,(this._next=t)&&(t._prev=this)}ha(vt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger",function(t){return ft[t]=1}),ot.TweenMax=ot.TweenLite=Jt,ot.TimelineLite=ot.TimelineMax=Xt,L=new Xt({sortChildren:!1,defaults:q,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),V.stringFilter=Fb;function Dc(t){return(be[t]||xe).map(function(t){return t()})}function Ec(){var t=Date.now(),o=[];2{setTimeout((()=>{this.progress(t,e,i)}))};this.images.forEach((function(e){e.once("progress",t),e.check()}))},n.prototype.progress=function(t,e,i){this.progressedCount++,this.hasAnyBroken=this.hasAnyBroken||!t.isLoaded,this.emitEvent("progress",[this,t,e]),this.jqDeferred&&this.jqDeferred.notify&&this.jqDeferred.notify(this,t),this.progressedCount===this.images.length&&this.complete(),this.options.debug&&s&&s.log(`progress: ${i}`,t,e)},n.prototype.complete=function(){let t=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emitEvent(t,[this]),this.emitEvent("always",[this]),this.jqDeferred){let t=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[t](this)}},h.prototype=Object.create(e.prototype),h.prototype.check=function(){this.getIsImageComplete()?this.confirm(0!==this.img.naturalWidth,"naturalWidth"):(this.proxyImage=new Image,this.img.crossOrigin&&(this.proxyImage.crossOrigin=this.img.crossOrigin),this.proxyImage.addEventListener("load",this),this.proxyImage.addEventListener("error",this),this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.proxyImage.src=this.img.currentSrc||this.img.src)},h.prototype.getIsImageComplete=function(){return this.img.complete&&this.img.naturalWidth},h.prototype.confirm=function(t,e){this.isLoaded=t;let{parentNode:i}=this.img,s="PICTURE"===i.nodeName?i:this.img;this.emitEvent("progress",[this,s,e])},h.prototype.handleEvent=function(t){let e="on"+t.type;this[e]&&this[e](t)},h.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindEvents()},h.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindEvents()},h.prototype.unbindEvents=function(){this.proxyImage.removeEventListener("load",this),this.proxyImage.removeEventListener("error",this),this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype=Object.create(h.prototype),d.prototype.check=function(){this.img.addEventListener("load",this),this.img.addEventListener("error",this),this.img.src=this.url,this.getIsImageComplete()&&(this.confirm(0!==this.img.naturalWidth,"naturalWidth"),this.unbindEvents())},d.prototype.unbindEvents=function(){this.img.removeEventListener("load",this),this.img.removeEventListener("error",this)},d.prototype.confirm=function(t,e){this.isLoaded=t,this.emitEvent("progress",[this,this.element,e])},n.makeJQueryPlugin=function(e){(e=e||t.jQuery)&&(i=e,i.fn.imagesLoaded=function(t,e){return new n(this,t,e).jqDeferred.promise(i(this))})},n.makeJQueryPlugin(),n})); -------------------------------------------------------------------------------- /js/index.js: -------------------------------------------------------------------------------- 1 | // Importing the necessary helper function to preload images from 'utils.js' file 2 | import { preloadImages } from './utils.js'; 3 | 4 | // Importing the Card class from 'card.js' file 5 | import { Card } from './card.js'; 6 | 7 | // Calling the Splitting function to split the text into individual characters, 8 | // it's a text animation library which splits the text for complex animations. 9 | Splitting(); 10 | 11 | // An array to hold different configurations/settings for different sets of cards. 12 | // Each setting object includes the orientation, number of slices, and animation details. 13 | const settingsArray = [ 14 | // 1st set of settings: Vertical orientation with 5 slices. 15 | { 16 | orientation: 'vertical', 17 | slicesTotal: 5 18 | }, 19 | // 2nd set of settings: Vertical orientation with 15 slices. 20 | { 21 | orientation: 'vertical', 22 | slicesTotal: 15 23 | }, 24 | // 3rd set of settings: Horizontal orientation with 5 slices and specific animation duration and easing. 25 | { 26 | orientation: 'horizontal', 27 | slicesTotal: 5, 28 | animation: { 29 | duration: 0.6, 30 | ease: 'expo.inOut' 31 | } 32 | }, 33 | // 4th set of settings: Horizontal orientation with 15 slices and specific animation duration and easing. 34 | { 35 | orientation: 'horizontal', 36 | slicesTotal: 15, 37 | animation: { 38 | duration: 0.6, 39 | ease: 'expo.inOut' 40 | } 41 | }, 42 | ]; 43 | 44 | // Initialize the Cards 45 | [...document.querySelectorAll('.card')].forEach((cardEl, index) => { 46 | new Card(cardEl, settingsArray[Math.floor(index / 3) % settingsArray.length]); 47 | }); 48 | 49 | // Preload images, then remove loader (loading class) from body 50 | preloadImages('.canvas-wrap').then(() => document.body.classList.remove('loading')); -------------------------------------------------------------------------------- /js/splitting.min.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):n.Splitting=t()}(this,function(){"use strict" 2 | var u=document,l=u.createTextNode.bind(u) 3 | function d(n,t,e){n.style.setProperty(t,e)}function f(n,t){return n.appendChild(t)}function p(n,t,e,r){var i=u.createElement("span") 4 | return t&&(i.className=t),e&&(!r&&i.setAttribute("data-"+t,e),i.textContent=e),n&&f(n,i)||i}function h(n,t){return n.getAttribute("data-"+t)}function m(n,t){return n&&0!=n.length?n.nodeName?[n]:[].slice.call(n[0].nodeName?n:(t||u).querySelectorAll(n)):[]}function o(n){for(var t=[];n--;)t[n]=[] 5 | return t}function g(n,t){n&&n.some(t)}function c(t){return function(n){return t[n]}}var a={} 6 | function n(n,t,e,r){return{by:n,depends:t,key:e,split:r}}function e(n){return function t(e,n,r){var i=r.indexOf(e) 7 | if(-1==i)r.unshift(e),g(a[e].depends,function(n){t(n,e,r)}) 8 | else{var u=r.indexOf(n) 9 | r.splice(i,1),r.splice(u,0,e)}return r}(n,0,[]).map(c(a))}function t(n){a[n.by]=n}function v(n,r,i,u,o){n.normalize() 10 | var c=[],a=document.createDocumentFragment() 11 | u&&c.push(n.previousSibling) 12 | var s=[] 13 | return m(n.childNodes).some(function(n){if(!n.tagName||n.hasChildNodes()){if(n.childNodes&&n.childNodes.length)return s.push(n),void c.push.apply(c,v(n,r,i,u,o)) 14 | var t=n.wholeText||"",e=t.trim() 15 | e.length&&(" "===t[0]&&s.push(l(" ")),g(e.split(i),function(n,t){t&&o&&s.push(p(a,"whitespace"," ",o)) 16 | var e=p(a,r,n) 17 | c.push(e),s.push(e)})," "===t[t.length-1]&&s.push(l(" ")))}else s.push(n)}),g(s,function(n){f(a,n)}),n.innerHTML="",f(n,a),c}var s=0 18 | var i="words",r=n(i,s,"word",function(n){return v(n,"word",/\s+/,0,1)}),y="chars",w=n(y,[i],"char",function(n,e,t){var r=[] 19 | return g(t[i],function(n,t){r.push.apply(r,v(n,"char","",e.whitespace&&t))}),r}) 20 | function b(t){var f=(t=t||{}).key 21 | return m(t.target||"[data-splitting]").map(function(a){var s=a["🍌"] 22 | if(!t.force&&s)return s 23 | s=a["🍌"]={el:a} 24 | var n=e(t.by||h(a,"splitting")||y),l=function(n,t){for(var e in t)n[e]=t[e] 25 | return n}({},t) 26 | return g(n,function(n){if(n.split){var t=n.by,e=(f?"-"+f:"")+n.key,r=n.split(a,l,s) 27 | e&&(i=a,c=(o="--"+e)+"-index",g(u=r,function(n,t){Array.isArray(n)?g(n,function(n){d(n,c,t)}):d(n,c,t)}),d(i,o+"-total",u.length)),s[t]=r,a.classList.add(t)}var i,u,o,c}),a.classList.add("splitting"),s})}function N(n,t,e){var r=m(t.matching||n.children,n),i={} 28 | return g(r,function(n){var t=Math.round(n[e]);(i[t]||(i[t]=[])).push(n)}),Object.keys(i).map(Number).sort(x).map(c(i))}function x(n,t){return n-t}b.html=function(n){var t=(n=n||{}).target=p() 29 | return t.innerHTML=n.content,b(n),t.outerHTML},b.add=t 30 | var T=n("lines",[i],"line",function(n,t,e){return N(n,{matching:e[i]},"offsetTop")}),L=n("items",s,"item",function(n,t){return m(t.matching||n.children,n)}),k=n("rows",s,"row",function(n,t){return N(n,t,"offsetTop")}),A=n("cols",s,"col",function(n,t){return N(n,t,"offsetLeft")}),C=n("grid",["rows","cols"]),M="layout",S=n(M,s,s,function(n,t){var e=t.rows=+(t.rows||h(n,"rows")||1),r=t.columns=+(t.columns||h(n,"columns")||1) 31 | if(t.image=t.image||h(n,"image")||n.currentSrc||n.src,t.image){var i=m("img",n)[0] 32 | t.image=i&&(i.currentSrc||i.src)}t.image&&d(n,"background-image","url("+t.image+")") 33 | for(var u=e*r,o=[],c=p(s,"cell-grid");u--;){var a=p(c,"cell") 34 | p(a,"cell-inner"),o.push(a)}return f(n,c),o}),H=n("cellRows",[M],"row",function(n,t,e){var r=t.rows,i=o(r) 35 | return g(e[M],function(n,t,e){i[Math.floor(t/(e.length/r))].push(n)}),i}),O=n("cellColumns",[M],"col",function(n,t,e){var r=t.columns,i=o(r) 36 | return g(e[M],function(n,t){i[t%r].push(n)}),i}),j=n("cells",["cellRows","cellColumns"],"cell",function(n,t,e){return e[M]}) 37 | return t(r),t(w),t(T),t(L),t(k),t(A),t(C),t(S),t(H),t(O),t(j),b}) -------------------------------------------------------------------------------- /js/utils.js: -------------------------------------------------------------------------------- 1 | const lettersAndSymbols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '!', '@', '#', '$', '%', '^', '&', '*', '-', '_', '+', '=', ';', ':', '<', '>', ',']; 2 | 3 | // Preload images 4 | const preloadImages = (selector = 'img') => { 5 | return new Promise((resolve) => { 6 | imagesLoaded(document.querySelectorAll(selector), {background: true}, resolve); 7 | }); 8 | }; 9 | 10 | export { 11 | lettersAndSymbols, 12 | preloadImages, 13 | }; --------------------------------------------------------------------------------