From 37dfa48f8049af9e7baa1e3163b5c54e3dab156d Mon Sep 17 00:00:00 2001 From: Ferran Buireu Date: Wed, 20 Dec 2023 21:57:24 +0100 Subject: [PATCH] fix: refine timeline callback to match header styles --- .../intersectionObserver.ts | 83 +++---- .../header/utils/toggleMenu/toggleMenu.ts | 224 +++++++++--------- 2 files changed, 156 insertions(+), 151 deletions(-) diff --git a/src/ui/components/organisms/header/utils/intersectionObserver/intersectionObserver.ts b/src/ui/components/organisms/header/utils/intersectionObserver/intersectionObserver.ts index ede8ae17..e6e5130b 100644 --- a/src/ui/components/organisms/header/utils/intersectionObserver/intersectionObserver.ts +++ b/src/ui/components/organisms/header/utils/intersectionObserver/intersectionObserver.ts @@ -1,53 +1,56 @@ const SELECTORS = { - HEADER: '.header', - LATEST_ARTICLES: '.latest-articles__wrapper', - SITE_LOGO_SVG: '.site__logo svg', - HEADER_MENU_TEXT: '.header', - HEADER_MENU_OUTLINES: '.header__menu-button__outline', + HEADER: '.header', + LATEST_ARTICLES: '.latest-articles__wrapper', + SITE_LOGO_SVG: '.site__logo svg', + SITE_LOGO: '.site__logo', + HEADER_MENU_TEXT: '.header__menu-text', + HEADER_MENU_OUTLINES: '.header__menu-button__outline', }; const getComputedStyleValue = (property: string): string => - getComputedStyle(document.documentElement).getPropertyValue(property); + getComputedStyle(document.documentElement).getPropertyValue(property); function isIntersecting(element: HTMLElement): boolean { - const { HEADER: HEADER_SELECTOR } = SELECTORS; - const headerOffsetHeight = (document.querySelector(HEADER_SELECTOR) as HTMLElement).offsetHeight / 2; - const threshold = element.offsetTop - headerOffsetHeight; - const sectionBottom = element.offsetTop + element.offsetHeight - headerOffsetHeight; + const { HEADER: HEADER_SELECTOR } = SELECTORS; + const headerOffsetHeight = (document.querySelector(HEADER_SELECTOR) as HTMLElement).offsetHeight / 2; + const threshold = element.offsetTop - headerOffsetHeight; + const sectionBottom = element.offsetTop + element.offsetHeight - headerOffsetHeight; - return window.scrollY >= threshold && window.scrollY < sectionBottom; + return window.scrollY >= threshold && window.scrollY < sectionBottom; } export function intersectionObserver(): void { - const WHITE = getComputedStyleValue('--white'); - const BLACK = getComputedStyleValue('--neutral-main'); - const { - HEADER: HEADER_SELECTOR, - LATEST_ARTICLES: LATEST_ARTICLES_SELECTOR, - SITE_LOGO_SVG: SITE_LOGO_SVG_SELECTOR, - HEADER_MENU_TEXT: HEADER_MENU_TEXT_SELECTOR, - HEADER_MENU_OUTLINES: HEADER_MENU_OUTLINES_SELECTOR, - } = SELECTORS; - - const HEADER = document.querySelector(HEADER_SELECTOR) as HTMLElement; - const LATEST_ARTICLES = document.querySelector(LATEST_ARTICLES_SELECTOR) as HTMLElement; - const SITE_LOGO_SVG = document.querySelector(SITE_LOGO_SVG_SELECTOR) as HTMLElement; - const HEADER_MENU_TEXT = document.querySelector(HEADER_MENU_TEXT_SELECTOR) as HTMLElement; - const HEADER_MENU_OUTLINES = document.querySelectorAll(HEADER_MENU_OUTLINES_SELECTOR) as unknown as HTMLElement[]; - - if (!HEADER || !LATEST_ARTICLES) return; - - const hasIntersected = isIntersecting(LATEST_ARTICLES); - - if (hasIntersected) { - SITE_LOGO_SVG.style.fill = WHITE; - HEADER_MENU_TEXT.style.color = WHITE; - HEADER_MENU_OUTLINES.forEach((element) => (element.style.borderColor = WHITE)); - } else { - SITE_LOGO_SVG.style.fill = BLACK; - HEADER_MENU_TEXT.style.color = BLACK; - HEADER_MENU_OUTLINES.forEach((element) => (element.style.borderColor = BLACK)); - } + const WHITE = getComputedStyleValue('--white'); + const BLACK = getComputedStyleValue('--neutral-main'); + const { + HEADER: HEADER_SELECTOR, + LATEST_ARTICLES: LATEST_ARTICLES_SELECTOR, + SITE_LOGO: SITE_LOGO_SELECTOR, + SITE_LOGO_SVG: SITE_LOGO_SVG_SELECTOR, + HEADER_MENU_TEXT: HEADER_MENU_TEXT_SELECTOR, + HEADER_MENU_OUTLINES: HEADER_MENU_OUTLINES_SELECTOR, + } = SELECTORS; + + const HEADER = document.querySelector(HEADER_SELECTOR) as HTMLElement; + const LATEST_ARTICLES = document.querySelector(LATEST_ARTICLES_SELECTOR) as HTMLElement; + const SITE_LOGO_SVG = document.querySelector(SITE_LOGO_SVG_SELECTOR) as HTMLElement; + const SITE_LOGO = document.querySelector(SITE_LOGO_SELECTOR) as HTMLElement; + const HEADER_MENU_TEXT = document.querySelector(HEADER_MENU_TEXT_SELECTOR) as HTMLElement; + const HEADER_MENU_OUTLINES = document.querySelectorAll(HEADER_MENU_OUTLINES_SELECTOR) as unknown as HTMLElement[]; + const isMenuOpen = SITE_LOGO.classList.contains('--is-menu-open'); + + if (!HEADER || !LATEST_ARTICLES || isMenuOpen) return; + const hasIntersected = isIntersecting(LATEST_ARTICLES); + + if (hasIntersected) { + SITE_LOGO_SVG.style.fill = WHITE; + HEADER_MENU_TEXT.style.color = WHITE; + HEADER_MENU_OUTLINES.forEach((element) => (element.style.borderColor = WHITE)); + } else { + SITE_LOGO_SVG.style.fill = BLACK; + HEADER_MENU_TEXT.style.color = BLACK; + HEADER_MENU_OUTLINES.forEach((element) => (element.style.borderColor = BLACK)); + } } window.addEventListener('scroll', intersectionObserver); diff --git a/src/ui/components/organisms/header/utils/toggleMenu/toggleMenu.ts b/src/ui/components/organisms/header/utils/toggleMenu/toggleMenu.ts index 2059c639..855a173a 100644 --- a/src/ui/components/organisms/header/utils/toggleMenu/toggleMenu.ts +++ b/src/ui/components/organisms/header/utils/toggleMenu/toggleMenu.ts @@ -1,130 +1,132 @@ import { gsap, Power2, Power3, Power4 } from 'gsap'; +import { intersectionObserver } from '@components/organisms/header/utils/intersectionObserver'; const ANIMATION_CONFIG = { - POWER4_IN_OUT: Power4.easeInOut, - POWER2_EASE_OUT: Power2.easeOut, - POWER3_OUT: Power3.easeOut, - WHITE: getComputedStyle(document.documentElement).getPropertyValue('--white') ?? 'var(--white)', - PATH_START: 'M0 502S175 272 500 272s500 230 500 230V0H0Z', - PATH_END: 'M0,1005S175,995,500,995s500,5,500,5V0H0Z', + POWER4_IN_OUT: Power4.easeInOut, + POWER2_EASE_OUT: Power2.easeOut, + POWER3_OUT: Power3.easeOut, + WHITE: getComputedStyle(document.documentElement).getPropertyValue('--white') ?? 'var(--white)', + PATH_START: 'M0 502S175 272 500 272s500 230 500 230V0H0Z', + PATH_END: 'M0,1005S175,995,500,995s500,5,500,5V0H0Z', }; const SELECTORS = { - BODY: 'body', - HTML: 'html', - TOGGLE_MENU_BUTTON: '.header__menu-button', - MENU_OVERLAY: '.header__menu-overlay__wrapper', - OVERLAY_PATH: '.header__menu-overlay__wrapper path', - HEADER_MENU_TEXT: '.header__menu-text', - BUTTON_OUTLINE: '.header__menu-button__outline', - SITE_LOGO: '.site__logo', - SITE_LOGO_SVG: '.site__logo svg', - HEADER_MENU: '.header__menu', - NAVIGATION_DIVIDER: '.navigation__menu__divider', - NAVIGATION_ITEMS: '.navigation__menu__item > *', - QUOTE: '.navigation__menu__quote > *', + BODY: 'body', + HTML: 'html', + TOGGLE_MENU_BUTTON: '.header__menu-button', + MENU_OVERLAY: '.header__menu-overlay__wrapper', + OVERLAY_PATH: '.header__menu-overlay__wrapper path', + HEADER_MENU_TEXT: '.header__menu-text', + BUTTON_OUTLINE: '.header__menu-button__outline', + SITE_LOGO: '.site__logo', + SITE_LOGO_SVG: '.site__logo svg', + HEADER_MENU: '.header__menu', + NAVIGATION_DIVIDER: '.navigation__menu__divider', + NAVIGATION_ITEMS: '.navigation__menu__item > *', + QUOTE: '.navigation__menu__quote > *', }; export function toggleMenu() { - let isMenuOpen = false; - let toggleMenuText = 'Menu'; - const timeline = gsap.timeline({ paused: true }); + let isMenuOpen = false; + let toggleMenuText = 'Menu'; + const timeline = gsap.timeline({ paused: true }); + timeline.eventCallback('onReverseComplete', () => intersectionObserver()); - const { - BODY: BODY_SELECTOR, - HTML: HTML_SELECTOR, - TOGGLE_MENU_BUTTON: TOGGLE_MENU_BUTTON_SELECTOR, - MENU_OVERLAY, - OVERLAY_PATH, - BUTTON_OUTLINE, - SITE_LOGO, - SITE_LOGO_SVG, - NAVIGATION_DIVIDER, - HEADER_MENU, - NAVIGATION_ITEMS, - QUOTE, - HEADER_MENU_TEXT, - } = SELECTORS; + const { + BODY: BODY_SELECTOR, + HTML: HTML_SELECTOR, + TOGGLE_MENU_BUTTON: TOGGLE_MENU_BUTTON_SELECTOR, + MENU_OVERLAY, + OVERLAY_PATH, + BUTTON_OUTLINE, + SITE_LOGO, + SITE_LOGO_SVG, + NAVIGATION_DIVIDER, + HEADER_MENU, + NAVIGATION_ITEMS, + QUOTE, + HEADER_MENU_TEXT, + } = SELECTORS; - const BODY = document.querySelector(BODY_SELECTOR)!; - const HTML = document.querySelector(HTML_SELECTOR)!; - const LOGO = document.querySelector(SITE_LOGO)!; - const TOGGLE_MENU_BUTTON = document.querySelector(TOGGLE_MENU_BUTTON_SELECTOR)!; - const MENU_DIVIDER = document.querySelector(NAVIGATION_DIVIDER)!; - const MENU_TEXT = document.querySelector(HEADER_MENU_TEXT)!; + const BODY = document.querySelector(BODY_SELECTOR)!; + const HTML = document.querySelector(HTML_SELECTOR)!; + const LOGO = document.querySelector(SITE_LOGO)!; + const TOGGLE_MENU_BUTTON = document.querySelector(TOGGLE_MENU_BUTTON_SELECTOR)!; + const MENU_DIVIDER = document.querySelector(NAVIGATION_DIVIDER)!; + const MENU_TEXT = document.querySelector(HEADER_MENU_TEXT)!; - const ELEMENTS_TO_TOGGLE = [BODY, HTML, LOGO, MENU_DIVIDER]; + const ELEMENTS_TO_TOGGLE = [BODY, HTML, LOGO, MENU_DIVIDER]; - toggleMenuItems(); + toggleMenuItems(); - function updateButtonContent() { - if (!MENU_TEXT) return; - toggleMenuText = isMenuOpen ? 'Close' : 'Menu'; - const timeout = isMenuOpen ? 300 : 0; - setTimeout(() => (MENU_TEXT.textContent = toggleMenuText), timeout); - } + function updateButtonContent() { + if (!MENU_TEXT) return; + toggleMenuText = isMenuOpen ? 'Close' : 'Menu'; + const timeout = isMenuOpen ? 300 : 0; + setTimeout(() => (MENU_TEXT.textContent = toggleMenuText), timeout); + } - function toggleMenuItems() { - const { POWER4_IN_OUT, POWER2_EASE_OUT, POWER3_OUT, WHITE, PATH_START, PATH_END } = ANIMATION_CONFIG; + function toggleMenuItems() { + const { POWER4_IN_OUT, POWER2_EASE_OUT, POWER3_OUT, WHITE, PATH_START, PATH_END } = ANIMATION_CONFIG; - timeline.to(MENU_OVERLAY, { display: 'block' }); - timeline.to(MENU_TEXT, { - top: '1.5rem', - left: '1rem', - fontSize: '1.5rem', - color: WHITE, - x: 0, - y: 0, - ease: POWER4_IN_OUT, - duration: 1, - }); - timeline.add(() => updateButtonContent(), '<'); - timeline.to( - BUTTON_OUTLINE, - { - width: '90px', - height: '90px', - border: `1px solid ${WHITE}`, - x: 0, - y: 0, - ease: POWER4_IN_OUT, - duration: 1, - }, - '<' - ); - timeline.to(SITE_LOGO, { y: '3rem', duration: 0.25 }, '<'); - timeline.to(SITE_LOGO_SVG, { fill: WHITE, duration: 0.25 }, '<'); - timeline - .to(OVERLAY_PATH, { attr: { d: PATH_START }, ease: POWER2_EASE_OUT, duration: 1 }, '<') - .to(OVERLAY_PATH, { attr: { d: PATH_END }, ease: POWER2_EASE_OUT, duration: 1 }, '-=.5'); - timeline.to(HEADER_MENU, { visibility: 'visible', duration: 1 }, '-=.5'); - timeline - .to( - NAVIGATION_ITEMS, - { - top: 0, - ease: POWER3_OUT, - stagger: { amount: 0.5 }, - duration: 0.75, - }, - '<' - ) - .reverse(); - timeline.to( - QUOTE, - { - top: 0, - ease: POWER3_OUT, - duration: 0.75, - }, - '<' - ); - } + timeline.to(MENU_OVERLAY, { display: 'block' }); + timeline.to(MENU_TEXT, { + top: '1.5rem', + left: '1rem', + fontSize: '1.5rem', + color: WHITE, + x: 0, + y: 0, + ease: POWER4_IN_OUT, + duration: 1, + }); + timeline.add(() => updateButtonContent(), '<'); + timeline.to( + BUTTON_OUTLINE, + { + width: '90px', + height: '90px', + border: `1px solid ${WHITE}`, + x: 0, + y: 0, + ease: POWER4_IN_OUT, + duration: 1, + }, + '<' + ); + timeline.to(SITE_LOGO, { y: '3rem', duration: 0.25 }, '<'); + timeline.to(SITE_LOGO_SVG, { fill: WHITE, duration: 0.25 }, '<'); + timeline + .to(OVERLAY_PATH, { attr: { d: PATH_START }, ease: POWER2_EASE_OUT, duration: 1 }, '<') + .to(OVERLAY_PATH, { attr: { d: PATH_END }, ease: POWER2_EASE_OUT, duration: 1 }, '-=.5'); + timeline.to(HEADER_MENU, { visibility: 'visible', duration: 1 }, '-=.5'); + timeline + .to( + NAVIGATION_ITEMS, + { + top: 0, + ease: POWER3_OUT, + stagger: { amount: 0.5 }, + duration: 0.75, + }, + '<' + ) + .reverse(); + timeline.to( + QUOTE, + { + top: 0, + ease: POWER3_OUT, + duration: 0.75, + }, + '<' + ); + } - TOGGLE_MENU_BUTTON.addEventListener('click', () => { - isMenuOpen = !isMenuOpen; - timeline.reversed(!timeline.reversed()); + TOGGLE_MENU_BUTTON.addEventListener('click', () => { + isMenuOpen = !isMenuOpen; + timeline.reversed(!timeline.reversed()); - ELEMENTS_TO_TOGGLE.forEach((element) => element.classList.toggle('--is-menu-open')); - }); + ELEMENTS_TO_TOGGLE.forEach((element) => element.classList.toggle('--is-menu-open')); + }); }