11---
22import { getImage } from " astro:assets" ;
33import crocoderLogo from " ../assets/crocoder-logo.png" ;
4- import " ../styles/navigation.css" ;
54
65const optimizedLogo = await getImage ({
76 src: crocoderLogo ,
@@ -34,8 +33,6 @@ const optimizedLogo = await getImage({
3433 md:rounded-r-lg
3534 md:overflow-hidden
3635 relative
37- gap:12
38- max-md:has-[input:checked]:bg-secondary
3936 md:w-[680px]"
4037 data-navhidden =" false"
4138 >
@@ -72,17 +69,17 @@ const optimizedLogo = await getImage({
7269 </li >
7370 </ul >
7471
75- <label for = " nav-menu-toggle " class = " md:hidden flex flex-col gap-1.5 " >
76- < input
77- id = " nav-menu-toggle "
78- type = " checkbox "
79- readonly
80- class =" hidden peer/menu-toggle "
81- / >
72+ <button
73+ id = " nav-menu-toggle "
74+ aria-expanded = " false "
75+ aria-controls = " mobile-list "
76+ aria-label = " Toggle menu "
77+ class =" md: hidden flex flex-col gap-1.5 "
78+ >
8279 <span id =" top-line" class =" h-0.5 w-6 bg-neutral-50 line line1" ></span >
8380 <span id =" middle-line" class =" h-0.5 w-6 bg-neutral-50 line line2" ></span >
8481 <span id =" bottom-line" class =" h-0.5 w-6 bg-neutral-50 line line3" ></span >
85- </label >
82+ </button >
8683
8784 <ul class =" hidden items-center md:flex" >
8885 <li id =" for-ctos" data-navhidden =" true" class =" m-2" >
@@ -101,7 +98,7 @@ const optimizedLogo = await getImage({
10198 Blog
10299 </a >
103100 </li >
104- <li id =" book-a-call-container" class =" ml-auto w-0 " >
101+ <li id =" book-a-call-container" class =" ml-auto pr-2 " >
105102 <a
106103 data-navhidden =" true"
107104 id =" book-a-call-action"
@@ -165,140 +162,152 @@ const optimizedLogo = await getImage({
165162 </div >
166163</header >
167164
168- <script is:inline >
169- let lastScrollTop = 0;
170- let isScroll = false;
165+ <script >
166+ import { gsap } from "gsap";
167+ import { ScrollTrigger } from "gsap/ScrollTrigger";
168+
169+ gsap.registerPlugin(ScrollTrigger);
170+ let mm = gsap.matchMedia();
171+
172+ const NAV_SHRINK_WIDTH = "377px";
173+ const NAV_EXPAND_WIDTH = "680px";
174+ const FRAME_DURATION = 0.425;
171175
172- const allDataHidden = document.querySelectorAll("[data-navhidden]");
176+ const navBar = document.getElementById("nav-bar");
177+ const logoElem = document.getElementById("logo");
178+ const forCtoElem = document.getElementById("for-ctos");
179+ const blogElem = document.getElementById("blog");
173180 const bookACallAction = document.getElementById("book-a-call-action");
181+ const containerElement = document.getElementById("book-a-call-container");
174182 const mobileBookACallAction = document.getElementById(
175183 "mobile-book-a-call-action",
176184 );
177- const navMenuToggle = document.getElementById("nav-menu-toggle");
185+
178186 const mobileContact = document.getElementById("mobile-contact");
179- const navElem = document.getElementById("nav-bar");
180- const logoElem = document.getElementById("logo");
181- const forCtoElem = document.getElementById("for-ctos");
182- const blogElem = document.getElementById("blog");
183- const containerElement = document.getElementById("book-a-call-container");
184187 const mobileList = document.getElementById("mobile-list");
188+ const navMenuToggle = document.getElementById("nav-menu-toggle");
185189
186- function onScroll() {
187- const currentScrollTop =
188- window.scrollY || document.documentElement.scrollTop;
190+ mm.add("(width >= 768px)", () => {
191+ if (!navBar || !bookACallAction) return;
189192
190- if (!isScroll) {
191- isScroll = true;
192- window.requestAnimationFrame(() => {
193- handleScroll(currentScrollTop);
194- isScroll = false;
195- });
196- }
197- }
193+ const navHideItems = [forCtoElem, blogElem, logoElem];
198194
199- function handleScroll(currentScrollTop) {
200- /* Since the animation shouldn't trigger on first render, all the animation
201- classes are added first time the handle scroll is triggered*/
202- if (!navElem.classList.contains("animated-navigation")) {
203- navElem.classList.add("animated-navigation");
204- navElem.classList.remove("md:w-[680px]");
205- forCtoElem.classList.add("animated-text");
206- blogElem.classList.add("animated-text");
207- bookACallAction.classList.add("animated-button", "w-fit");
208- containerElement.classList.remove("w-0");
209- containerElement.classList.add("pr-2");
210- handleClassChanges(allDataHidden);
211- }
212- if (
213- currentScrollTop > lastScrollTop &&
214- allDataHidden[0].getAttribute("data-navhidden") === "true"
215- ) {
216- invertDataAttributte(allDataHidden);
217- handleClassChanges(allDataHidden);
218- } else if (
219- currentScrollTop <= lastScrollTop &&
220- allDataHidden[0].getAttribute("data-navhidden") === "false"
221- ) {
222- invertDataAttributte(allDataHidden);
223- }
195+ gsap.set(navBar, { width: NAV_EXPAND_WIDTH, overwrite: false });
196+ gsap.set(navHideItems, {
197+ autoAlpha: 1,
198+ overflow: "hidden",
199+ });
200+ gsap.set(containerElement, { autoAlpha: 0 });
201+ gsap.set(bookACallAction, {
202+ autoAlpha: 0,
203+ overflow: "hidden",
204+ display: "none",
205+ });
224206
225- lastScrollTop = currentScrollTop <= 0 ? 0 : currentScrollTop;
226- }
207+ const tl = gsap.timeline({
208+ paused: true,
209+ defaults: { duration: FRAME_DURATION, ease: "none" },
210+ });
227211
228- function invertDataAttributte(dataHiddenNodes) {
229- dataHiddenNodes.forEach((node) => {
230- if (node.closest("li")?.textContent?.includes("Contact us")) return;
212+ tl.to(navHideItems, {
213+ autoAlpha: 0,
214+ stagger: 0.05,
215+ overwrite: "auto",
216+ display: "none",
217+ }).to(navBar, { width: NAV_SHRINK_WIDTH }, "-=0.1");
231218
232- const currentVal = node.getAttribute("data-navhidden");
233- node.setAttribute(
234- "data-navhidden",
235- currentVal === "true" ? "false" : "true",
236- );
219+ tl.addLabel("showBookACall");
237220
238- node.style.animationName = "none";
239- void node.offsetWidth;
240- node.style.animationName = "";
241- });
242- }
221+ tl.to(
222+ bookACallAction,
223+ { autoAlpha: 1, display: "list-item" },
224+ "showBookACall",
225+ );
226+ tl.to(containerElement, { autoAlpha: 1 }, "showBookACall");
227+
228+ let lastDirection = 0;
243229
244- function handleClassChanges(dataHiddenNodes) {
245- dataHiddenNodes.forEach((node) => {
246- let condition;
247- switch (node.getAttribute("id")) {
248- case "logo":
249- condition = node.getAttribute("data-navhidden") === "false";
250- logoElem.classList.toggle("md:hidden", condition);
251- logoElem.classList.toggle("w-0", condition);
252- break;
253- case "for-ctos":
254- condition = node.getAttribute("data-navhidden") === "true";
255- forCtoElem.classList.toggle("hidden", condition);
256- forCtoElem.classList.toggle("!w-0", condition);
257- break;
258- case "blog":
259- condition = node.getAttribute("data-navhidden") === "true";
260- blogElem.classList.toggle("hidden", condition);
261- blogElem.classList.toggle("!w-0", condition);
262- break;
263- case "book-a-call-action":
264- condition = node.getAttribute("data-navhidden") === "false";
265- bookACallAction.classList.toggle("hidden", condition);
266- bookACallAction.classList.toggle("!w-0", condition);
267- break;
268- default:
269- break;
270- }
230+ const st = ScrollTrigger.create({
231+ onUpdate(self) {
232+ if (self.direction === 1 && lastDirection !== 1) {
233+ tl.play();
234+ lastDirection = 1;
235+ } else if (self.direction === -1 && lastDirection !== -1) {
236+ tl.reverse();
237+ lastDirection = -1;
238+ }
239+ },
271240 });
272- }
273241
274- navMenuToggle.addEventListener("change", () => {
275- mobileContact.classList.toggle("hidden", navMenuToggle.checked);
276- mobileList.classList.toggle("hidden", !navMenuToggle.checked);
277- mobileList.classList.toggle("flex", navMenuToggle.checked);
242+ return () => {
243+ tl.kill();
244+ st.kill();
245+ gsap.killTweensOf([
246+ navBar,
247+ ...navHideItems,
248+ bookACallAction,
249+ containerElement,
250+ ]);
251+ };
278252 });
279253
280- navElem.addEventListener("animationend ", (event ) => {
254+ mm.add("(width < 768px) ", () => {
281255 if (
282- event.animationName === "navigation-animation" &&
283- allDataHidden[0].getAttribute("data-navhidden") === "true"
284- ) {
285- handleClassChanges(allDataHidden);
286- }
287- });
256+ !navMenuToggle ||
257+ !mobileList ||
258+ !mobileContact ||
259+ !mobileBookACallAction
260+ )
261+ return;
262+
263+ gsap.set(mobileList, {
264+ height: 0,
265+ autoAlpha: 0,
266+ display: "flex",
267+ overflow: "hidden",
268+ });
269+ gsap.set(mobileContact, { autoAlpha: 1 });
270+
271+ const tlBurger = gsap.timeline({
272+ paused: true,
273+ defaults: { duration: 0.3, ease: "power2.inOut" },
274+ });
275+
276+ tlBurger
277+ .to("#top-line", { rotation: 45, x: 0, y: 10 }, 0)
278+ .to("#middle-line", { autoAlpha: 0 }, 0)
279+ .to("#bottom-line", { rotation: -45, x: 0, y: -6 }, 0)
280+ .to(navBar, { backgroundColor: "#3c3843" }, 0)
281+ .to(mobileList, { height: "auto", autoAlpha: 1, duration: 0.4 }, 0)
282+ .to(mobileContact, { autoAlpha: 0 }, 0);
283+
284+ const handleBurgerClick = () => {
285+ const expanded = navMenuToggle.getAttribute("aria-expanded") === "true";
286+ navMenuToggle.setAttribute("aria-expanded", String(!expanded));
287+
288+ if (expanded) tlBurger.reverse();
289+ else tlBurger.play();
290+ };
288291
289- function handleBookACall() {
290- navMenuToggle.checked = false;
291- window.navScroll = true;
292+ const handleBookACallClick = () => {
293+ tlBurger.reverse();
294+ navMenuToggle.setAttribute("aria-expanded", "false");
295+ };
292296
293- setTimeout(() => {
294- window.navScroll = false;
295- }, 1500);
296- }
297+ mobileBookACallAction.addEventListener("click", handleBookACallClick);
298+ navMenuToggle.addEventListener("click", handleBurgerClick);
297299
298- window.addEventListener("scroll", onScroll, { passive: true });
299- bookACallAction.addEventListener("click", handleBookACall);
300+ return () => {
301+ navMenuToggle.removeEventListener("click", handleBurgerClick);
302+ mobileBookACallAction.removeEventListener("click", handleBookACallClick);
300303
301- mobileBookACallAction.addEventListener("click", () => {
302- navMenuToggle.click();
304+ gsap.killTweensOf([
305+ mobileList,
306+ mobileContact,
307+ "#top-line",
308+ "#middle-line",
309+ "#bottom-line",
310+ ]);
311+ };
303312 });
304313</script >
0 commit comments