Параллакс эффект на чистом CSS - перевод

Параллакс эффект на чистом CSS - перевод

Всем привет, сегодня мы рассмотрим очень интересную плюшку для сайта - параллакс эффект. При грамотном использовании, он может добавить глубину и утонченность Вашему сайту или приложению.

Но есть проблема - сделать параллакс без тормозов и дерганий может стать сложной задачей для начинающего (и не очень) веб-мастера.

В этой статье мы обсудим решения, которые имеют хорошую производительность, а это немало важно для красивого и информативного сайта или приложения.

Содержание статьи:

Для начала давайте отметим несколько важных пунктов:

  • Не используйте события scroll либо background-position для создания параллакс анимации.
  • Используйте CSS 3D трансформации для создания более точного эффекта параллакса.
  • Для мобильного браузера Safari используйте position: sticky чтобы эффект параллакса гарантированно размножился.

Вы можете зайти в репозиторий Github – примеры UI элементов и вытащить от туда Parallax helper JS! Вы можете посмотреть живое демо параллакс скролла в репозитории Github.

Проблемы параллакс эффекта

Для начала давайте рассмотрим 2 основных пути для достижения параллакс эффекта, и в частности почему они не подходят для наших целей.

Плохо: использование событий scroll

Ключевое требование для параллакса в том, чтобы он был scroll зависимым. При каждой прокрутке скролла позиция элементов параллакса должна обновляться. Хотя это звучит просто, важным механизмом современных браузеров является их способность работать асинхронно. В нашем конкретном случае это относится к событию прокрутки.

Во многих браузерах события прокрутки предоставляются низкого качества и нет гарантии доставки их к каждому кадру анимации прокрутки!

Этот кусок важной информации рассказывает нам почему нужно избегать решений на javascript, которые передвигают элементы, основанные на событиях прокрутки:

Javascript не гарантирует что параллакс будет успевать за прокруткой страницы. В старых версиях мобильного Safari события прокрутки на самом деле предоставлялись к концу прокрутки, что делало не возможным выполнение эффектов прокрутки, основанных на Javascript.

Большинство последних версий предоставляют события прокрутки во время анимации прокрутки, но по аналогии с Chrome, основаны на низком качестве. Если основной поток занят другими задачами, события прокрутки не будут доставлены немедленно, это означает что эффект параллакса будет утерян.

Плохо: обновление background-position

Другая ситуация, которую мы должны избежать это отрисовка каждого кадра. Многие решения пытаются изменить background-position обеспечить вид параллакса, что приводит к перерисовке испорченных частей страницы при прокрутке и это может привести к ненужной анимации.

Если мы хотим выполнить обещанное параллакс перемещение, нам необходимо что-то такое, что может быть применено в качестве ускорителя (что на сегодняшний день означает придерживаться transforms и opacity), и которое не полагается на события scroll.

3D в CSS

Scott Kellum и Keith Clark провели значительную работу в области использования CSS 3D для достижения параллакс перемещения. Они эффективно используют следующий технический прием:

  • Установить scroll для содержимого элемента с overflow-y: scroll (и вероятно overflow-x: hidden).
  • К тому же элементу добавить значение perspective, и для perspective-origin установить top left или 0 0.
  • Потомкам того элемента применить сдвиг по оси Z, и масштабировать резервную копию таким образом, чтобы параллакс движение не затрагивало их размер на экране.

CSS для такого подхода выглядит так:

css
1
2
3
4
5
6
7
8
9
10
11
12
.container {
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
perspective: 1px;
perspective-origin: 0 0;
}
.parallax-child {
transform-origin: 0 0;
transform: translateZ(-2px) scale(3);
}

Он предполагает следующий фрагмент HTML:

html
1
2
3
<div class="container">
<div class="parallax-child"></div>
</div>

Настройка масштаба для perspective

Сдвиг дочернего элемента назад может стать причиной уменьшения пропорционально значению perspective. Вы можете рассчитать до какого значения масштабировать с помощью следующего уравнения: (perspective - distance) / perspective. Так как мы скорее всего хотим сместить элемент, но при появлении его в том размере, в котором мы ему указали, его необходимо масштабировать таким образом, чтобы не сдвинуть его в лево.

В нашем случае в коде выше perspective равен 1 px, Z дистанция parallax-child равна -2px. Это означает, что элемент нужно 3х кратно увеличить, в коде вы можете наблюдать это как следующую запись scale(3).

Для любого контента, у которого не будет задана величина translateZ Вы можете заменить ее на 0. Это значит масштаб будет (perspective - 0) / perspective, что даются сетку из 1 значения, что значит невозможность ни увеличить ни уменьшить масштаб. Реально удобно.

Как работает данный подход

Важно чтобы было ясно почему это работает, поскольку мы будем использовать эти знания в ближайшее время. Скроллинг это фактически transform, именно поэтому он может быть ускорен; это в основном включается себя смещение слоев вокруг с помощью GPU. В типичной прокрутке, которая не имеет представление о perspective, прокрутка происходит в соотношении 1 к 1 когда сравнивают прикручиваемые элементы и их потомки. Если прокрутить элементы на 300 px вниз, то предки трансформируются на верх на те же 300 px.

Однако применение значения perspective к прокручиваемым элементам вносит путаницу с этим процессом; это меняет матрицу, которая лежит в основе преобразование скролла. Сейчас прокрутка на 300px может сдвинуть потомка на 150px, в зависимости от того какие Вы выбрали значения perspective и translateZ. Если какой-то элемент имеет значение translateZ равным 0 он будет прокручиваться в масштабе 1 к 1(как раньше), но потомок оттолкнутый по оси Z от начального perspective будет скролится с другой скоростью. Чистый результат: параллакс перемещение. И что немало важно, это регулировка происходит внутренним механизмом браузеров автоматически, что значит отсутствие необходимости прислушиваться к событиям scroll или менять background-position.

Ложка дегтя в бочке меда: мобильный Safari

Есть много предостережений к каждому эффекту, и один из важным для transforms это сохранение 3D эффектов для потомков. Если есть элементы в иерархии, между элементом и его параллакс потомком, 3D perspective имеет значение “flattened”, что означает эффект потерян.

html
1
2
3
4
5
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>

В коде выше, .parallax-container новый, и он фактически будет сглаживать значение perspective и мы потеряем эффект параллакса. Решение, в большинстве случаев, довольно простое: вы задаете элементу transform-style: preserve-3d, заставляя его размножать некоторые 3D эффекты (такие как наше значение perspective) которые применяться дальше по дереву.

css
1
2
3
.parallax-container {
transform-style: preserve-3d;
}

Однако в случае мобильного Safari все немного запутаннее. Применение overflow-y: scroll к элементу контейнера технически работает, но за счёт швыряния прокручиваемых элементов. Решением будет добавить -webkit-overflow-scrolling: touch, но он также сгладит perspective и мы не получим никакого параллакса.

С прогрессивной точки зрения, это не такая уж большая проблема. Если мы не сможем добиться параллакса в любой ситуации наше приложение все равно будет работать, но неплохо было бы выяснить обходной путь.

В помощь position: sticky!

Существует, на самом деле, некоторая помощь в виде position: sticky, это нужно чтобы элементы “придерживались” к верху от viewport либо учитывали родительский элемент в время прокрутки. Спецификация, как и многие из них, довольно большая, но она содержит в себе маленькую полезную жемчужину:

Блок с позиционированием stick позиционируется аналогично блоку с позиционированием relative, но смещение вычисляется с ссылкой на ближайшего предка с ползунком прокрутки или viewport если нет предка с ползунком прокрутки. - CSS Positioned Layout Module Level 3.

Это может показаться не таким уж значимым на первый взгляд, но ключевой момент в том выражении в том, как вычисляется параметр элемента stick: “смещение вычисляется с ссылкой на ближайшего предка с ползунком прокрутки”. Другими словами, расстояние движения элементов со значением stick (для того чтобы казаться прикрепленным к другому элементу или к видимой части экрана) высчитывается до применения каких-либо других трансформаций, а не после. Это значит, очень похоже на пример приведенный ранее, если смещение было рассчитано на 300px, это новая возможность использования значений perspective (или иного transform) для того чтобы влиять на значение перемещения на 300px до того как оно будет применено к любому элементу со значением stick.

Применяя position: -webkit-sticky для элемента с параллаксом мы можем фактически получить “обратное” эффекту от -webkit-overflow-scrolling: touch. Это гарантирует, что элемент с параллаксом ссылается на ближайшего предка с ползунком прокрутки, в данном случае это .container . Далее также как и до этого добавляем значение perspective к .parallax-container, которое изменяет рассчитанное перемещение скролла и создает эффект параллакса.

html
1
2
3
4
5
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.container {
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
.parallax-container {
perspective: 1px;
}
.parallax-child {
position: -webkit-sticky;
top: 0px;
transform: translate(-2px) scale(3);
}

Это восстанавливает эффект параллакса для мобильного Safari, что является отличной новостью для всех!

Предостережения для позиционирования sticky

Тем не менее тут есть различие: position: sticky изменяет механизм параллакса. Позиционирование sticky пытается, полностью, приклеить элемент к прокручиваемому контейнеру, в то время как версия без sticky этого не делает.

  • С position: sticky, чем ближе элемент к z = 0 тем меньше он двигается.
  • Без position: sticky, чем ближе элемент к z = 0 тем больше он двигается.

Если все это кажется немного абстрактным, Посмотрите демо от Роберта Флака, которое демонстрирует разницу в поведении элементов с позиционированием sticky и без. Чтобы видеть разницу Вам нужен Chrome Canary (версия 58 на момент написания статьи) или Safari.

Демо Роберта Флака показывает как position: sticky влияет на параллакс прокручивание.

Разные ошибки и способы их устранения

Как всегда есть бугорки и неровности, которые нужно сгладить:

  • Поддержка sticky противоречива. Поддержка реализована в Chrome, Edge не поддерживает полностью, а FireFox имеет баги отрисовки при совместном использовании sticky и transforms: perspective. В таких случаях стоит добавить небольшой код только лишь для того чтобы подключить position: sticky (с префиксом -webkit-) когда это необходимо, это нужно только для мобильного Safari.
  • Данный эффект совсем не работает в Edge. Edge пытается обработать прокручивание на уровне ОС, что в общем хорошая вещь, но в данном случае это препятствует обнаружения перспективных изменений во время прокрутки. Чтобы исправить это Вы можете добавить элемент с позиционированием fixed, как это выглядит в переходе Edge к методу обработки без ОС, что гарантирует учет перспективных изменений.
  • Контент страницы просто огромен! Многие браузеры не берут в расчет масштаб при решении на сколько большой контент страницы, но к сожалению Chrome и Safari не учитывают перспективы. Поэтому если скажем применить масштаб 3Х к элементу, мы можем увидеть полосы прокрутки и тому подобное, даже при масштабе 1Х после применения perspective. Есть возможность обойти эту проблему масштабируя элементы относительно правого нижнего угла (transform-origin: bottom right). Это работает благодаря тому, что негабаритные элементы перерастут в “отрицательные области” (обычно с права с верху) относительно прокуриваемой области; Прокручиваемые области никогда не покажут либо не прокрутят контент до отрицательной области.

Заключение

Параллакс это забавный эффект если использовать его вдумчиво. Как Вы видите это можно реализовать таким образом, чтобы было произвотдельно, скролл зависимо и кроссбраузерно. Поскольку это требует небольшого математического выверта, а также небольшого шаблона для получения желаемого эффекта, мы подключим небольшую вспомогательную библиотеку и образец, которые вы можете найти в нашем репозитории GitHub - примеры UI элементов.

Автор: Paul Lewis и Robert Flack

Оригинал: https://developers.google.com/web/updates/2016/12/performant-parallaxing