Параллакс на чистом CSS

В этой статье показывается, как с помощью 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»)
Share post

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 46

    0
    В Хроме 37.0.2062.94 не работают демки.
    В последних FF/Safari — все ок.
      +1
      >Версия 37.0.2062.102 m
      Всё работает
      0
      Хром 37.0.2062.94 mac os, тоже не работает
      0
      Глупый вопрос — А в чем подвох?
      Не бывает же все так замечательно.
      • UFO just landed and posted this here
        +2
        Chrome, OSX не работает.
        image
          +1
          Хороший пример: красивый сайт с эффектом паралакса — может, кто не в курсе, что это, как я…
            0
            Кстати, пример из статьи работает в Safari, но сильно грузит процессор — не круто.
              0
              У меня не грузит процессор в сафари
              Все чисто и спокойно
                0
                Хром. Грузит заметно.
              • UFO just landed and posted this here
              0
              у меня замечательно в chrome 37.0.2062.94 на os x 10.9.4
              +1
              Хочу заметить, что зачастую JS быстрее и менее ресурсоёмок, нежели CSS3. Особо ощущается на мобильных телефонах.
                +3
                Как раз-таки наоборот. Css3-трансформации работают как будто это нативное приложение, для них есть аппаратная поддержка, а для Js-анимаций ничего нет, оно просто работает как есть.
                  +3
                  Sony Ericsson Xperia Neo — простая анимация позиции (left) влево-вправо на JS работает в разы быстрее, нежели на CSS3 animations, в тоже время Google Nexus (какой-то, циферку не назову) — показывает прямо противоположные результаты. Моё дело предупредить о том, что бы не считали CSS3 панацеей от всех бед.

                  Заодно хочу заметить (небольшой оффтоп, раз уж пошла речь), что избыток всяких transition, transform, border-radius, rgba (особенно), box-shadow (ещё особенней), linear-gradient и проч. напрягает, подозреваю что любой мобильный девайс похлеще, нежели 10к+ строк JS кода (проверялось на iphone, ipad, galaxy tab 10.1 (вроде) и озвученных выше двух устройствах) =)
                    +7
                    на iOS девайсах, например, отслеживать изменения скролла сложнее, тк пока палец прижат к экрану, скрипты висят, CSS не вызывает таких проблем, работает отлично
                    +2
                    Ну вы загнули конечно, что в js ничего нет — взять хотя бы requestAnimationFrame.
                    И потом все от задачи зависит — в простых css, в сложных js.
                      0
                      И нет и да. Все зависит от того, будете ли вы использовать tarnsform, и будете ли вы использовать аппаратное ускорение, например сделав translateZ(0).
                      А тормозят анимации иногда, как мне кажется, из-за ограничений видеопамяти или чего-то такого.
                      Вся проблема анимаций на JS — синхронизация рендеринга и чтения состояний. Кстати, проблема эта решена в velocity.js.
                        0
                        Здесь немного подробней про производительность анимации при помощи только стилей и с помощью скриптов.
                        Если коротко, многое зависит от скорости фреймворка (если он используется) и способа анимирования скриптом.
                        Вот такое сравнение есть, например.
                        0
                        Согласен. Сталкивался с, как оказалось, известной проблемой когда на IOS девайсах «падает» браузер из-за CCS анимации (transition). Пришлось все на JS переписывать.
                      • UFO just landed and posted this here
                          +1
                          У меня тоже win8, хром, тестировал не в «metro», все отлично скролится, кликать не нужно — скрол идет по скролу, а не по клику. Текст на слое foreground и base выделяется, не выделяется текст на бекграунде, но как я понимаю там должна быть картинка.
                          • UFO just landed and posted this here
                              0
                              Могу предположить, что это из-за аппаратного ускорения. У нас на работе была почти паника с 36 хромом под виндовс, когда большие по размеру картинки, уменьшенные с помощью css и выровненные по центру блока, находящиеся в блоке со скоролом, при скролинги вызывали моргание и пропадания всего со страницы. Причём с той же версией под линукс было всё норм. С выходом 37 — всё вернулось на свои места.
                                0
                                Может быть дело в версии хрома? У меня стоит 39.0.2138.3 (64-bit).

                                На моем компе без фокуса скролится, открываю страницу — кручу колесико мышки и все работает.
                            0
                            На айпеде работает в режиме реального времени, то есть во время скроллинга, чего не добиться жс параллаксом. Это оч круто!
                              +1
                              Однако перестала работать инерционная система промотки.
                                –2
                                жс можно добиться почти всего
                                  0
                                  Нет, на джаваскрипте на iOS невозможно заставить репейнтить после того, как пользователь отпустил экран, и система продолжает иннерционно доскраливать. Если считаете, что можно, то пруфы в студию.
                                    0
                                    onTouchStart + onTouchEnd + requestAnimationFrame на webkitTransform
                                      0
                                      На жс можно свою кинетическую прокрутку сделать как в картах гугла и яндекса.
                                      И тогда будет контроль над ререндером и рефлоу.
                                        0
                                        Да, можно, но это велосипед со всеми вытекающими оттуда последствиями. Как минимум почти невозможно, чтоб скроллинг вел себя так же хорошо, как нативный, к тому же фпс будет не фонтан. Это ок в картах, но ставить такое на роль скроллера страницы я бы не стал. А сабж таки при системном скроллинге работает.
                                +1
                                А если зажать центральную кнопку мыши и потянуть вбок, будет вот такой забавный эффект:
                                screencloud.net/v/260i
                                  +3
                                  Опция debug в демке выглядит эффектно, обязательно попробуйте.
                                    +5
                                    Огромное спасибо за кнопку дебаг, наконец-то понял как работает паралакс)
                                      0
                                      FF31: скроллинг тормозит, но эффект есть.
                                        0
                                        Ах, тормозит только последнее, с группами.
                                        +1
                                        В дополнение: не работает в Опере 12.16 Linux.

                                        (Предвосхищая предложения перейти на более новую версию, скажу, что 12 — последняя для Линукса.)
                                          –2
                                          Для Windows ситуация не сильно лучше — 12.17 последняя Опера. Дальше уже она Блинк или Хром или как там… А многие пользователи принципиально не хотят переходить на новую версию.
                                            +1
                                            Вы серьезно решили объяснить «проблему» Opera? Здесь, на Хабре? ;)
                                            Или это тонкая ирония какая-то?
                                            +1
                                              0
                                              Оу, эту тему я пропустила. Спасибо!
                                            0
                                            На Мавериксе все отлично везде работает под все браузеры, какой айпад, какой линукс и мобильные гаджеты. Я уже молчу про андроид с режимом экономии. И про зоопарк браузеров с типо долфином.
                                            Вы о чем? Давайте няшности и красивости оставлять дэсктопам, а адаптивка будет показывать контент юзеру в удобо читаемом виде. Версия попроще без ресурсоемких операций, пускай даже обрабатываемых аппаратно.

                                            Спасибо большое за перевод, сегодня в офисе буду играться
                                              0
                                              Chrome M37 M38 под Mac OS X, не работает.
                                              Причина — code.google.com/p/chromium/issues/detail?id=395258
                                                0
                                                P.S. Возможно обусловлено разрешением Retina, уже встречал два бага в Chrome, которые проявляются только на Retina.

                                              Only users with full accounts can post comments. Log in, please.