Школа
Веб-разработка

Многослойный параллакс на ЦСС

6 мар 2025
👁 701  
Веб-разработка

Многослойный параллакс на ЦСС

6 мар 2025
👁 701  
Василий Половнёв
Технический директор бюро
Полезно
13
13
Непонятно
Войдите в Бюросферу, чтобы голосовать

Параллакс — это изменение видимого положения объекта относительно удалённого фона в зависимости от положения наблюдателя. Параллакс пришёл в веб из старых видеоигр. В них фоновые изображения перемещаются медленнее, чем изображения на переднем плане, чтобы создать иллюзию глубины двухмерной сцены, псевдо‑3Д:

Параллакс в играх. Дэн Рут

Чтобы сделать такой сложный, многослойный параллакс понадобятся отдельные слои и ЦСС‑анимации, привязанные к прокрутке. Возьмём первые попавшиеся слои из бесплатного набора изображений для параллакса в играх:

sky.png, небо, самый дальний план
mountains.png, дальний план
dunes.png, средний план
city.png, ближний план
front.png, передний план

Наложим их друг на друга:

<section class="parallax">
  <img src="sky.png" class="parallax__sky">
  <img src="mountains.png" class="parallax__mountains">
  <img src="dunes.png" class="parallax__dunes">
  <img src="city.png" class="parallax__city">
  <img src="front.png" class="parallax__front">
</section>
.parallax {
  display: grid;
  grid-template-areas: 'stack';
}

.parallax > * {
  grid-area: stack; /* Накладываем слои один на один */
}

.parallax > img {
  width: 100%; /* Тянем картинки на весь контейнер */
  height: 100%;
  object-fit: cover;
}

Зазумим ближние слои:

:root {
  --parallaxHeight: 80lvh;
  --parallaxWidth: 100vw;
  --parallaxTopColor: #fdf8f1;
  --parallaxBottomColor: #bf6f4a;
}

.parallax {
  display: grid;
  grid-template-areas: 'stack';
  height: var(--parallaxHeight);
  overflow: clip; /* Чтобы слои параллакса при движении не вылезали за границу контейнера */
}

.parallax > * {
  grid-area: stack;
}

.parallax > img {
  width: 100%;
  height: 100%;
  object-fit: cover;

  min-width: calc(100% * var(--zoom, 1));
  min-height: calc(100% * var(--zoom, 1));
  margin-top: calc(var(--parallaxHeight) * ((1 - var(--zoom, 1)) / 2)); /* Скомпенсируем положение слоёв, чтобы после зума центр слоя оставался на том же месте */
  margin-left: calc(var(--parallaxWidth) * ((1 - var(--zoom, 1)) / 2));

  image-rendering: pixelated; /* Чтобы слои не мылились при увеличении изображения, сохраняя «пиксельность» */
  will-change: transform;
}

.parallax__sky {
  --zoom: 1;
}

.parallax__mountains {
  --zoom: 1;
}

.parallax__dunes {
  --zoom: 1.2;
}

.parallax__city {
  --zoom: 1.25;
}

.parallax__front {
  --zoom: 1.32;
}

Добавим анимацию. При прокрутке будем двигать слои по вертикали с разной скоростью:

:root {
  --parallaxHeight: 80lvh;
  --parallaxWidth: 100vw;
  --parallaxTopColor: #fdf8f1;
  --parallaxBottomColor: #bf6f4a;
  --parallaxScrollHeight: 200px;
}

body {
  background: var(--parallaxBottomColor);
  min-height: calc(100lvh + var(--parallaxScrollHeight)); /* Чтобы нам было куда крутить */
}

.parallax {
  display: grid;
  grid-template-areas: 'stack';
  height: var(--parallaxHeight);
  overflow: clip;
  background-color: var(--parallaxTopColor);
}

.parallax > * {
  grid-area: stack;
  animation: parallax linear both; /* Указываем анимацию */
  animation-timeline: scroll(); /* Просим браузер анимировать при прокрутке окна */
  animation-range: 0 var(--parallaxScrollHeight); /* При прокрутке от 0 до 200 пк */
}

@keyframes parallax { /* Анимация слоёв: двигаем по вертикали с заданной скоростью */
  to {
    transform: translateY(calc(var(--parallaxSpeed, 1) * 2px));
  }
}

.parallax > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  min-width: calc(100% * var(--zoom, 1));
  min-height: calc(100% * var(--zoom, 1));
  margin-top: calc(var(--parallaxHeight) * ((1 - var(--zoom, 1)) / 2));
  margin-left: calc(var(--parallaxWidth) * ((1 - var(--zoom, 1)) / 2));
  image-rendering: pixelated;
  will-change: transform;
}

/* Задаём разную скорость и приближение для слоёв */
.parallax__sky {
  --parallaxSpeed: 50;
  --zoom: 1;
}

.parallax__mountains {
  --parallaxSpeed: 40;
  --zoom: 1;
}

.parallax__dunes {
  --parallaxSpeed: 30;
  --zoom: 1.2;
}

.parallax__city {
  --parallaxSpeed: 20;
  --zoom: 1.25;
}

.parallax__front {
  --parallaxSpeed: 10;
  --zoom: 1.32;
}

Сафари и Файрфокс пока не поддерживают анимации, привязанные к прокрутке. Для них подключим полифил — крошечную библиотеку на Яваскрипте, эмулирующую пока не поддерживаемую браузером фичу. В нашем случае полифил добавляет поддержку animation-timeline: scroll()

<!doctype html>
<html lang="ru"
<head>
  <!-- ... -->
  <script src="https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js"></script>
</head>
<body>
  <!-- ... -->
</body>
</html>

P. S. Это был совет о веб‑разработке. Хотите знать всё о коде, тестах, фронтенд‑разработке, цеэсэсе, яваскрипте, рельсах и джейде? Присылайте вопросы.

Веб‑разработка
Полезно
13
13
Непонятно
Войдите в Бюросферу, чтобы голосовать
Отправить
Поделиться
Поделиться
Запинить
Твитнуть

Комментариев пока нет

Цель рубрики — обсуждение вопросов дизайна, веб-разработки, переговоров, редактуры и управления.
Комментарии модерируются. Мы публикуем комментарии, которые добавляют к уже сказанному новые мысли и хорошие примеры.

Рекомендуем другие советы