Skip to content

Instantly share code, notes, and snippets.

@baslie
Created June 23, 2025 13:45
Show Gist options
  • Select an option

  • Save baslie/12867cadd62994b9299bc0b327d366c1 to your computer and use it in GitHub Desktop.

Select an option

Save baslie/12867cadd62994b9299bc0b327d366c1 to your computer and use it in GitHub Desktop.
Фиксированный блок с динамической маской (для Тильды)
<!-- ********************************************************************** -->
<!-- Фиксированный блок с динамической маской -->
<!-- ********************************************************************** -->
<!-- Закрепляет первый экран (класс 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