Параллакс скроллинг — это когда элементы заднего плана движутся медленнее переднего при прокрутке страницы. Эффект параллакса создаёт ощущение глубины и делает лендинги запоминающимися. В этой статье разберём, как сделать CSS параллакс для сайта с нуля: возьмём за основу знаменитый сайт игры Firewatch и повторим его эффект — на чистом JavaScript.
Финальный результат — девять слоёв изображения, каждый из которых при прокрутке смещается с разной скоростью. Слои ближе к «зрителю» двигаются быстрее, дальние — медленнее. Именно это и создаёт иллюзию трёхмерного пространства.
Как работает эффект параллакса
Суть метода — слои параллакса фиксируются на экране через CSS-свойство position: fixed (элемент остаётся на одном месте при прокрутке, как будто приклеен к экрану), а затем JavaScript смещает каждый слой по вертикали пропорционально позиции прокрутки. Чем больше коэффициент скорости у слоя, тем быстрее он уходит вверх. Когда пользователь докручивает страницу до основного контента, тот перекрывает параллакс-слои за счёт z-index — CSS-свойства, которое управляет порядком наложения элементов по оси глубины.
Формула смещения слоя проста:
// yPos — смещение слоя в пикселях (отрицательное, чтобы слой уходил вверх)
// scrollY — текущая позиция прокрутки страницы в пикселях
// speed — коэффициент слоя от 0 (не движется) до 100 (движется синхронно с прокруткой)
yPos = -(scrollY * speed / 100) Где scrollY — текущая позиция прокрутки в пикселях, а speed — коэффициент от 0 до 100, который задаётся каждому слою через атрибут data-speed . Задний слой с data-speed="2" почти не движется, передний с data-speed="100" уходит синхронно с прокруткой.
Подготовка графики
Изображение для параллакса нарезается на слои — PNG-файлы с прозрачностью. Каждый слой содержит только «свою» часть картины: небо, горы вдали, лес ближе, передний план. Для нашего примера — девять слоёв из оригинальной графики Firewatch.
Для нарезки своей графики используйте Photoshop или Figma. Экспортируйте каждый слой как PNG с прозрачным фоном. Слои должны быть одинакового размера — это важно для корректного позиционирования. Скачайте слои из примера, чтобы сразу запустить демо:
Скачать слои (PNG) parallax-layers.zip 0.6 MBШаг 1 — HTML-разметка
Создаём контейнер .parallax с девятью слоями внутри. Каждый слой получает id для фонового изображения и атрибут data-speed для управления скоростью прокрутки. Ключевая деталь — инлайновый style="transform: translate3d(0px, 0px, 0px)" на каждом слое: он инициализирует трансформ прямо в HTML, до того как выполнится любой JavaScript. Браузер рисует слои правильно с первого кадра — без артефактов при загрузке или переключении вкладок. Слой 8 — особый: он position: absolute и JavaScript его не анимирует, поэтому инлайновый трансформ ему не нужен.
<div class="parallax">
<div class="parallax__layer" id="parallax-0" data-speed="2" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-1" data-speed="5" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-2" data-speed="11" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-3" data-speed="16" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-4" data-speed="26" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-5" data-speed="36" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-6" data-speed="49" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-7" data-speed="69" style="transform: translate3d(0px, 0px, 0px)"></div>
<div class="parallax__layer" id="parallax-8" data-speed="100"></div>
</div>
<div class="maincontent">
<!-- Основной контент страницы -->
</div> Шаг 2 — CSS для параллакса
Зададим высоту параллакс-зоны и зафиксируем слои. Ключевые свойства:
- position: fixed — слои остаются на месте, пока JavaScript их не сдвигает
- background-size: auto 1038px — изображения масштабируются по высоте, растягиваясь по горизонтали через repeat-x
- z-index: 10 / 99 — контент поверх параллакса при прокрутке
- position: relative на контейнере .parallax — создаёт контекст позиционирования для последнего слоя
- #parallax-8 { position: absolute } — последний слой выключается из анимации и прокручивается браузером нативно: без JS-задержки, без щели при скролле вверх
html, body {
margin: 0;
background-color: #210002;
}
.parallax,
.parallax__layer {
height: 1000px;
}
.parallax {
position: relative;
}
.parallax__layer {
position: fixed;
width: 100%;
background-position: bottom center;
background-size: auto 1038px;
background-repeat: repeat-x;
z-index: 10;
}
#parallax-8 {
position: absolute;
}
.maincontent {
position: relative;
width: 100%;
min-height: 300vh;
z-index: 99;
background-color: #210002;
} Теперь назначим каждому слою своё фоновое изображение:
#parallax-0 { background-image: url(img/layer0.png); background-color: #ffaf1b; }
#parallax-1 { background-image: url(img/layer1.png); }
#parallax-2 { background-image: url(img/layer2.png); }
#parallax-3 { background-image: url(img/layer3.png); }
#parallax-4 { background-image: url(img/layer4.png); }
#parallax-5 { background-image: url(img/layer5.png); }
#parallax-6 { background-image: url(img/layer6.png); }
#parallax-7 { background-image: url(img/layer7.png); }
#parallax-8 { background-image: url(img/layer8.png); } Шаг 3 — JavaScript
Вешаем обработчик scroll на window. При каждом событии читаем pageYOffset и перебираем все слои в цикле: вычисляем смещение и записываем его через setAttribute — именно так устроен оригинальный сайт Firewatch. Слой 8 пропускаем — он position: absolute и прокручивается браузером нативно без задержек. Никакого jQuery, никаких фреймворков.
window.addEventListener('scroll', function() {
var top = window.pageYOffset;
var layers = document.getElementsByClassName('parallax__layer');
for (var i = 0; i < layers.length; i++) {
if (layers[i].id === 'parallax-8') continue;
var speed = layers[i].getAttribute('data-speed');
layers[i].setAttribute('style', 'transform: translate3d(0px, ' + -(top * speed / 100) + 'px, 0px)');
}
}); Нюансы и оптимизация
Отключайте параллакс на мобильных
На мобильных устройствах, особенно iOS, position: fixed ведёт себя непредсказуемо — слои могут прыгать или залипать при инерционном скролле. Проще всего отключить параллакс через медиазапрос в CSS:
@media (max-width: 767px) {
.parallax__layer {
position: absolute;
transform: none !important;
}
} Уважайте настройки доступности
Часть пользователей отключает анимации через системные настройки («Уменьшить движение» в macOS/iOS). Параллакс для них лучше полностью убрать:
@media (prefers-reduced-motion: reduce) {
.parallax__layer {
position: absolute;
transform: none !important;
}
} Добавьте прелоадер
Девять изображений — это девять HTTP-запросов. При медленном соединении слои будут «проявляться» один за другим прямо на глазах у пользователя. Решение — скрыть слои до полной загрузки страницы:
/* Скрываем слои до загрузки */
.parallax__layer {
opacity: 0;
transition: opacity 0.4s;
}
.parallax.is-loaded .parallax__layer {
opacity: 1;
} window.addEventListener('load', function() {
document.querySelector('.parallax').classList.add('is-loaded');
}); Если хотите добавить параллакс проще и быстрее — посмотрите на библиотеку Simple Parallax: она делает всю работу за вас в три строки кода. В блоге есть отдельная статья с разбором.
Скачайте готовый архив с финальным кодом и всеми слоями изображения:
Скачать исходники parallax.zip 0.6 MB