Рисунок используемый для parallax эффекта. Автор Patryk Zabielski
Привет друзья, я покажу вам как создать простою многослойную иллюстрацию с глубиной, которая переходит к контенту. Мы будем использовать метод, в котором необходим только css и чистый JS(coffeescript) (Никаких jQuery!).
Этот урок для начинающих, с начальным знанием JS и CSS, так что я буду объяснять большинство вещей и ссылаться на внешние источники.
Финальное демо
Подготовим илюстрацию
Давайте начнем с того, что нарежем иллюстрацию на слои. Лучшим вариантом развития событий будет, то что вы сами нарисовали или у вас есть доступ к исходному файлу со всеми слоями.
Если нет, то не волнуйтесь мы разберемся с этим.
Я буду работать с картиной, которую нарисовал в прошлом году. Если у вас нет возможности создать свою, мы можете скачать мои исходники перед тем, как мы начнем.
Визуализированный концепт слоев в 3D пространстве
Нам нужно разделить картину на несколько png файлов с прозрачным фоном, что даст нам возможность создать чувство глубины.
Слои на заднем плане будут двигаться медленнее, чем те, что на переднем, что даст нам эффект глубины.
Еще одна вещь, которая даст нам лучший переход к следующей секции — это нарисовать внизу картинки линию такого же цвета, как фон страницы. Я добавил незаметную линию на переднем фоне внизу картинки.
На картинке справа есть незаметная линия, которая даст нам лучший переход в следующую секцию
Начнем кодить
Подготовление
Что вам потребуется:
- Свежий проект в Codepen (Если вы хотите следовать коду ниже, то не забудьте изменить HTML на HAML, CSS на SCSS и JS на Coffeescript, в настройках Codepen)
- Начальные знания HAML & Sass(SCSS)
- Базовое понимание Javascript и Coffeescript (Если у вас все еще плохо с этим — я рекомендую «Javascript & jQuery” by Jon Duckett) Если вам не хочется работать с Coffeescript, то вы можете автоматически конвертировать его в JS и вставить фрагменты кода ниже чтобы получить чистый JS
- Ваши иллюстрация/фото разделенная на несколько png слоев
Давайте начнем с HTML структуры. Мы сделаем контейнер-родитель и дадим ему ID 'hero'. Потом мы добавим несколько div, с классом 'layer' и data-type атрибутом со значением 'parallax'.
#hero
.layer{“data-type” => “parallax”}
.layer{“data-type” => “parallax”}
.layer{“data-type” => “parallax”}
.layer{“data-type” => “parallax”}
.layer{“data-type” => “parallax”}
Добавим базовых стилей. Начнем с #hero. Я установил высоту иллюстрации 800px
#hero {
height: 800px;
overflow: hidden;
position: relative;
}
Теперь, перейдем к стилизации повторяющего класса слоев. У каждого из них будет одинаковая высота, как у #hero и позиционирование, и мы добавим position: fixed
.layer {
background-position: bottom center;
background-size: auto;
background-repeat: no-repeat;
width: 100%;
height: 800px;
position: fixed;
z-index: -1;
}
Следующая вещь, которую мы хотим сделать это добавить картинки, которая мы подготовили до этого. Мы создадим еще несколько классов, по одному для каждого слоя. После чего добавим ссылку на картинку внутри свойства background-image.
.layer-01 {
background-image: url('ссылка на картинку');
}
.layer-02 {
background-image: url('ссылка на картинку');
}
// и т.д.
Не забудьте обновить HTML и назначить классы к нужным div в нашем порядке, первым слой это задний фон, следующие слои будут накладывать поверх друг-друга
#hero
.layer.layer-01{“data-type” => “parallax”}
.layer.layer-02{“data-type” => “parallax”}
.layer.layer-03{“data-type” => “parallax”}
.layer.layer-04{“data-type” => “parallax”}
.layer.layer-05{“data-type” => “parallax”}
Время JavaScript
Добавим методом, который будет проверить если пользователь начал прокручивать страницу вниз.
window.addEventListener ‘scroll’, (event)
EventTarget.addEventListener() метод регистрирует слушателя на EventTarget. event target может быть элемент в document, сам document, объект Window или любой объект поддерживающий события (такие как XMLHttpRequest)
Сохраним количество пикселей, которое проскролено вертикально от начала страницы в переменную topDistance. Чтобы сделать это, будем использовать свойство pageYOffset
window.addEventListener ‘scroll’, (event) ->
topDistance = @pageYOffset
После этого, мы выберем все слои в нашей илюстрации и сохраним их в переменную layers. Для этого мы будем использоваться методом querySelectorAll и data атрибут внутри элементов HTML, который мы установили до этого.
window.addEventListener ‘scroll’, (event) ->
topDistance = @pageYOffset
layers = document.querySelectorAll("[data-type='parallax']")
Следующее что мы сделаем, это пройдемся циклом через все слои и применим свойство transform к каждому из них. Но перед этим, мы укажем еще одну вещь внутри нашего HTML файла, атрибут data-depth. Он даст нам возможность контролировать как быстро элемент будет двигаться, давайте не будем глубоко углубляться в значения, мы вернемся к этому позже.
#hero
.layer.layer-01{“data-type” => “parallax”, "data-depth" => "0.10"}
.layer.layer-02{“data-type” => “parallax”, "data-depth" => "0.20"}
.layer.layer-03{“data-type” => “parallax”, "data-depth" => "0.50"}
.layer.layer-04{“data-type” => “parallax”, "data-depth" => "0.80"}
.layer.layer-05{“data-type” => “parallax”, "data-depth" => "1.00"}
Чтобы пройтись через все элементы, мы будем использоваться цикл for. Начнем наш цикл с создания переменной, где мы будем хранить наши слои. После чего возьмем значение из data-depth атрибута, который мы указали внутри нашего HTML. Далее, мы вычислим передвижение слоев, умножив дистанцию скрола от начала страницы на значение атрибута data-depth данного слоя. Элемент со значение 1.0 будет двигаться нормально с остальной страницей, вы можете воспринимать его, как элемент с выключенным parallax.
Последнее что мы сделаем, это обновим окончательное значение движения для параметра transform translate3d слоя, чтобы сделать это мы будем использовать свойство style вместе со всеми вендорными префиксами для transform.
Чтобы код был более читаем и DRY, мы сохраним параметр translate3d в переменную translate3d
for layer in layers
depth = layer.getAttribute(‘data-depth’)
movement = -(topDistance * depth)
translate3d = 'translate3d(0, ' + movement + 'px, 0)'
layer.style['-webkit-transform'] = translate3d
layer.style['-moz-transform'] = translate3d
layer.style['-ms-transform'] = translate3d
layer.style['-o-transform'] = translate3d
layer.style.transform = translate3d
Теперь когда мы указали data-depth=»1.00" элемент будет двигаться вместе со страницей, как обычный элемент без parallax эффекта. Все значение меньше чем 100 будут иметь эффект parallax.
Телефоны
Для телефонов мы отключим parallax версию и заменим ее на статичную картинку, чтобы сохранить производительность. Чтобы сделать это, создадим новый div ниже нашего div #hero, с id hero-mobile и применим к нему display: none вместе с параметрами background и height.
#hero-mobile {
display: none;
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/272781/full_illustration.png") no-repeat center bottom / cover;
height: 320px;
}
Чтобы показывать его вместо parallax, будем использовать media query и применять параметр display: none к десктопной версии, переопределяя наш display: none на display: block для #hero-mobile.
@media all and (max-width: 640px) {
#hero {
display: none;
}
#hero-mobile {
display: block;
}
}
Финальный код
Комментарии (23)
dshster
02.04.2016 21:13+7Интересно! Но может уже пора слазить с CoffeScript в угоду того же ES6, иначе уже как-то дико код выглядит? И, я так понимаю, ко всему этому прелоадер нужен будет, а то послойная загрузка картиное не очень красиво смотрится.
Alexufo
02.04.2016 23:32+6Меня смущает только одно, автор не видел, что размер его слоев по полтора мегабайта каждый?
Вроде бы давно не секрет, что нельзя большие картинки из фотошопа сохранять в png24 без последующего пережатия той же pandapng.
Берем https://s3-us-west-2.amazonaws.com/s.cdpn.io/272781/ilu_03.png
Итог: -80%!
Было 1.3 MB стало 256.2 KB. И вроде жить можно.FrozenInternet
03.04.2016 10:20+2Да, да. Как-то уж больно долго демка картинки грузит.
Конкретно к автору. Уже после «Подготовим илюстрация» начали возникать вопросы. Вы хоть раз прочитали статью, если вы ее переводили? Такое ощущение, будто после google transtate-а ее ник-то не ревьюил.
Теперь по поводу статьи. Как я понял, статья рассчитана на новичков. Почему тогда coffescript, sass (хотя больше обычного css я тут не увидел) и тем более HAML? Странно это, учитывая то, что вначале статьи обещали «чистый css и js». :)meam
03.04.2016 10:22-1Опечатки принято отправлять в личные сообщения. 2 часть это претензия к автору, для меня это тоже большой вопрос, но он обещал лишь чистый js без jQuery
dbanet
03.04.2016 14:35+2Всем и так известно, что опечатки принято отправлять в личных сообщениях.
Наверное, если FrozenInternet сообщил о них не в ЛС, а в комментариях, он хотел этим что-то сказать?
А, да нет, тут даже ни о чём думать надо: он прямым текстом сообщает вам:Вы хоть раз прочитали статью, если вы ее переводили? Такое ощущение, будто после google transtate-а ее ник-то не ревьюил.
, что с вашей статьёй что-то сильно не так, и в таком виде ей тут не место.
По ощущениям, только херовые переводчики размахивают ссаными "опечатки-в-ЛС" и "это-не-ко-мне-это-к-автору". И публика жрёт всё: статьи всё равно в плюсе. Самоуправление работать перестало. Впрочем, той самой публики и так осталась десятая часть.
dom1n1k
03.04.2016 15:06-2ФШ жмет нормально, обычно после него дополнительные оптимизации без потери качества не превышают нескольких процентов. Если -80%, то это совершенно точно с потерей. Для картинки из примера 24-битный цвет, конечно, избыточен — но никто не мешает и в самом ФШ выбрать png8.
Alexufo
03.04.2016 16:13+1У photoshop нет возможности сохранять png8 с полутоновой маской как у png24, она у него битовая. А битовая маска прозрачности равносильна тому что прозрачности как таковой нет вообще — она бесполезна. png24 формат lossless — для веба кране неудобный при больших картинках — сами видите 1.3mb при 600px на 600px это капец. PNG panda делает png8 но с полутоновой маской прозрачности.
Что зачастую КУДА важнее, чем наличие truecolor.dom1n1k
03.04.2016 16:24+2Офигеть, и правда.
Я всегда пользовался 32 битами + pngout, который переводит картинку в 8 бит, если это не ведет к потере качества, а иначе сохраняет 32.
PavlovM
03.04.2016 07:22+3Этот урок для начинающих, с начальным знанием JS и CSS
И тут же: HAML, SCSS, Coffeescript.
Все таки для начинающих нужно писать на чистом HTML, чистом CSS и чистом JS.
Тем более, код простой и никакой существенной выгоды от использования вышеперечисленного — нет.
Хотя, конечно, это скорее претензия к автору исходной статьи.
Tenebrius
03.04.2016 17:45-2А обязательно ли использовать JS? Ведь можно сделать параллакс без него.
Например: habrahabr.ru/post/235531
asv2001
03.04.2016 17:46Я наверное не стал использовать бы translate3d, если есть translateY или просто translate. Насколько я знаю translate3d не очень хорошо работает на iOS 5 (совсем не работает?), но при этом translate и translateY работают хорошо и во всех браузерах.
З.Ы. Если вы пользуете translate3d для получения ускорения, тоже самое произойдет и при использовании translate и translateY, т.к. transform принуждает браузер включить аппаратное ускорение.
Спасибо за пример!Xao
04.04.2016 09:28+1Зато translate3d включает аппаратное ускорение, хак такой. Хотя я точно не могу сказать, но слышал что-то подобное.
SerafimArts
04.04.2016 10:44Так и есть. Под мобилки как раз и используется (использовался раньше) хак для включения ускорения, вида: translate3d(0, 0, 0). Потом вроде бы включили аппаратное ускорение по-умолчанию, так что он перестал быть особо нужен, но я не уверен в этом.
asv2001
04.04.2016 13:22да, Вы все верно говорите. Я о том же, но апаратное ускорение включает также простое наличие transform. Излишнее использование translateZ может привести к проблемам описанным по ссылке: https://plus.google.com/+NatDuca/posts/BvMgvdnBvaQ?e=-RedirectToSandbox
phoenixweiss
03.04.2016 23:10+1тутор начального уровня смотрится странно в 2016-м. Уж очень странный материал для перевода выбрали. Кроме того, уже писали что "начинающий" не поймет почему такой "яваскрипт" не работает.
В общем, тех кто знает про Coffee параллаксом не удивишь, а новичков в тупик явно этим "уроком" поставите. Для остальных, как мне кажется, объем и ценность материала все же не совсем для уровня хабра, скорее для личного блога.
acupofspirt
В хроме(51.0.2697.0 canary) при скролле мышкой коряво выглядит.