Если вы хотите, чтобы сайт вызывал «вау-эффект», а ссылками на него делились, используйте параллакс. С ним сайты выглядят объёмно, а элементы могут плавно перемещаться при прокрутке страницы или движении курсора.
В этой статье мы покажем, как сделать параллакс на чистом CSS и JavaScript, поговорим про некоторые JS-библиотеки и посмотрим, как оптимизировать анимацию.
На чём делать параллакс-эффект: CSS или JavaScript
Параллакс при прокрутке страниц создают с помощью 3D трансформаций: transform-style: preserve-3d, perspective, transform: translateZ и других.
Делать такие эффекты на JavaScript слишком ресурсозатратно, потому что браузеру приходится отслеживать движение по событию scroll
, вызывать функцию перерасчёта положения блоков и смещать их. В результате страницы тормозят, а скролл становится прерывистым.
А вот параллакс по движению мыши создаётся на JavaScript. В CSS пока нет подходящих для этого свойств, но с его помощью можно оптимизировать параллакс-эффект.
Как сделать параллакс на чистом CSS
Как задать элементу глубину
Чтобы задать элементу глубину, нужно применить к нему transform: translateZ;
и указать значение свойства perspective
.
Разработчики позиционируют элементы на странице по двум осям: Х и Y, по вертикали и горизонтали. Но есть и третья ось — Z, которая отражает глубину элемента и его расстояние до пользователя. Если просто сдвинуть элемент по этой оси, задав ему свойство transform: translateZ
, то мы не увидим разницы. Элемент пока находится в двухмерной плоскости, ведь экран смартфона или монитор ноутбука не обладают глубиной.
Сделать плоскость трёхмерной можно с помощью CSS-свойства perspective
. В качестве значения оно принимает расстояние от элемента до пользователя по оси Z — чем больше это значение, тем дальше элемент от вас находится, и наоборот. Часто для perspective
указывают значение в 1px — этого достаточно, чтобы установить глубину перспективы.
Пример параллакс-эффекта на чистом CSS
Сделаем параллакс при прокрутке страницы. Сначала подготовим разметку блока с параллаксом. Добавляем <div>
— родитель с классом parallax
. Внутри него создаём два элемента-слоя с классами parallax-layer
. Первый элемент — <div>
с изображением, второй — <span>
с текстом. Указываем для <div>
дополнительный класс parallax-image
, а для <span>
— parallax-text
.
<div class="wrapper parallax">
<div class="parallax-layer parallax-image">
<img src="<https://i.pinimg.com/originals/9e/20/fc/9e20fc9ba2e1456ff29caa6780521cb7.jpg>" alt="Сад изящных слов">
</div>
<span class="parallax-layer parallax-text">Сад изящных слов</span>
</div>
Задаём <div>
-родителю свойство perspective: 1px
. Оно создаёт виртуальную 3D-плоскость, указывая, что центр блока parallax
— исходная точка построения перспективы. Добавляем overflow-y: auto
, чтобы прокручивать элементы-потомки относительно фиксированной точки.
.parallax {
perspective: 1px;
height: 100vh;
overflow-y: auto;
}
Теперь удаляем внутренние элементы с классом parallax-layer
из общего потока и растягиваем на всю площадь родителя.
.parallax-layer {
position: absolute;
inset: 0; // вместо top, bottom, left, right: 0;
}
Остаётся задать смещение по оси Z. Текст будет дальше от пользователя, а фон ближе — за счёт этого мы создадим параллакс-эффект.
.parallax-image {
transform: translateZ(0);
}
.parallax-text {
transform: translateZ(-2px);
}
Добавим стили и получим результат:
Если открыть вкладку с CSS, можно заметить, что для блока parallax-text
задан scale(3)
— то есть элемент увеличен в три раза. Зачем мы это сделали?
Дело в том, что элемент, отдаляясь от нас в 3D-плоскости, визуально уменьшается в размерах. И наоборот, приближаясь — увеличивается. Чтобы компенсировать эти изменения, мы используем scale
. А его значение вычисляем по формуле:
1 + (translateZ * −1) / perspective
В нашем случае вычисление будет таким:
1 + (-2 * −1) / −1
Мы добились параллакс-эффекта на чистом CSS. Все использованные свойства поддерживаются современными браузерами. При желании можно добавить другие элементы в блок parallax
и играть с их смещением по оси Z. Главное — не забывайте, что при изменении положения по этой оси меняются и размеры элемента, поэтому значение scale
надо корректировать.
Пример параллакса с множеством элементов. Автор — Скотт Келлум, CodePen
⭐ Научитесь делать анимации разного уровня сложности — записывайтесь на наш курс. Вы узнаете, как анимировать слайдер, бургер-меню и аккордеон, как создавать карточки товаров с 3D-эффектом, многослойный параллакс и параллакс шапки.
Как сделать параллакс на JavaScript
Теперь создадим параллакс-эффект на JavaScript: сделаем карточку с несколькими элементами, которые будут смещаться при движении курсора — каждый со своей скоростью. Так как элементы смещаются по осям X и Y, это будет 2D-параллакс.
Для начала напишем разметку, похожую на ту, что мы использовали в прошлом примере. Нам нужны <div>
-обёртка и внутренние анимируемые элементы:
<div class="parallax">
<div class="parallax__inner">
<h1 class="parallax__layer title">
Здорово быть енотом!
</h1>
<button class="parallax__layer button" type="button">
Стать енотом
</button>
<div class="parallax__layer circle"></div>
</div>
</div>
Стили:
.parallax__inner {
position: relative;
overflow: hidden;
}
.parallax__layer {
transition: transform 0.3s linear;
}
Уже сейчас можно задать характер анимации при смещении элементов, указав свойство transition
для элементов параллакса. Для более точной настройки можно использовать кривые Безье. Но не рекомендуем использовать значение ease-in-out
: могут появиться «рывки» при быстром движении курсора, ведь функция не обладает достаточной линейностью.
Перейдём к JavaScript. Найдём параллакс-родитель и все параллакс-элементы, а затем добавим обработчик на родитель. Будем слушать движение курсора мыши — mousemove
.
const wrapper = document.querySelector('.parallax__inner');
const layers = document.querySelectorAll('.parallax__layer');
wrapper.addEventListener('mousemove', initParallax);
Опишем функцию initParallax
, в которой будем вести расчёты. Далее декомпозируем задачу: сначала найдём координаты курсора относительно карточки, а затем вычислим новые координаты для элементов при срабатывании события мыши.
Всегда вычисляйте координаты относительно того блока, на котором слушается событие мыши — тогда расчёты будут точными. Если вычислять координаты курсора относительно вьюпорта, то смещение параллакс-элементов будет рассчитываться неправильно, так как пропорции вьюпорта и карточки, скорее всего, не будут совпадать.
Сначала вычислим координаты. В JS нет метода, который возвращал бы координаты курсора относительно нужного блока. Свойство clientX
возвращает положение по оси X относительно начала вьюпорта. Чтобы начало блока parallax
совпадало с 0 по оси X, надо из положения относительно экрана вычесть левый отступ блока parallax
. В этом нам поможет метод getBoundingClientRect()
.
Перейдём к вычислениям. Для удобства записываем текущую координату в переменную и следом добавляем переменную parallaxLeftOffset
с внешним отступом блока wrapper
от границ экрана. Будем вычитать отступ из текущей позиции курсора и записывать это в переменную coordX
:
const initParallax = (evt) => {
const clientX = evt.clientX;
const clientY = evt.clientY;
const parallaxLeftOffset = wrapper.getBoundingClientRect().left;
const coordX = clientX - parallaxLeftOffset;
const coordY = clientY - parallaxTopOffset;
}
Теперь левая граница параллакс-блока совпадает с координатой 0. Это не совсем правильно, ведь мы можем отслеживать изменения курсора только вправо. Нужно сделать так, чтобы центр блока совпадал с координатой 0. Для этого дополнительно вычтем половину ширины блока.
const coordX = clientX - parallaxLeftOffset - (0.5 * wrapper.offsetWidth);
const coordX = clientY - parallaxTopOffset - (0.5 * wrapper.offsetHeight);
Чтобы получить ширину, используем свойство offsetWidth
. Теперь центр враппера — точка с координатами 0.0. Можно двигаться к самому интересному.
Мы вычислили положение курсора относительно параллакс-блока. Теперь мы можем рассчитать смещение его элементов и задать им его:
layers.forEach((layer)=>{
const x = (coordX).toFixed(2);
const y = (coordY).toFixed(2);
layer.setAttribute('style', `transform: translate(${x}px, ${y}px);`)
});
Округляем координату с помощью метода toFixed
и задаём каждому элементу трансформацию по обеим осям. Вот что получилось:
Теперь элементы следуют за курсором. Добавляем коэффициент скорости, который будет замедлять трансформацию элементов. Пусть он будет равен 0.5 — слишком большое значение лучше не устанавливать, так как трансформация должна быть плавной.
Будем умножать вычисленную координату на этот коэффициент.
Код работает, но мы забыли о сути параллакса. Параллакс — это смещение элементов с разной скоростью, а сейчас все элементы двигаются с одинаковой.
Изменим это. Коэффициент скорости будем хранить прямо в разметке в data-атрибуте, так как это удобно.
<div class="parallax">
<div class="parallax__inner">
<h1 class="parallax__layer title" data-speed="0.03">
Здорово быть енотом!
</h1>
<button class="parallax__layer button" type="button" data-speed="0.05">
Стать енотом
</button>
<div class="parallax__layer circle" data-speed="0.18"></div>
</div>
</div>
Значение не должно быть слишком большим, чтобы элементы двигались плавно. Допишем скрипт с поправкой на скорость:
layers.forEach((layer)=>{
const layerSpeed = layer.dataset.speed;
const x = (coordX * layerSpeed).toFixed(2);
const y = (coordY * layerSpeed).toFixed(2);
layer.setAttribute('style', `transform: translate(${x}px, ${y}px);`)
});
Теперь элементы двигаются с разной скоростью. Последний штрих: инвертируем значение смещаемых координат, чтобы при движении мыши влево элементы смещались вправо, и наоборот. Готово!
Как оптимизировать параллакс
В начале статьи мы упомянули, что параллакс ресурсозатратен для браузера — на каждое движение мыши вызывается несколько команд по перерасчёту координат. Что можно с этим сделать?
Не смещайте элементы с помощью свойств top, left, right, bottom
. Вместо них лучше использовать transform: translateX, translateY
— они снижают нагрузку на графический процессор.
В CSS есть свойство will-change. Если задать ему значение transform
, то браузер ещё до анимирования выполнит оптимизации. Это снизит нагрузку на графический процессор, и анимация будет работать плавнее, без рывков.
Теоретически, можно использовать декоратор throttle
на событии мыши. Но чаще всего у вас будут появляться нежелательные рывки при трансформации. Поэтому задавать задержку стоит с осторожностью.
Заключение
Мы показали простые способы создания параллакса на CSS и JS, но иногда нужны более сложные эффекты. Для таких случаев есть специальные библиотеки, например, Loco Scroll, parallax JS или rellax. С их помощью можно управлять направлением движения элементов, фиксировать слайды при прокрутке, создавать эффект «спешки» или «задержки» анимации.