Параллакс эффект
Фоновое изображение движется медленнее контента при скролле — CSS
background-attachment: fixedили JS черезtransform: translateY.
Задача
Героическая секция сайта должна создавать глубину: фон скользит медленнее, чем текст поверх него. Нужно реализовать это без тяжёлых библиотек.
Решение
Способ 1 — CSS background-attachment: fixed (простой)
<section class="parallax-hero">
<div class="parallax-hero__content">
<h1>Заголовок</h1>
<p>Описание</p>
</div>
</section>
.parallax-hero {
background-image: url('hero.jpg');
background-attachment: fixed; /* ключевое свойство */
background-size: cover;
background-position: center;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.parallax-hero__content {
text-align: center;
color: #fff;
text-shadow: 0 2px 8px rgba(0,0,0,0.5);
}
Ограничение: не работает корректно в iOS Safari и некоторых браузерах на мобильных.
Способ 2 — JS через transform (кроссбраузерный)
<div class="parallax-wrap">
<div class="parallax-bg" id="parallaxBg" style="background-image: url('hero.jpg')"></div>
<div class="parallax-content">
<h1>Заголовок</h1>
</div>
</div>
.parallax-wrap {
position: relative;
height: 100vh;
overflow: hidden;
}
.parallax-bg {
position: absolute;
inset: -20%; /* запас для сдвига */
background-size: cover;
background-position: center;
will-change: transform;
}
.parallax-content {
position: relative;
z-index: 1;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
}
const bg = document.getElementById('parallaxBg');
const section = bg.closest('.parallax-wrap');
function updateParallax() {
const rect = section.getBoundingClientRect();
// Смещение: от -10% до +10% относительно позиции секции
const progress = rect.top / window.innerHeight; // -1..1
const offset = progress * 15; // 15% скорость параллакса
bg.style.transform = `translateY(${offset}%)`;
}
window.addEventListener('scroll', () => {
requestAnimationFrame(updateParallax);
}, { passive: true });
Ключевые моменты
will-change: transform— создаёт отдельный GPU-слой; анимация не вызывает repaint.requestAnimationFrameв scroll-обработчике — синхронизация с кадром рендера.{ passive: true }— браузер знает чтоpreventDefaultне вызывается, скролл не тормозит.- CSS-вариант проще, но ненадёжен на iOS; JS-вариант работает везде.
Когда НЕ использовать
- На мобильных устройствах — может вызывать укачивание (vestibular disorders); используй
@media (prefers-reduced-motion: reduce). - Для многих параллакс-элементов — производительность падает; рассмотри CSS-only или Intersection Observer.