🧠 Description
This project is a visually engaging web page that displays travel destination cards with animation and interactivity.
It uses HTML, CSS, and GSAP (JavaScript animation library) to create a dynamic card transition effect with a navigation bar, details section, and progress pagination.
💻 HTML Code
1<!DOCTYPE html>
2<html lang="en">
3
4 <head>
5 <meta charset="UTF-8">
6 <title>CodewithLord - Timed Cards Opening</title>
7 <link rel="stylesheet" href="./style.css">
8
9 </head>
10
11 <body>
12 <body>
13 <div class="indicator"></div>
14
15 <nav>
16 <div>
17 <div class="svg-container">
18 <svg
19 xmlns="http://www.w3.org/2000/svg"
20 fill="none"
21 viewBox="0 0 24 24"
22 stroke-width="1.5"
23 stroke="currentColor"
24 >
25 <path
26 stroke-linecap="round"
27 stroke-linejoin="round"
28 d="M12 21a9.004 9.004 0 008.716-6.747M12 21a9.004 9.004 0 01-8.716-6.747M12 21c2.485 0 4.5-4.03 4.5-9S14.485 3 12 3m0 18c-2.485 0-4.5-4.03-4.5-9S9.515 3 12 3m0 0a8.997 8.997 0 017.843 4.582M12 3a8.997 8.997 0 00-7.843 4.582m15.686 0A11.953 11.953 0 0112 10.5c-2.998 0-5.74-1.1-7.843-2.918m15.686 0A8.959 8.959 0 0121 12c0 .778-.099 1.533-.284 2.253m0 0A17.919 17.919 0 0112 16.5c-3.162 0-6.133-.815-8.716-2.247m0 0A9.015 9.015 0 013 12c0-1.605.42-3.113 1.157-4.418"
29 />
30 </svg>
31 </div>
32 <div>CodewithLord</div>
33 </div>
34 <div>
35 <div class="active">Home</div>
36 <div>Holidays</div>
37 <div>Destinations</div>
38 <div>Flights</div>
39 <div>Offers</div>
40 <div>Contact</div>
41 <div class="svg-container">
42 <svg
43 xmlns="http://www.w3.org/2000/svg"
44 fill="none"
45 viewBox="0 0 24 24"
46 stroke-width="1.5"
47 stroke="currentColor"
48 >
49 <path
50 stroke-linecap="round"
51 stroke-linejoin="round"
52 d="M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z"
53 />
54 </svg>
55 </div>
56 <div class="svg-container">
57 <svg
58 xmlns="http://www.w3.org/2000/svg"
59 viewBox="0 0 24 24"
60 fill="currentColor"
61 >
62 <path
63 fill-rule="evenodd"
64 d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z"
65 clip-rule="evenodd"
66 />
67 </svg>
68 </div>
69 </div>
70 </nav>
71
72 <div id="demo"></div>
73
74 <div class="details" id="details-even">
75 <div class="place-box">
76 <div class="text">Switzerland Alps</div>
77 </div>
78 <div class="title-box-1"><div class="title-1">SAINT</div></div>
79 <div class="title-box-2"><div class="title-2">ANTONIEN</div></div>
80 <div class="desc">
81 Tucked away in the Switzerland Alps, Saint Antönien offers an idyllic retreat for those seeking tranquility and adventure alike. It's a hidden gem for backcountry skiing in winter and boasts lush trails for hiking and mountain biking during the warmer months.
82 </div>
83 <div class="cta">
84 <button class="bookmark">
85 <svg
86 xmlns="http://www.w3.org/2000/svg"
87 viewBox="0 0 24 24"
88 fill="currentColor"
89 >
90 <path
91 fill-rule="evenodd"
92 d="M6.32 2.577a49.255 49.255 0 0111.36 0c1.497.174 2.57 1.46 2.57 2.93V21a.75.75 0 01-1.085.67L12 18.089l-7.165 3.583A.75.75 0 013.75 21V5.507c0-1.47 1.073-2.756 2.57-2.93z"
93 clip-rule="evenodd"
94 />
95 </svg>
96 </button>
97 <button class="discover">Discover Location</button>
98 </div>
99 </div>
100
101 <div class="details" id="details-odd">
102 <div class="place-box">
103 <div class="text">Switzerland Alps</div>
104 </div>
105 <div class="title-box-1"><div class="title-1">SAINT </div></div>
106 <div class="title-box-2"><div class="title-2">ANTONIEN</div></div>
107 <div class="desc">
108 Tucked away in the Switzerland Alps, Saint Antönien offers an idyllic retreat for those seeking tranquility and adventure alike. It's a hidden gem for backcountry skiing in winter and boasts lush trails for hiking and mountain biking during the warmer months.
109 </div>
110 <div class="cta">
111 <button class="bookmark">
112 <svg
113 xmlns="http://www.w3.org/2000/svg"
114 viewBox="0 0 24 24"
115 fill="currentColor"
116 >
117 <path
118 fill-rule="evenodd"
119 d="M6.32 2.577a49.255 49.255 0 0111.36 0c1.497.174 2.57 1.46 2.57 2.93V21a.75.75 0 01-1.085.67L12 18.089l-7.165 3.583A.75.75 0 013.75 21V5.507c0-1.47 1.073-2.756 2.57-2.93z"
120 clip-rule="evenodd"
121 />
122 </svg>
123 </button>
124 <button class="discover">Discover Location</button>
125 </div>
126 </div>
127
128 <div class="pagination" id="pagination">
129 <div class="arrow arrow-left">
130 <svg
131 xmlns="http://www.w3.org/2000/svg"
132 fill="none"
133 viewBox="0 0 24 24"
134 stroke="currentColor"
135 >
136 <path
137 stroke-linecap="round"
138 stroke-linejoin="round"
139 d="M15.75 19.5L8.25 12l7.5-7.5"
140 />
141 </svg>
142 </div>
143 <div class="arrow arrow-right">
144 <svg
145 xmlns="http://www.w3.org/2000/svg"
146 fill="none"
147 viewBox="0 0 24 24"
148 stroke="currentColor"
149 >
150 <path
151 stroke-linecap="round"
152 stroke-linejoin="round"
153 d="M8.25 4.5l7.5 7.5-7.5 7.5"
154 />
155 </svg>
156 </div>
157 <div class="progress-sub-container" >
158 <div class="progress-sub-background" >
159 <div class="progress-sub-foreground" ></div>
160 </div>
161 </div>
162 <div class="slide-numbers" id="slide-numbers"></div>
163 </div>
164
165 <div class="cover" ></div>
166
167
168
169 </body>
170 <script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js'></script><script src="./script.js"></script>
171
172 </body>
173
174</html>
175
The HTML defines the layout of the page.
It includes:
A navbar with icons and links.
A main “details” section showing the travel card content.
A pagination section for slide transitions.
A cover element used for animated page entry effects.
The GSAP library is loaded for animations.
CSS Code
1@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Oswald:wght@500&display=swap"); 2body { 3 margin: 0; 4 background-color: #1a1a1a; 5 color: #FFFFFFDD; 6 position: relative; 7 overflow: hidden; 8 font-family: "Inter", sans-serif; 9} 10 11.card { 12 position: absolute; 13 left: 0; 14 top: 0; 15 background-position: center; 16 background-size: cover; 17 box-shadow: 6px 6px 10px 2px rgba(0, 0, 0, 0.6); 18} 19 20#btn { 21 position: absolute; 22 top: 690px; 23 left: 16px; 24 z-index: 99; 25} 26 27.card-content { 28 position: absolute; 29 left: 0; 30 top: 0; 31 color: #FFFFFFDD; 32 padding-left: 16px; 33} 34 35.content-place { 36 margin-top: 6px; 37 font-size: 13px; 38 font-weight: 500; 39} 40 41.content-place { 42 font-weight: 500; 43} 44 45.content-title-1, 46.content-title-2 { 47 font-weight: 600; 48 font-size: 20px; 49 font-family: "Oswald", sans-serif; 50} 51 52.content-start { 53 width: 30px; 54 height: 5px; 55 border-radius: 99px; 56 background-color: #FFFFFFDD; 57} 58 59.details { 60 z-index: 22; 61 position: absolute; 62 top: 240px; 63 left: 60px; 64} 65.details .place-box { 66 height: 46px; 67 overflow: hidden; 68} 69.details .place-box .text { 70 padding-top: 16px; 71 font-size: 20px; 72} 73.details .place-box .text:before { 74 top: 0; 75 left: 0; 76 position: absolute; 77 content: ""; 78 width: 30px; 79 height: 4px; 80 border-radius: 99px; 81 background-color: white; 82} 83.details .title-1, 84.details .title-2 { 85 font-weight: 600; 86 font-size: 72px; 87 font-family: "Oswald", sans-serif; 88} 89.details .title-box-1, 90.details .title-box-2 { 91 margin-top: 2px; 92 height: 100px; 93 overflow: hidden; 94} 95.details > .desc { 96 margin-top: 16px; 97 width: 500px; 98} 99.details > .cta { 100 width: 500px; 101 margin-top: 24px; 102 display: flex; 103 align-items: center; 104} 105.details > .cta > .bookmark { 106 border: none; 107 background-color: #ecad29; 108 width: 36px; 109 height: 36px; 110 border-radius: 99px; 111 color: white; 112 display: grid; 113 place-items: center; 114} 115.details > .cta > .bookmark svg { 116 width: 20px; 117 height: 20px; 118} 119.details > .cta > .discover { 120 border: 1px solid #ffffff; 121 background-color: transparent; 122 height: 36px; 123 border-radius: 99px; 124 color: #ffffff; 125 padding: 4px 24px; 126 font-size: 12px; 127 margin-left: 16px; 128 text-transform: uppercase; 129} 130 131nav { 132 position: fixed; 133 left: 0; 134 top: 0; 135 right: 0; 136 z-index: 50; 137 display: flex; 138 align-items: center; 139 justify-content: space-between; 140 padding: 20px 36px; 141 font-weight: 500; 142} 143nav svg { 144 width: 20px; 145 height: 20px; 146} 147nav .svg-container { 148 width: 20px; 149 height: 20px; 150} 151nav > div { 152 display: inline-flex; 153 align-items: center; 154 text-transform: uppercase; 155 font-size: 14px; 156} 157nav > div:first-child { 158 gap: 10px; 159} 160nav > div:last-child { 161 gap: 24px; 162} 163nav > div:last-child > .active { 164 position: relative; 165} 166nav > div:last-child > .active:after { 167 bottom: -8px; 168 left: 0; 169 right: 0; 170 position: absolute; 171 content: ""; 172 height: 3px; 173 border-radius: 99px; 174 background-color: #ecad29; 175} 176 177.indicator { 178 position: fixed; 179 left: 0; 180 right: 0; 181 top: 0; 182 height: 5px; 183 z-index: 60; 184 background-color: #ecad29; 185} 186 187.pagination { 188 position: absolute; 189 left: 0px; 190 top: 0px; 191 display: inline-flex; 192} 193.pagination > .arrow { 194 z-index: 60; 195 width: 50px; 196 height: 50px; 197 border-radius: 999px; 198 border: 2px solid #ffffff55; 199 display: grid; 200 place-items: center; 201} 202.pagination > .arrow:nth-child(2) { 203 margin-left: 20px; 204} 205.pagination > .arrow svg { 206 width: 24px; 207 height: 24px; 208 stroke-width: 2; 209 color: #ffffff99; 210} 211.pagination .progress-sub-container { 212 margin-left: 24px; 213 z-index: 60; 214 width: 500px; 215 height: 50px; 216 display: flex; 217 align-items: center; 218} 219.pagination .progress-sub-container .progress-sub-background { 220 width: 500px; 221 height: 3px; 222 background-color: #ffffff33; 223} 224.pagination .progress-sub-container .progress-sub-background .progress-sub-foreground { 225 height: 3px; 226 background-color: #ecad29; 227} 228.pagination .slide-numbers { 229 width: 50px; 230 height: 50px; 231 overflow: hidden; 232 z-index: 60; 233 position: relative; 234} 235.pagination .slide-numbers .item { 236 width: 50px; 237 height: 50px; 238 position: absolute; 239 color: white; 240 top: 0; 241 left: 0; 242 display: grid; 243 place-items: center; 244 font-size: 32px; 245 font-weight: bold; 246} 247 248.cover { 249 position: absolute; 250 left: 0; 251 top: 0; 252 width: 100vw; 253 height: 100vh; 254 background-color: #fff; 255 z-index: 100; 256} 257
The dark theme with #1a1a1a background gives a cinematic feel.
Typography uses Inter for text and Oswald for large titles.
.details styles structure the content area (titles, descriptions, buttons).
.pagination defines circular navigation arrows and a progress bar that animates between slides.
.cover creates a smooth transition overlay during page load.
Javascipt Code
1const data = [
2 {
3 place:'Switzerland Alps',
4 title:'SAINT',
5 title2:'ANTONIEN',
6 description:'Tucked away in the Switzerland Alps, Saint Antönien offers an idyllic retreat for those seeking tranquility and adventure alike. It\'s a hidden gem for backcountry skiing in winter and boasts lush trails for hiking and mountain biking during the warmer months.',
7 image:'https://assets.codepen.io/3685267/timed-cards-1.jpg'
8 },
9 {
10 place:'Japan Alps',
11 title:'NANGANO',
12 title2:'PREFECTURE',
13 description:'Nagano Prefecture, set within the majestic Japan Alps, is a cultural treasure trove with its historic shrines and temples, particularly the famous Zenkō-ji. The region is also a hotspot for skiing and snowboarding, offering some of the country\'s best powder.',
14 image:'https://assets.codepen.io/3685267/timed-cards-2.jpg'
15 },
16 {
17 place:'Sahara Desert - Morocco',
18 title:'MARRAKECH',
19 title2:'MEROUGA',
20 description:'The journey from the vibrant souks and palaces of Marrakech to the tranquil, starlit sands of Merzouga showcases the diverse splendor of Morocco. Camel treks and desert camps offer an unforgettable immersion into the nomadic way of life.',
21 image:'https://assets.codepen.io/3685267/timed-cards-3.jpg'
22 },
23 {
24 place:'Sierra Nevada - USA',
25 title:'YOSEMITE',
26 title2:'NATIONAL PARAK',
27 description:'Yosemite National Park is a showcase of the American wilderness, revered for its towering granite monoliths, ancient giant sequoias, and thundering waterfalls. The park offers year-round recreational activities, from rock climbing to serene valley walks.',
28 image:'https://assets.codepen.io/3685267/timed-cards-4.jpg'
29 },
30 {
31 place:'Tarifa - Spain',
32 title:'LOS LANCES',
33 title2:'BEACH',
34 description:'Los Lances Beach in Tarifa is a coastal paradise known for its consistent winds, making it a world-renowned spot for kitesurfing and windsurfing. The beach\'s long, sandy shores provide ample space for relaxation and sunbathing, with a vibrant atmosphere of beach bars and cafes.',
35 image:'https://assets.codepen.io/3685267/timed-cards-5.jpg'
36 },
37 {
38 place:'Cappadocia - Turkey',
39 title:'Göreme',
40 title2:'Valley',
41 description:'Göreme Valley in Cappadocia is a historical marvel set against a unique geological backdrop, where centuries of wind and water have sculpted the landscape into whimsical formations. The valley is also famous for its open-air museums, underground cities, and the enchanting experience of hot air ballooning.',
42 image:'https://assets.codepen.io/3685267/timed-cards-6.jpg'
43 },
44]
45
46const _ = (id)=>document.getElementById(id)
47const cards = data.map((i, index)=>`<div class="card" id="card${index}" style="background-image:url(${i.image})" ></div>`).join('')
48
49
50
51const cardContents = data.map((i, index)=>`<div class="card-content" id="card-content-${index}">
52<div class="content-start"></div>
53<div class="content-place">${i.place}</div>
54<div class="content-title-1">${i.title}</div>
55<div class="content-title-2">${i.title2}</div>
56
57</div>`).join('')
58
59
60const sildeNumbers = data.map((_, index)=>`<div class="item" id="slide-item-${index}" >${index+1}</div>`).join('')
61_('demo').innerHTML = cards + cardContents
62_('slide-numbers').innerHTML = sildeNumbers
63
64
65const range = (n) =>
66 Array(n)
67 .fill(0)
68 .map((i, j) => i + j);
69const set = gsap.set;
70
71function getCard(index) {
72 return `#card${index}`;
73}
74function getCardContent(index) {
75 return `#card-content-${index}`;
76}
77function getSliderItem(index) {
78 return `#slide-item-${index}`;
79}
80
81function animate(target, duration, properties) {
82 return new Promise((resolve) => {
83 gsap.to(target, {
84 ...properties,
85 duration: duration,
86 onComplete: resolve,
87 });
88 });
89}
90
91let order = [0, 1, 2, 3, 4, 5];
92let detailsEven = true;
93
94let offsetTop = 200;
95let offsetLeft = 700;
96let cardWidth = 200;
97let cardHeight = 300;
98let gap = 40;
99let numberSize = 50;
100const ease = "sine.inOut";
101
102function init() {
103 const [active, ...rest] = order;
104 const detailsActive = detailsEven ? "#details-even" : "#details-odd";
105 const detailsInactive = detailsEven ? "#details-odd" : "#details-even";
106 const { innerHeight: height, innerWidth: width } = window;
107 offsetTop = height - 430;
108 offsetLeft = width - 830;
109
110 gsap.set("#pagination", {
111 top: offsetTop + 330,
112 left: offsetLeft,
113 y: 200,
114 opacity: 0,
115 zIndex: 60,
116 });
117 gsap.set("nav", { y: -200, opacity: 0 });
118
119 gsap.set(getCard(active), {
120 x: 0,
121 y: 0,
122 width: window.innerWidth,
123 height: window.innerHeight,
124 });
125 gsap.set(getCardContent(active), { x: 0, y: 0, opacity: 0 });
126 gsap.set(detailsActive, { opacity: 0, zIndex: 22, x: -200 });
127 gsap.set(detailsInactive, { opacity: 0, zIndex: 12 });
128 gsap.set(`${detailsInactive} .text`, { y: 100 });
129 gsap.set(`${detailsInactive} .title-1`, { y: 100 });
130 gsap.set(`${detailsInactive} .title-2`, { y: 100 });
131 gsap.set(`${detailsInactive} .desc`, { y: 50 });
132 gsap.set(`${detailsInactive} .cta`, { y: 60 });
133
134 gsap.set(".progress-sub-foreground", {
135 width: 500 * (1 / order.length) * (active + 1),
136 });
137
138 rest.forEach((i, index) => {
139 gsap.set(getCard(i), {
140 x: offsetLeft + 400 + index * (cardWidth + gap),
141 y: offsetTop,
142 width: cardWidth,
143 height: cardHeight,
144 zIndex: 30,
145 borderRadius: 10,
146 });
147 gsap.set(getCardContent(i), {
148 x: offsetLeft + 400 + index * (cardWidth + gap),
149 zIndex: 40,
150 y: offsetTop + cardHeight - 100,
151 });
152 gsap.set(getSliderItem(i), { x: (index + 1) * numberSize });
153 });
154
155 gsap.set(".indicator", { x: -window.innerWidth });
156
157 const startDelay = 0.6;
158
159 gsap.to(".cover", {
160 x: width + 400,
161 delay: 0.5,
162 ease,
163 onComplete: () => {
164 setTimeout(() => {
165 loop();
166 }, 500);
167 },
168 });
169 rest.forEach((i, index) => {
170 gsap.to(getCard(i), {
171 x: offsetLeft + index * (cardWidth + gap),
172 zIndex: 30,
173 delay: 0.05 * index,
174 ease,
175 delay: startDelay,
176 });
177 gsap.to(getCardContent(i), {
178 x: offsetLeft + index * (cardWidth + gap),
179 zIndex: 40,
180 delay: 0.05 * index,
181 ease,
182 delay: startDelay,
183 });
184 });
185 gsap.to("#pagination", { y: 0, opacity: 1, ease, delay: startDelay });
186 gsap.to("nav", { y: 0, opacity: 1, ease, delay: startDelay });
187 gsap.to(detailsActive, { opacity: 1, x: 0, ease, delay: startDelay });
188}
189
190let clicks = 0;
191
192function step() {
193 return new Promise((resolve) => {
194 order.push(order.shift());
195 detailsEven = !detailsEven;
196
197 const detailsActive = detailsEven ? "#details-even" : "#details-odd";
198 const detailsInactive = detailsEven ? "#details-odd" : "#details-even";
199
200 document.querySelector(`${detailsActive} .place-box .text`).textContent =
201 data[order[0]].place;
202 document.querySelector(`${detailsActive} .title-1`).textContent =
203 data[order[0]].title;
204 document.querySelector(`${detailsActive} .title-2`).textContent =
205 data[order[0]].title2;
206 document.querySelector(`${detailsActive} .desc`).textContent =
207 data[order[0]].description;
208
209 gsap.set(detailsActive, { zIndex: 22 });
210 gsap.to(detailsActive, { opacity: 1, delay: 0.4, ease });
211 gsap.to(`${detailsActive} .text`, {
212 y: 0,
213 delay: 0.1,
214 duration: 0.7,
215 ease,
216 });
217 gsap.to(`${detailsActive} .title-1`, {
218 y: 0,
219 delay: 0.15,
220 duration: 0.7,
221 ease,
222 });
223 gsap.to(`${detailsActive} .title-2`, {
224 y: 0,
225 delay: 0.15,
226 duration: 0.7,
227 ease,
228 });
229 gsap.to(`${detailsActive} .desc`, {
230 y: 0,
231 delay: 0.3,
232 duration: 0.4,
233 ease,
234 });
235 gsap.to(`${detailsActive} .cta`, {
236 y: 0,
237 delay: 0.35,
238 duration: 0.4,
239 onComplete: resolve,
240 ease,
241 });
242 gsap.set(detailsInactive, { zIndex: 12 });
243
244 const [active, ...rest] = order;
245 const prv = rest[rest.length - 1];
246
247 gsap.set(getCard(prv), { zIndex: 10 });
248 gsap.set(getCard(active), { zIndex: 20 });
249 gsap.to(getCard(prv), { scale: 1.5, ease });
250
251 gsap.to(getCardContent(active), {
252 y: offsetTop + cardHeight - 10,
253 opacity: 0,
254 duration: 0.3,
255 ease,
256 });
257 gsap.to(getSliderItem(active), { x: 0, ease });
258 gsap.to(getSliderItem(prv), { x: -numberSize, ease });
259 gsap.to(".progress-sub-foreground", {
260 width: 500 * (1 / order.length) * (active + 1),
261 ease,
262 });
263
264 gsap.to(getCard(active), {
265 x: 0,
266 y: 0,
267 ease,
268 width: window.innerWidth,
269 height: window.innerHeight,
270 borderRadius: 0,
271 onComplete: () => {
272 const xNew = offsetLeft + (rest.length - 1) * (cardWidth + gap);
273 gsap.set(getCard(prv), {
274 x: xNew,
275 y: offsetTop,
276 width: cardWidth,
277 height: cardHeight,
278 zIndex: 30,
279 borderRadius: 10,
280 scale: 1,
281 });
282
283 gsap.set(getCardContent(prv), {
284 x: xNew,
285 y: offsetTop + cardHeight - 100,
286 opacity: 1,
287 zIndex: 40,
288 });
289 gsap.set(getSliderItem(prv), { x: rest.length * numberSize });
290
291 gsap.set(detailsInactive, { opacity: 0 });
292 gsap.set(`${detailsInactive} .text`, { y: 100 });
293 gsap.set(`${detailsInactive} .title-1`, { y: 100 });
294 gsap.set(`${detailsInactive} .title-2`, { y: 100 });
295 gsap.set(`${detailsInactive} .desc`, { y: 50 });
296 gsap.set(`${detailsInactive} .cta`, { y: 60 });
297 clicks -= 1;
298 if (clicks > 0) {
299 step();
300 }
301 },
302 });
303
304 rest.forEach((i, index) => {
305 if (i !== prv) {
306 const xNew = offsetLeft + index * (cardWidth + gap);
307 gsap.set(getCard(i), { zIndex: 30 });
308 gsap.to(getCard(i), {
309 x: xNew,
310 y: offsetTop,
311 width: cardWidth,
312 height: cardHeight,
313 ease,
314 delay: 0.1 * (index + 1),
315 });
316
317 gsap.to(getCardContent(i), {
318 x: xNew,
319 y: offsetTop + cardHeight - 100,
320 opacity: 1,
321 zIndex: 40,
322 ease,
323 delay: 0.1 * (index + 1),
324 });
325 gsap.to(getSliderItem(i), { x: (index + 1) * numberSize, ease });
326 }
327 });
328 });
329}
330
331async function loop() {
332 await animate(".indicator", 2, { x: 0 });
333 await animate(".indicator", 0.8, { x: window.innerWidth, delay: 0.3 });
334 set(".indicator", { x: -window.innerWidth });
335 await step();
336 loop();
337}
338
339async function loadImage(src) {
340 return new Promise((resolve, reject) => {
341 let img = new Image();
342 img.onload = () => resolve(img);
343 img.onerror = reject;
344 img.src = src;
345 });
346}
347
348async function loadImages() {
349 const promises = data.map(({ image }) => loadImage(image));
350 return Promise.all(promises);
351}
352
353async function start() {
354 try {
355 await loadImages();
356 init();
357 } catch (error) {
358 console.error("One or more images failed to load", error);
359 }
360}
361
362start()
363
GSAP (GreenSock Animation Platform) provides smooth, high-performance animations.
The code:
Moves the white cover upwards on load.
Continuously animates the progress bar to simulate slide timing.
This can be expanded to automatically change cards or content every few seconds.
