Skip to content

Commit 1eb8dc3

Browse files
authored
Voyage Slider Gsap
1 parent c0ac608 commit 1eb8dc3

3 files changed

Lines changed: 699 additions & 0 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
<!DOCTYPE html>
2+
<html lang="en" >
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Voyage Slider | GSAP</title>
6+
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.1/css/all.min.css'>
7+
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat&amp;display=swap"rel="stylesheet'><link rel="stylesheet" href="./style.css">
8+
9+
</head>
10+
<body>
11+
12+
<div class="app">
13+
14+
<div class="cardList">
15+
<button class="cardList__btn btn btn--left">
16+
<div class="icon">
17+
<svg>
18+
<use xlink:href="#arrow-left"></use>
19+
</svg>
20+
</div>
21+
</button>
22+
23+
<div class="cards__wrapper">
24+
<div class="card current--card">
25+
<div class="card__image">
26+
<img src="https://source.unsplash.com/Z8dtTatMVMw" alt="" />
27+
</div>
28+
</div>
29+
30+
<div class="card next--card">
31+
<div class="card__image">
32+
<img src="https://source.unsplash.com/9dmycbFE7mQ" alt="" />
33+
</div>
34+
</div>
35+
36+
<div class="card previous--card">
37+
<div class="card__image">
38+
<img src="https://source.unsplash.com/m7K4KzL5aQ8" alt="" />
39+
</div>
40+
</div>
41+
</div>
42+
43+
<button class="cardList__btn btn btn--right">
44+
<div class="icon">
45+
<svg>
46+
<use xlink:href="#arrow-right"></use>
47+
</svg>
48+
</div>
49+
</button>
50+
</div>
51+
52+
<div class="infoList">
53+
<div class="info__wrapper">
54+
<div class="info current--info">
55+
<h1 class="text name">Highlands</h1>
56+
<h4 class="text location">Scotland</h4>
57+
<p class="text description">The mountains are calling</p>
58+
</div>
59+
60+
<div class="info next--info">
61+
<h1 class="text name">Machu Pichu</h1>
62+
<h4 class="text location">Peru</h4>
63+
<p class="text description">Adventure is never far away</p>
64+
</div>
65+
66+
<div class="info previous--info">
67+
<h1 class="text name">Chamonix</h1>
68+
<h4 class="text location">France</h4>
69+
<p class="text description">Let your dreams come true</p>
70+
</div>
71+
</div>
72+
</div>
73+
74+
75+
<div class="app__bg">
76+
<div class="app__bg__image current--image">
77+
<img src="https://source.unsplash.com/Z8dtTatMVMw" alt="" />
78+
</div>
79+
<div class="app__bg__image next--image">
80+
<img src="https://source.unsplash.com/9dmycbFE7mQ" alt="" />
81+
</div>
82+
<div class="app__bg__image previous--image">
83+
<img src="https://source.unsplash.com/m7K4KzL5aQ8" alt="" />
84+
</div>
85+
</div>
86+
</div>
87+
88+
<div class="loading__wrapper">
89+
<div class="loader--text">Loading...</div>
90+
<div class="loader">
91+
<span></span>
92+
</div>
93+
</div>
94+
95+
96+
<svg class="icons" style="display: none;">
97+
<symbol id="arrow-left" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
98+
<polyline points='328 112 184 256 328 400'
99+
style='fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:48px' />
100+
</symbol>
101+
<symbol id="arrow-right" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>
102+
<polyline points='184 112 328 256 184 400'
103+
style='fill:none;stroke:#fff;stroke-linecap:round;stroke-linejoin:round;stroke-width:48px' />
104+
</symbol>
105+
</svg>
106+
107+
108+
109+
110+
111+
112+
113+
114+
115+
116+
117+
<div class="support">
118+
<a href="#" target="_blank"><i class="fab fa-twitter-square"></i></a>
119+
<a href="#" target="_blank"><i class="fab fa-dribbble"></i></a>
120+
</div>
121+
<script src='https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js'></script>
122+
<script src='https://cdnjs.cloudflare.com/ajax/libs/gsap/3.3.3/gsap.min.js'></script><script src="./script.js"></script>
123+
124+
</body>
125+
</html>
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
console.clear();
2+
3+
const { gsap, imagesLoaded } = window;
4+
5+
const buttons = {
6+
prev: document.querySelector(".btn--left"),
7+
next: document.querySelector(".btn--right"),
8+
};
9+
const cardsContainerEl = document.querySelector(".cards__wrapper");
10+
const appBgContainerEl = document.querySelector(".app__bg");
11+
12+
const cardInfosContainerEl = document.querySelector(".info__wrapper");
13+
14+
buttons.next.addEventListener("click", () => swapCards("right"));
15+
16+
buttons.prev.addEventListener("click", () => swapCards("left"));
17+
18+
function swapCards(direction) {
19+
const currentCardEl = cardsContainerEl.querySelector(".current--card");
20+
const previousCardEl = cardsContainerEl.querySelector(".previous--card");
21+
const nextCardEl = cardsContainerEl.querySelector(".next--card");
22+
23+
const currentBgImageEl = appBgContainerEl.querySelector(".current--image");
24+
const previousBgImageEl = appBgContainerEl.querySelector(".previous--image");
25+
const nextBgImageEl = appBgContainerEl.querySelector(".next--image");
26+
27+
changeInfo(direction);
28+
swapCardsClass();
29+
30+
removeCardEvents(currentCardEl);
31+
32+
function swapCardsClass() {
33+
currentCardEl.classList.remove("current--card");
34+
previousCardEl.classList.remove("previous--card");
35+
nextCardEl.classList.remove("next--card");
36+
37+
currentBgImageEl.classList.remove("current--image");
38+
previousBgImageEl.classList.remove("previous--image");
39+
nextBgImageEl.classList.remove("next--image");
40+
41+
currentCardEl.style.zIndex = "50";
42+
currentBgImageEl.style.zIndex = "-2";
43+
44+
if (direction === "right") {
45+
previousCardEl.style.zIndex = "20";
46+
nextCardEl.style.zIndex = "30";
47+
48+
nextBgImageEl.style.zIndex = "-1";
49+
50+
currentCardEl.classList.add("previous--card");
51+
previousCardEl.classList.add("next--card");
52+
nextCardEl.classList.add("current--card");
53+
54+
currentBgImageEl.classList.add("previous--image");
55+
previousBgImageEl.classList.add("next--image");
56+
nextBgImageEl.classList.add("current--image");
57+
} else if (direction === "left") {
58+
previousCardEl.style.zIndex = "30";
59+
nextCardEl.style.zIndex = "20";
60+
61+
previousBgImageEl.style.zIndex = "-1";
62+
63+
currentCardEl.classList.add("next--card");
64+
previousCardEl.classList.add("current--card");
65+
nextCardEl.classList.add("previous--card");
66+
67+
currentBgImageEl.classList.add("next--image");
68+
previousBgImageEl.classList.add("current--image");
69+
nextBgImageEl.classList.add("previous--image");
70+
}
71+
}
72+
}
73+
74+
function changeInfo(direction) {
75+
let currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
76+
let previousInfoEl = cardInfosContainerEl.querySelector(".previous--info");
77+
let nextInfoEl = cardInfosContainerEl.querySelector(".next--info");
78+
79+
gsap.timeline()
80+
.to([buttons.prev, buttons.next], {
81+
duration: 0.2,
82+
opacity: 0.5,
83+
pointerEvents: "none",
84+
})
85+
.to(
86+
currentInfoEl.querySelectorAll(".text"),
87+
{
88+
duration: 0.4,
89+
stagger: 0.1,
90+
translateY: "-120px",
91+
opacity: 0,
92+
},
93+
"-="
94+
)
95+
.call(() => {
96+
swapInfosClass(direction);
97+
})
98+
.call(() => initCardEvents())
99+
.fromTo(
100+
direction === "right"
101+
? nextInfoEl.querySelectorAll(".text")
102+
: previousInfoEl.querySelectorAll(".text"),
103+
{
104+
opacity: 0,
105+
translateY: "40px",
106+
},
107+
{
108+
duration: 0.4,
109+
stagger: 0.1,
110+
translateY: "0px",
111+
opacity: 1,
112+
}
113+
)
114+
.to([buttons.prev, buttons.next], {
115+
duration: 0.2,
116+
opacity: 1,
117+
pointerEvents: "all",
118+
});
119+
120+
function swapInfosClass() {
121+
currentInfoEl.classList.remove("current--info");
122+
previousInfoEl.classList.remove("previous--info");
123+
nextInfoEl.classList.remove("next--info");
124+
125+
if (direction === "right") {
126+
currentInfoEl.classList.add("previous--info");
127+
nextInfoEl.classList.add("current--info");
128+
previousInfoEl.classList.add("next--info");
129+
} else if (direction === "left") {
130+
currentInfoEl.classList.add("next--info");
131+
nextInfoEl.classList.add("previous--info");
132+
previousInfoEl.classList.add("current--info");
133+
}
134+
}
135+
}
136+
137+
function updateCard(e) {
138+
const card = e.currentTarget;
139+
const box = card.getBoundingClientRect();
140+
const centerPosition = {
141+
x: box.left + box.width / 2,
142+
y: box.top + box.height / 2,
143+
};
144+
let angle = Math.atan2(e.pageX - centerPosition.x, 0) * (35 / Math.PI);
145+
gsap.set(card, {
146+
"--current-card-rotation-offset": `${angle}deg`,
147+
});
148+
const currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
149+
gsap.set(currentInfoEl, {
150+
rotateY: `${angle}deg`,
151+
});
152+
}
153+
154+
function resetCardTransforms(e) {
155+
const card = e.currentTarget;
156+
const currentInfoEl = cardInfosContainerEl.querySelector(".current--info");
157+
gsap.set(card, {
158+
"--current-card-rotation-offset": 0,
159+
});
160+
gsap.set(currentInfoEl, {
161+
rotateY: 0,
162+
});
163+
}
164+
165+
function initCardEvents() {
166+
const currentCardEl = cardsContainerEl.querySelector(".current--card");
167+
currentCardEl.addEventListener("pointermove", updateCard);
168+
currentCardEl.addEventListener("pointerout", (e) => {
169+
resetCardTransforms(e);
170+
});
171+
}
172+
173+
initCardEvents();
174+
175+
function removeCardEvents(card) {
176+
card.removeEventListener("pointermove", updateCard);
177+
}
178+
179+
function init() {
180+
181+
let tl = gsap.timeline();
182+
183+
tl.to(cardsContainerEl.children, {
184+
delay: 0.15,
185+
duration: 0.5,
186+
stagger: {
187+
ease: "power4.inOut",
188+
from: "right",
189+
amount: 0.1,
190+
},
191+
"--card-translateY-offset": "0%",
192+
})
193+
.to(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), {
194+
delay: 0.5,
195+
duration: 0.4,
196+
stagger: 0.1,
197+
opacity: 1,
198+
translateY: 0,
199+
})
200+
.to(
201+
[buttons.prev, buttons.next],
202+
{
203+
duration: 0.4,
204+
opacity: 1,
205+
pointerEvents: "all",
206+
},
207+
"-=0.4"
208+
);
209+
}
210+
211+
const waitForImages = () => {
212+
const images = [...document.querySelectorAll("img")];
213+
const totalImages = images.length;
214+
let loadedImages = 0;
215+
const loaderEl = document.querySelector(".loader span");
216+
217+
gsap.set(cardsContainerEl.children, {
218+
"--card-translateY-offset": "100vh",
219+
});
220+
gsap.set(cardInfosContainerEl.querySelector(".current--info").querySelectorAll(".text"), {
221+
translateY: "40px",
222+
opacity: 0,
223+
});
224+
gsap.set([buttons.prev, buttons.next], {
225+
pointerEvents: "none",
226+
opacity: "0",
227+
});
228+
229+
images.forEach((image) => {
230+
imagesLoaded(image, (instance) => {
231+
if (instance.isComplete) {
232+
loadedImages++;
233+
let loadProgress = loadedImages / totalImages;
234+
235+
gsap.to(loaderEl, {
236+
duration: 1,
237+
scaleX: loadProgress,
238+
backgroundColor: `hsl(${loadProgress * 120}, 100%, 50%`,
239+
});
240+
241+
if (totalImages == loadedImages) {
242+
gsap.timeline()
243+
.to(".loading__wrapper", {
244+
duration: 0.8,
245+
opacity: 0,
246+
pointerEvents: "none",
247+
})
248+
.call(() => init());
249+
}
250+
}
251+
});
252+
});
253+
};
254+
255+
waitForImages();

0 commit comments

Comments
 (0)