// Находим все dropdown const dropdowns = document.querySelectorAll('.services-dropdown'); // Размеры в rem const hoverSizeIncrease = 1.15; const hoverTextColor = '#969799'; const defaultTextColor = ''; // Исходный цвет текста const dropdownItemCircles = document.querySelectorAll('.dropdown-list_item'); // Определяем глобальные переменные для размеров let dropMaskSize = window.innerWidth >= 1920 ? '15rem' : window.innerWidth >= 1440 ? '12.5rem' : window.innerWidth <= 478 ? '10rem' : '10rem'; let initialSize = window.innerWidth >= 1920 ? '17.5' : window.innerWidth <= 478 ? '15' : '15'; let expandedSize = window.innerWidth >= 1920 ? '60' : window.innerWidth >= 1440 ? '54' : window.innerWidth > 991 ? '36' : // для экранов от 992 до 1439 window.innerWidth > 767 ? '54' : // для экранов от 768 до 991 window.innerWidth > 478 ? '60' : // для экранов от 479 до 767 '40'; // для экранов до 478px // Определяем глобальные переменные для marginTop let dropNameMarginTop = window.innerWidth <= 478 ? '-28rem' : window.innerWidth <= 991 ? '-27rem' : window.innerWidth >= 1920 ? '-15rem' : window.innerWidth >= 1440 ? '-20rem' : window.innerWidth >= 1280 ? '-12rem' : '-16rem'; let dropdownListMarginTop = window.innerWidth <= 478 ? '15rem' : // Для экранов 478px и меньше window.innerWidth <= 767 ? '20rem' : // Для экранов от 479px до 767px window.innerWidth <= 991 ? '20rem' : // Для экранов от 768px до 991px window.innerWidth >= 1920 ? '30rem' : // Для экранов 1920px и больше window.innerWidth >= 1440 ? '25rem' : // Для экранов от 1440px до 1919px window.innerWidth >= 1280 ? '17rem' : // Для экранов от 1280px до 1439px '16rem'; // Для всех остальных // Устанавливаем коэффициент в зависимости от ширины экрана let scrollFactor = window.innerWidth <= 478 ? 1 : window.innerWidth <= 767 ? 1.4 : window.innerWidth >= 1920 ? 0.9 : window.innerWidth >= 1440 ? 1 : 0.8; let circleHeight = window.innerWidth <= 478 ? "3rem" : window.innerWidth < 991 ? "5.625rem" : "3.75rem"; let currentlyOpenedItem = null; // Для отслеживания текущего открытого .dropdown-item_circle // Функция для закрытия всех dropdowns // Function to close all dropdowns function closeAllDropdowns(callback) { const closeTimeline = gsap.timeline({ onComplete: () => callback && callback(), }); dropdowns.forEach((dropdown) => { if (dropdown.classList.contains("opened")) { const circle = dropdown.querySelector(".services-circle"); const dropdownList = dropdown.querySelector(".dropdown-list"); const dropdownItems = dropdown.querySelectorAll(".dropdown-list_item"); const dropName = dropdown.querySelector(".drop-wrapper"); const circleClose = dropdown.querySelector(".circle-close"); closeTimeline.to( dropdownItems, { opacity: 0, y: 10, duration: 0.2, ease: "power2.in", stagger: 0.05, }, 0 ); closeTimeline.add(() => { dropdownItems.forEach((item) => (item.style.display = "none")); dropdownList.style.opacity = "0"; dropdownList.style.display = "none"; }); closeTimeline.to(circle, { width: `${initialSize}rem`, height: `${initialSize}rem`, duration: 0.3, ease: "power2.in", }); closeTimeline.to( dropdown, { height: `${initialSize}rem`, duration: 0.3, ease: "power2.in", color: defaultTextColor, }, "<" ); closeTimeline.to( dropName, { marginTop: dropName.dataset.defaultMarginTop || "0rem", duration: 0.3, ease: "power2.in", }, "<" ); closeTimeline.to( dropdownList, { marginTop: dropdownList.dataset.defaultMarginTop || "0rem", duration: 0.3, ease: "power2.in", }, "<" ); // Hide .circle-close after closing if (circleClose) { circleClose.style.height = "0"; circleClose.style.opacity = "0"; } dropdown.classList.remove("opened"); } }); if (Array.from(dropdowns).some((dropdown) => dropdown.classList.contains("opened")) && callback) { callback(); } } // Устанавливаем изначально display: none для всех dropdown-list_items dropdowns.forEach(dropdown => { const dropdownList = dropdown.querySelector('.dropdown-list'); const dropdownItems = dropdown.querySelectorAll('.dropdown-list_item'); const dropName = dropdown.querySelector('.drop-wrapper'); // Сохраняем начальные значения marginTop dropName.dataset.defaultMarginTop = getComputedStyle(dropName).marginTop; dropdownList.dataset.defaultMarginTop = getComputedStyle(dropdownList).marginTop; dropdownItems.forEach(item => { item.style.display = 'none'; }); dropdownList.style.display = 'none'; const circle = dropdown.querySelector('.services-circle'); const circleClose = dropdown.querySelector('.circle-close'); // Событие клика dropdown.addEventListener('click', (event) => { // Если клик на .circle-close, закрываем dropdown if (event.target.closest('.circle-close')) { closeAllDropdowns(); return; } // Игнорируем клик, если цель — dropdown-list_item if (event.target.closest('.dropdown-list_item')) { return; } if (!dropdown.classList.contains('opened')) { closeAllDropdowns(() => { const openTimeline = gsap.timeline(); openTimeline.to(circle, { width: `${expandedSize}rem`, height: `${expandedSize}rem`, y: `0rem`, duration: 0.3, ease: 'power2.out', }); openTimeline.to( dropdown, { height: `${expandedSize}rem`, duration: 0.3, ease: 'power2.out', color: defaultTextColor, }, '<' ); openTimeline.to( dropName, { marginTop: dropNameMarginTop, duration: 0.3, ease: 'power2.out', }, '<' ); // Добавляем анимацию показа .circle-close openTimeline.to( dropdown.querySelector('.circle-close'), { height: circleHeight, opacity: 1, duration: 0.3, ease: 'power2.out', }, '<' // Синхронизируем с увеличением dropdown ); // Добавляем скролл к открытому dropdown openTimeline.add(() => { const circleRect = circle.getBoundingClientRect(); // Получаем позицию круга const windowHeight = window.innerHeight; // Вычисляем, куда нужно прокрутить const scrollY = window.scrollY + circleRect.top + circleRect.height - windowHeight * scrollFactor; // Скроллим к позиции window.scrollTo({ top: scrollY, behavior: 'smooth', }); }); openTimeline.to( dropdownList, { marginTop: dropdownListMarginTop, duration: 0.3, ease: 'power2.out', }, '<' ); openTimeline.add(() => { dropdownList.style.display = 'flex'; dropdownList.style.opacity = '1'; dropdownItems.forEach(item => { item.style.display = 'flex'; }); }); openTimeline.fromTo( dropdown.querySelectorAll('.dropdown-list_item'), { opacity: 0, y: 10 }, { opacity: 1, y: 0, duration: 0.3, ease: 'power2.out', stagger: 0.1, delay: 0.5 }, '+=0.1' ); dropdown.classList.add('opened'); }); } else { closeAllDropdowns(); } }); }); function resetMaskAndBackground(element, duration = 0.2) { const mask = element.querySelector('.drop-mask'); if (mask) { gsap.to(mask, { width: '0', height: '0', duration: duration, }); } gsap.to(element, { backgroundColor: '', // Сбрасываем фон duration: 0, }); element.style.zIndex = ''; // Сбрасываем z-index } let currentlyOpenedForElement = null; let currentlyOpenedDotElement = null; dropdownItemCircles.forEach((circle) => { circle.addEventListener('click', (e) => { e.stopPropagation(); // Обработка клика для мобильных устройств if (window.innerWidth <= 478) { const dropdownList = circle.closest('.dropdown-list'); if (dropdownList) { gsap.to(dropdownList, { margin: '10rem', duration: 0.3, ease: 'power2.out' }); } // Находим номер элемента из класса is-X let itemNumber = null; for (let i = 1; i <= 9; i++) { if (circle.classList.contains(`is-${i}`)) { itemNumber = i; break; } } // Если нашли номер, обрабатываем for- элемент if (itemNumber !== null) { const targetForElement = document.querySelector(`.for-${itemNumber}`); // Закрываем текущий открытый for- элемент мгновенно if (currentlyOpenedForElement && currentlyOpenedForElement !== targetForElement) { gsap.set(currentlyOpenedForElement, { display: 'none', opacity: 0 }); } // Переключаем отображение целевого for- элемента if (targetForElement) { if (currentlyOpenedForElement === targetForElement) { // Мгновенное скрытие gsap.set(targetForElement, { display: 'none', opacity: 0 }); currentlyOpenedForElement = null; } else { // Плавное появление gsap.set(targetForElement, { display: 'block', opacity: 0 }); gsap.to(targetForElement, { opacity: 1, duration: 0.3, ease: 'power2.out' }); currentlyOpenedForElement = targetForElement; } } } // Закрываем текущую открытую маску if (currentlyOpenedItem && currentlyOpenedItem !== circle) { resetMaskAndBackground(currentlyOpenedItem); currentlyOpenedItem.classList.remove('opened'); } if (circle.classList.contains('opened')) { resetMaskAndBackground(circle); circle.classList.remove('opened'); currentlyOpenedItem = null; const circleClose = circle.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = "0"; circleClose.style.opacity = "0"; } // Мгновенное закрытие for- элемента при закрытии круга if (currentlyOpenedForElement) { gsap.set(currentlyOpenedForElement, { display: 'none', opacity: 0 }); currentlyOpenedForElement = null; } const dropdownList = circle.closest('.dropdown-list'); if (dropdownList) { gsap.to(dropdownList, { margin: '15rem', duration: 0.3, ease: 'power2.out' }); } } else { const dropMask = circle.querySelector('.drop-mask'); if (dropMask) { gsap.to(dropMask, { width: dropMaskSize, height: dropMaskSize, duration: 0.3 }); } gsap.to(circle, { backgroundColor: '#fff', duration: 0, }); circle.style.zIndex = '1'; circle.classList.add('opened'); currentlyOpenedItem = circle; const circleClose = circle.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = circleHeight; circleClose.style.opacity = "1"; } } return; } // Десктопная версия let found = false; for (let i = 1; i <= 9; i++) { if (circle.classList.contains(`is-${i}`)) { const targetElement = document.querySelector(`.for-${i}`); if (currentlyOpenedForElement === targetElement) { targetElement.style.display = 'none'; currentlyOpenedForElement = null; } else { if (currentlyOpenedForElement) { currentlyOpenedForElement.style.display = 'none'; } if (targetElement) { targetElement.style.display = 'block'; currentlyOpenedForElement = targetElement; const children = targetElement.querySelectorAll('div'); if (children.length > 0) { gsap.fromTo(children, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.5, stagger: 0.1 } ); } if (currentlyOpenedDotElement && currentlyOpenedDotElement !== circle) { const previousServicePointer = currentlyOpenedDotElement.querySelector('.service-pointer'); if (previousServicePointer) { previousServicePointer.style.display = 'none'; } } dropdownItemCircles.forEach((dot) => { const dotNumber = [...dot.classList].find(cls => cls.startsWith('is-')); if (dotNumber) { const number = dotNumber.split('-')[1]; const dotElement = document.querySelector(`.dot-${number}`); if (dotElement) { const servicePointer = dotElement.querySelector('.service-pointer'); if (servicePointer) { if (number == i) { servicePointer.style.display = 'block'; servicePointer.style.opacity = '0'; gsap.to(servicePointer, { opacity: 1, duration: 0.5, ease: 'power2.out' }); currentlyOpenedDotElement = dotElement; } else { gsap.to(servicePointer, { opacity: 0, duration: 0.3, ease: 'power2.in', onComplete: () => { servicePointer.style.display = 'none'; } }); } } } } }); } } found = true; break; } } if (currentlyOpenedItem && currentlyOpenedItem !== circle) { resetMaskAndBackground(currentlyOpenedItem); currentlyOpenedItem.classList.remove('opened'); } if (circle.classList.contains('opened')) { resetMaskAndBackground(circle); circle.classList.remove('opened'); currentlyOpenedItem = null; const circleClose = circle.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = "0"; circleClose.style.opacity = "0"; } } else { const dropMask = circle.querySelector('.drop-mask'); if (dropMask) { gsap.to(dropMask, { width: dropMaskSize, height: dropMaskSize, duration: 0.3 }); } gsap.to(circle, { backgroundColor: '#fff', duration: 0, }); circle.style.zIndex = '1'; circle.classList.add('opened'); currentlyOpenedItem = circle; const circleClose = circle.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = circleHeight; circleClose.style.opacity = "1"; } } }); }); const servicesDropdowns = document.querySelectorAll('.services-dropdown'); servicesDropdowns.forEach((servicesDropdown) => { servicesDropdown.addEventListener('click', () => { if (window.innerWidth <= 478) { // На мобильных закрываем текущий открытый item и for- элемент if (currentlyOpenedItem) { resetMaskAndBackground(currentlyOpenedItem); currentlyOpenedItem.classList.remove('opened'); currentlyOpenedItem = null; } if (currentlyOpenedForElement) { gsap.set(currentlyOpenedForElement, { display: 'none', opacity: 0 }); currentlyOpenedForElement = null; } return; } if (currentlyOpenedForElement) { currentlyOpenedForElement.style.display = 'none'; currentlyOpenedForElement = null; } if (currentlyOpenedDotElement) { const servicePointer = currentlyOpenedDotElement.querySelector('.service-pointer'); if (servicePointer) { servicePointer.style.display = 'none'; } currentlyOpenedDotElement = null; } if (currentlyOpenedItem) { resetMaskAndBackground(currentlyOpenedItem); currentlyOpenedItem.classList.remove('opened'); currentlyOpenedItem = null; } const dropdownItemCircles = document.querySelectorAll('.dropdown-item_circle'); dropdownItemCircles.forEach((circle) => { if (circle.classList.contains('opened')) { resetMaskAndBackground(circle); circle.classList.remove('opened'); } }); }); }); function closeAllForAndDotElements() { if (window.innerWidth <= 478) { if (currentlyOpenedForElement) { gsap.set(currentlyOpenedForElement, { display: 'none', opacity: 0 }); currentlyOpenedForElement = null; } return; } if (currentlyOpenedForElement) { currentlyOpenedForElement.style.display = "none"; currentlyOpenedForElement = null; } if (currentlyOpenedDotElement) { const servicePointer = currentlyOpenedDotElement.querySelector(".service-pointer"); if (servicePointer) servicePointer.style.display = "none"; currentlyOpenedDotElement = null; } } const serviceItemWrappers = document.querySelectorAll('.service-item-wrapper'); serviceItemWrappers.forEach((wrapper) => { wrapper.addEventListener("click", () => { // Отключаем функционал на мобильных if (window.innerWidth <= 478) { return; } const dotClass = Array.from(wrapper.classList).find((cls) => cls.startsWith("dot-")); if (!dotClass) return; const dotNumber = dotClass.split("-")[1]; const isClass = `is-${dotNumber}`; const targetDropdown = Array.from(dropdowns).find((dropdown) => { const dropdownListItems = dropdown.querySelectorAll(".dropdown-list_item"); return Array.from(dropdownListItems).some((item) => item.classList.contains(isClass)); }); if (!targetDropdown) return; if (!targetDropdown.classList.contains("opened")) { const isAnyDropdownOpen = Array.from(dropdowns).some(d => d.classList.contains("opened")); if (isAnyDropdownOpen) { closeAllDropdowns(() => { if (!targetDropdown.dataset.clicked) { targetDropdown.dataset.clicked = true; targetDropdown.click(); setTimeout(() => { delete targetDropdown.dataset.clicked; const targetItem = targetDropdown.querySelector(`.dropdown-list_item.${isClass}`); if (!targetItem) return; const dropMask = targetItem.querySelector('.drop-mask'); if (dropMask) { gsap.to(dropMask, { width: dropMaskSize, height: dropMaskSize, duration: 0.3 }); } gsap.to(targetItem, { backgroundColor: '#fff', duration: 0, }); targetItem.style.zIndex = '1'; targetItem.classList.add('opened'); currentlyOpenedItem = targetItem; const circleClose = targetItem.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = circleHeight; circleClose.style.opacity = "1"; } const forElement = document.querySelector(`.for-${dotNumber}`); if (forElement) { forElement.style.display = 'block'; currentlyOpenedForElement = forElement; const children = forElement.querySelectorAll('div'); if (children.length > 0) { gsap.fromTo(children, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.5, stagger: 0.1 } ); } } const dotElement = document.querySelector(`.dot-${dotNumber}`); if (dotElement) { const servicePointer = dotElement.querySelector('.service-pointer'); if (servicePointer) { servicePointer.style.display = 'block'; servicePointer.style.opacity = '0'; gsap.to(servicePointer, { opacity: 1, duration: 0.5, ease: 'power2.out' }); currentlyOpenedDotElement = dotElement; } } }, 800); } }); } else { if (!targetDropdown.dataset.clicked) { targetDropdown.dataset.clicked = true; targetDropdown.click(); setTimeout(() => { delete targetDropdown.dataset.clicked; const targetItem = targetDropdown.querySelector(`.dropdown-list_item.${isClass}`); if (!targetItem) return; const dropMask = targetItem.querySelector('.drop-mask'); if (dropMask) { gsap.to(dropMask, { width: dropMaskSize, height: dropMaskSize, duration: 0.3 }); } gsap.to(targetItem, { backgroundColor: '#fff', duration: 0, }); targetItem.style.zIndex = '1'; targetItem.classList.add('opened'); currentlyOpenedItem = targetItem; const circleClose = targetItem.querySelector('.circle-close'); if (circleClose) { circleClose.style.height = circleHeight; circleClose.style.opacity = "1"; } const forElement = document.querySelector(`.for-${dotNumber}`); if (forElement) { forElement.style.display = 'block'; currentlyOpenedForElement = forElement; const children = forElement.querySelectorAll('div'); if (children.length > 0) { gsap.fromTo(children, { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.5, stagger: 0.1 } ); } } const dotElement = document.querySelector(`.dot-${dotNumber}`); if (dotElement) { const servicePointer = dotElement.querySelector('.service-pointer'); if (servicePointer) { servicePointer.style.display = 'block'; servicePointer.style.opacity = '0'; gsap.to(servicePointer, { opacity: 1, duration: 0.5, ease: 'power2.out' }); currentlyOpenedDotElement = dotElement; } } }, 800); } } } else { const targetItem = targetDropdown.querySelector(`.dropdown-list_item.${isClass}`); if (!targetItem) return; targetItem.click(); } }); });