В этой статье показывается, как с помощью CSS трансформаций и махинаций с 3d сделать параллакс-эффект на сайте на чистом CSS.
Параллакс почти всегда создаётся с помощью JavaScript и, чаще всего, получается ресурсоёмким, из-за вешания листенеров на событие скролла, модификации DOM напрямую и срабатывания ненужных перерисовок и перестановок. Всё это происходит асинхронно с потоком, в котором браузер рендерит страницу, из-за чего скролл начинает подтормаживать, а картинка рваться на части. Более правильные реализации параллакса отслеживают скролл и используют отложенные обновления DOM с помощью requestAnimationFrame
. Получается качественной другой результат, но почему бы вообще не избавиться от JavaScript?
Перенос параллакс эффекта в CSS спасает от проблем с производительностью и лишних манипуляций, позволяя браузеру самому всё регулировать за счёт аппаратного ускорения. В результате, почти все ресурсоёмкие процессы обрабатываются напрямую браузерным движком. Частота кадров (FPS) остаётся стабильной, а картинка становится плавной. Плюс, можно сразу комбинировать параллакс с другими CSS фишками — media queries или supports. Отзывчивый параллакс — каково?
Теория
Прежде, чем погрузиться в понимание работы этого механизма, создадим необходимую разметку:
<div class="parallax">
<div class="parallax__group">
<div class="parallax__layer parallax__layer--back">
...
</div>
<div class="parallax__layer parallax__layer--base">
...
</div>
</div>
<div class="parallax__group">
...
</div>
</div>
И базовые стили:
.parallax {
perspective: 1px;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
}
.parallax__layer {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.parallax__layer--base {
transform: translateZ(0);
}
.parallax__layer--back {
transform: translateZ(-1px);
}
Вся магия происходит в классе parallax
. Определение свойств стилей height
и perspective
установит перспективу элемента в его центре, создав фиксированный 3D вьюпорт. overflow-y: auto
позволит контенту внутри элемента нормально скроллиться, при этом потомки элемента будут отрисовываться относительно фиксированной перспективы. В этом и заключается ключ к созданию параллакс эффекта.
Далее, класс parallax__layer
. Как и следует из имени, он определяет слой контента, к которому будет применен параллакс эффект. Элемент с этим классом выдирается из общего потока контента и позиционируется так, чтобы заполнить свой контейнер.
Наконец, у нас есть классы-модификаторы parallax__layer--base
и parallax__layer--back
. Они нужны, чтобы регулировать скорость скролла параллакс элементов, смещая их по оси Z (удаляя или приближая к вьюпорту). Для краткости я сделал всего две скорости скролла — позже мы добавим еще несколько.
Коррекция глубины
Так как параллакс эффект создаётся за счёт 3D преобразований, смещение элемента по оси Z имеет побочный эффект — размеры элемента меняются, в зависимости от того, ближе или дальше он к вьюпорту. Чтобы исправить это, нам нужно применять scale()
трансформацию, чтобы элемент отрисовывался в своём изначальном размере:
.parallax__layer--back {
transform: translateZ(-1px) scale(2);
}
Коэффицент скейла можно посчитать по формуле 1 + (translateZ * -1) / perspective)
. Например, если перспектива вьюпорта задана как 1px
и мы смещаем элемент на -2px
по оси Z, то коэффицентом будет scale(3)
.
.parallax__layer--deep {
transform: translateZ(-2px) scale(3);
}
Смотреть демо с скорректированной глубиной
Регулирование скорости слоя
Скорость слоя регулируется комбинацией значений перспективы и смещения по Z. Элементы с отрицательными значениями Z будут скроллиться медленнее, чем элементы с положительными значениями. Чем больше разность значения от 0, тем явнее параллакс эффект
( т.е. translateZ(-10px)
будет скроллиться медленнее, чем translateZ(-1px)
).
Создание разных участков параллакс эффекта
Предыдущие примеры демонстрировали базовую технику использования простого контента, но ведь большинство параллакс сайтов делят страницу на разные участки с разными эффектами. Вот как можно реализовать это в нашем методе.
Во-первых, нам нужен элемент parallax__group
, чтобы сгруппировать наши слои вместе:
<div class="parallax">
<div class="parallax__group">
<div class="parallax__layer parallax__layer--back">
...
</div>
<div class="parallax__layer parallax__layer--base">
...
</div>
</div>
<div class="parallax__group">
...
</div>
</div>
для него CSS будет выглядеть так:
.parallax__group {
position: relative;
height: 100vh;
transform-style: preserve-3d;
}
В этом примере я хочу, чтобы каждая группа заполнила вьюпорт, поэтому я задаю height: 100vh
, хотя, если нужно, число для каждой группы может быть разным. transform-style: preserve-3d
не даёт браузеру сделать плоскими элементы с parallax__layer
, а position: relative
позволяет дочерним parallax__layer
элементам позиционироваться относительно их группы.
Важное правило, которое нужно помнить — при группировке элементов мы не можем обрезать контент внутри группы, тк overflow: hidden
у элемента parallax__group
сломает весь параллакс эффект. Необрезанный контент приведёт к тому, что дочерние элементы будут выступать за рамки. Поэтому нужно пошаманить с значением z-index
у группы, чтобы быть уверенным, что контент будет корректно прятаться и показываться по мере того, как пользователь будет скроллить сайт.
Нет никаких жестких или быстрых правил по поводу работы со слоями и разные методы подразумевают разную реализацию. Но, чтобы упростить отладку позиционирования слоёв, можно применить простую трансформацию групповых элементов:
.parallax__group {
transform: translate3d(700px, 0, -800px) rotateY(30deg);
}
Взгляните на следующий пример и обратите внимание на галочку debug
!
Поддержка браузеров
- Firefox, Safari, Opera и Chrome — все поддерживают эти эффекты
- Firefox работает, но у него есть небольшая проблема с выравниванием.
- IE пока не поддерживает
preserve-3d
(на подходе), поэтому параллакс работать не будет. Но это нормально, тк всё равно нужно дизайнить так, чтобы контент был адекватным и без параллакса – Ну, progressive enhancement и всё такое!
Статья — перевод (оригинал на blog.keithclark.co.uk — «Pure CSS parallax scrolling websites»)
Автор: alspaladin