Created
June 23, 2025 13:45
-
-
Save baslie/12867cadd62994b9299bc0b327d366c1 to your computer and use it in GitHub Desktop.
Фиксированный блок с динамической маской (для Тильды)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- ********************************************************************** --> | |
| <!-- Фиксированный блок с динамической маской --> | |
| <!-- ********************************************************************** --> | |
| <!-- Закрепляет первый экран (класс uc-first-screen) и создает эффект --> | |
| <!-- «ухода под» остальные блоки при скролле через белую маску. --> | |
| <!-- Поддерживает lazy loading Тильды и адаптивность. --> | |
| <!-- ********************************************************************** --> | |
| <script> | |
| (function() { | |
| // Основные переменные для работы скрипта | |
| let mask; // DOM-элемент белой маски, которая будет скрывать фиксированный блок | |
| let fixedBlock; // DOM-элемент блока, который нужно зафиксировать | |
| let firstSiblingTop; // Позиция первого блока после фиксированного (в пикселях от верха страницы) | |
| // Настройки скрипта - все параметры собраны в одном месте для удобства изменения | |
| const CONFIG = { | |
| fixedBlockClass: 'uc-first-screen', // CSS-класс блока, который нужно зафиксировать | |
| maskClass: 'uc-dynamic-mask', // CSS-класс для создаваемой маски | |
| siblingClass: 't-rec', // CSS-класс блоков-соседей (стандартный класс блоков Тильды) | |
| maskBackground: '#ffffff', // Цвет фона маски (белый) | |
| transitionSpeed: '0.1s' // Скорость анимации движения маски | |
| }; | |
| /** | |
| * Главная функция инициализации | |
| * Находит нужный блок, создает маску и настраивает все элементы | |
| */ | |
| function initializeMask() { | |
| // Ищем блок с классом uc-first-screen | |
| fixedBlock = document.querySelector(`.${CONFIG.fixedBlockClass}`); | |
| // Если блок еще не загружен (lazy loading), ждем еще 100мс | |
| if (!fixedBlock) { | |
| setTimeout(initializeMask, 100); | |
| return; | |
| } | |
| // Проверяем, что блок с таким классом только один (для избежания конфликтов) | |
| const allFixedBlocks = document.querySelectorAll(`.${CONFIG.fixedBlockClass}`); | |
| if (allFixedBlocks.length > 1) { | |
| console.warn(`Warning: Found ${allFixedBlocks.length} blocks with class "${CONFIG.fixedBlockClass}". Using the first one.`); | |
| } | |
| // Получаем высоту блока, который будем фиксировать | |
| const fixedBlockHeight = fixedBlock.offsetHeight; | |
| // Применяем CSS-стили для фиксации блока в верхней части экрана | |
| fixedBlock.style.cssText += ` | |
| position: fixed !important; /* Фиксируем блок относительно окна браузера */ | |
| top: 0 !important; /* Прижимаем к верху */ | |
| left: 0 !important; /* Прижимаем к левому краю */ | |
| width: 100% !important; /* Растягиваем на всю ширину */ | |
| z-index: 1 !important; /* Низкий z-index, чтобы другие блоки были поверх */ | |
| `; | |
| // Создаем белую маску, которая будет скрывать фиксированный блок | |
| mask = document.createElement('div'); | |
| mask.className = CONFIG.maskClass; | |
| // Применяем стили к маске | |
| mask.style.cssText = ` | |
| position: fixed !important; /* Фиксируем маску */ | |
| top: 100vh !important; /* Изначально размещаем за нижним краем экрана */ | |
| left: 0 !important; /* Прижимаем к левому краю */ | |
| width: 100% !important; /* Растягиваем на всю ширину */ | |
| height: 100vh !important; /* Высота равна высоте экрана */ | |
| background: ${CONFIG.maskBackground} !important; /* Белый фон */ | |
| z-index: 50 !important; /* Средний z-index (выше фиксированного блока, но ниже контента) */ | |
| pointer-events: none !important; /* Маска не должна блокировать клики */ | |
| transition: transform ${CONFIG.transitionSpeed} ease-out !important; /* Плавная анимация движения */ | |
| `; | |
| // Добавляем маску в DOM | |
| document.body.appendChild(mask); | |
| // Настраиваем все блоки, которые идут после фиксированного | |
| const firstSibling = fixedBlock.nextElementSibling; | |
| if (firstSibling) { | |
| let currentSibling = firstSibling; // Текущий обрабатываемый блок | |
| let isFirst = true; // Флаг для определения первого блока после фиксированного | |
| // Проходим по всем блокам-соседям | |
| while (currentSibling) { | |
| // Проверяем, что это блок Тильды (имеет класс t-rec) | |
| if (currentSibling.classList.contains(CONFIG.siblingClass)) { | |
| // Применяем стили к блоку-соседу | |
| currentSibling.style.cssText += ` | |
| position: relative !important; /* Относительное позиционирование */ | |
| z-index: 100 !important; /* Высокий z-index (поверх маски и фиксированного блока) */ | |
| background: ${CONFIG.maskBackground} !important; /* Белый фон для полного перекрытия */ | |
| `; | |
| // Для первого блока после фиксированного добавляем отступ сверху | |
| if (isFirst) { | |
| // Отступ равен высоте фиксированного блока, чтобы контент не наезжал | |
| currentSibling.style.marginTop = fixedBlockHeight + 'px'; | |
| // Запоминаем позицию первого блока для расчета движения маски | |
| firstSiblingTop = currentSibling.getBoundingClientRect().top + window.scrollY; | |
| isFirst = false; | |
| } | |
| } | |
| // Переходим к следующему блоку | |
| currentSibling = currentSibling.nextElementSibling; | |
| } | |
| } | |
| // Разрешаем overflow для корневого контейнера Тильды | |
| // (по умолчанию Тильда ставит overflow: hidden) | |
| document.getElementById('allrecords').style.overflow = 'visible !important'; | |
| // Подключаем обработчики событий | |
| window.addEventListener('scroll', updateMask); // Обновление маски при скролле | |
| window.addEventListener('resize', recalculatePositions); // Пересчет позиций при изменении размера окна | |
| // Выполняем первоначальное обновление маски | |
| updateMask(); | |
| } | |
| /** | |
| * Функция обновления позиции маски при скролле | |
| * Маска поднимается снизу и постепенно закрывает фиксированный блок | |
| */ | |
| function updateMask() { | |
| // Проверяем, что маска создана и позиция первого блока определена | |
| if (!mask || !firstSiblingTop) return; | |
| const scrollY = window.scrollY; // Текущая позиция скролла | |
| const windowHeight = window.innerHeight; // Высота окна браузера | |
| // Вычисляем, насколько пользователь проскроллил относительно начала первого блока-соседа | |
| // Формула: текущий скролл - (позиция первого блока - высота окна) | |
| // Это означает, что маска начнет подниматься, когда первый блок появится в нижней части экрана | |
| const scrollProgress = scrollY - (firstSiblingTop - windowHeight); | |
| if (scrollProgress > 0) { | |
| // Если пользователь проскроллил до области, где должна работать маска | |
| // Вычисляем, на сколько пикселей поднять маску | |
| // Маска не может подняться выше, чем на высоту окна | |
| const maskOffset = Math.min(scrollProgress, windowHeight); | |
| // Применяем трансформацию - поднимаем маску снизу | |
| mask.style.transform = `translateY(-${maskOffset}px)`; | |
| } else { | |
| // Если еще не дошли до нужной области - маска остается внизу (не видна) | |
| mask.style.transform = 'translateY(0)'; | |
| } | |
| } | |
| /** | |
| * Функция пересчета позиций при изменении размера окна | |
| * Нужна для корректной работы на мобильных устройствах при повороте экрана | |
| */ | |
| function recalculatePositions() { | |
| if (!fixedBlock) return; | |
| // Находим первый блок после фиксированного | |
| const firstSibling = fixedBlock.nextElementSibling; | |
| if (firstSibling && firstSibling.classList.contains(CONFIG.siblingClass)) { | |
| // Пересчитываем его позицию | |
| firstSiblingTop = firstSibling.getBoundingClientRect().top + window.scrollY; | |
| } | |
| } | |
| // Запуск инициализации | |
| // Проверяем, загрузился ли уже DOM | |
| if (document.readyState === 'loading') { | |
| // Если DOM еще загружается - ждем события DOMContentLoaded | |
| document.addEventListener('DOMContentLoaded', initializeMask); | |
| } else { | |
| // Если DOM уже загружен - запускаем сразу | |
| initializeMask(); | |
| } | |
| })(); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment