Пользовательский параллакс без какихлибо библиотек

Пользовательский параллакс без каких-либо библиотек

В предыдущих уроках этой серии мы рассмотрели множество вариантов создания параллаксов. Хотя некоторые из них были кастомными и не имели библиотек, они были довольно простыми. Здесь мы раскроем весь потенциал настраиваемых параллаксов..

Фактически, это первый пример кода, который я написал. Однако я решил, что сначала нужно заложить какой-то фундамент (всей этой серией). Как только он у нас есть, давайте продолжим пояснительные примечания здесь.

Демонстрация настраиваемого параллакса

В отличие от предыдущих уроков — давайте сначала посмотрим на саму демонстрацию: https://magadanskiuchen.github.io/weather-parallax-demo/.

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

Вы можете увидеть источник здесь: https://github.com/magadanskiuchen/weather-parallax-demo (вместе с относительно дружественным журналом фиксации).

Хотя это не эстетично, мы демонстрируем:

  • преобразовать: повернуть
  • преобразовать: translateX
  • непрозрачность

которые плавно меняются / анимируются при прокрутке пользователем.

Основы

В моем уроке Parallax Text Reveal я уже поделился ссылкой на статью из Medium, но я не дал ей полной оценки. Речь идет о Parallax Done Right, Дэйв Гамаш. Из этой статьи я узнал несколько основных принципов. Некоторые из них совпадали с утверждениями, которые были мне уже знакомы, но в целом вдохновляли меня на подход с точки зрения кода..

Продуктивность

Конечно, огромное внимание уделяется производительности. Пользовательская анимация параллакса не может быть красивой, если она не работает плавно.

Если мы упадем ниже естественных 60 кадров в секунду для потребителей, то то, что мы делаем, теряет смысл..

GPU

По этой причине настоятельно рекомендуется анимировать только те свойства CSS, которые обрабатываются видеокартой (поскольку процессор занят другой работой). Кто не знает — это трансформация (со всеми ее подсвойствами, такими как перевод, масштаб, поворот, матрица, перекос и их параметры) и непрозрачность..

На уроках серии GSAP мы позволяли себе анимировать других, но рисковали потерять частоту кадров..

requestAnimationFrame

Следующее, что важно для продуктивности, — не делать лишних вычислений..

После того, как у нас есть опыт прокрутки, нам нужно знать, что она и события изменения размера являются самыми тяжелыми с точки зрения ресурсов ЦП. В основном это связано с тем, что их вызывают очень часто. Много раз в секунду. Даже во многих случаях более 60 раз в секунду.

Погоня за 60 кадрами в секунду, это означает, что иногда мы думаем о том, как должен выглядеть элемент, и еще до того, как мы его покажем, мы думаем о том, как он должен выглядеть на основе новых данных из события прокрутки / изменения размера. Это не нужно.

Всегда оборачивайте тело обработчика событий для прокрутки и изменения размера в requestAnimationFrame.

Это касается не только настраиваемых параллаксов. Просто прими это как должное.

Целые числа, а не дроби

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

Только возможно, при анимации непрозрачности мы сможем использовать десятичную дробь до 2 знаков после запятой. Другими словами, использовать десятичную дробь вместо обычного числа с плавающей запятой, хотя в качестве типа переменной в JavaScript это не имеет значения. Однако работа с менее значащими числами упростит процессор. И мы следуем этому принципу в каждом действии. Будьте фанатичны в этом вопросе.

Анимируйте только элементы с абсолютным или фиксированным положением

Когда элементы не в position: absolute / fixed, при их изменении браузер должен полностью перерисовать все остальные элементы на странице. Это связано с тем, что элементы с position: static / relative смещаются при изменении, и это требует больших ресурсов. Очень возможно падение частоты кадров на 30 кадров в секунду..

Читать также:  Активное состояние меню блога при чтении сообщения блога

Именно в учебнике Parallax Text Reveal мы увидели, что приводит к анимации элемента с помощью position: relative..

Читаемость кода и обслуживание

Я бы не стал много думать об этом и о большинстве эффектов, иначе я бы просто вставил их где-нибудь в коде. Даже половина меня, вероятно, была бы в CSS, а остальная часть в JS.

Однако Дэйв рекомендует создать объект со значениями, от которых и до которых мы хотим анимировать. Таким образом, мы можем видеть, что происходит, почти как временная шкала. И не только нас самих, но и других людей, которым может понадобиться прочитать наш код..

Шаг за шагом

Теперь, когда мы рассмотрели общую теорию того, что делать хорошо, а что нет, давайте посмотрим на пример..

Хотя я следую обязательствам GitHub в примечаниях ниже, это не означает, что у меня будут заметки для всех. Например, я полностью пропускаю фиксацию лицензии, потому что это не важно для целей урока..

начало

Хронология анимации

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

ключевые кадры const = [{селектор: ‘.fa-sun’, начало: 0, конец: ‘150%’, свойство: ‘вращать’, от: 0, до: 360}];

Естественно, мы начинаем с массива, а в нем — объектов (изначально всего 1), которые содержат информацию об элементах, которые мы будем анимировать..

У нас есть:

  • селектор (какой элемент мы будем анимировать),
  • начало и конец, которые указывают от «где» до «где» при прокрутке страницы мы изменим некоторые свойства CSS.
  • это само свойство CSS
  • от и до значений, которые элемент будет иметь в начале и конце «точек» соответственно

Инициализация и отзывчивость

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

функция init () {absoluteKeyframes = relativeToAbsolute (ключевые кадры); }
функция relativeToAbsolute (кадры) {const absolute = [];
for (const i в кадрах) {let obj = {};
for (const j in frames [i]) {if (frames [i] [j] .toString (). match (/% $ /)) {obj [j] = parseInt (frames [i] [j]) * window.innerHeight * 0,01; } еще {объект [j] = кадры [я] [j]; }}
absolute.push (объект); }
вернуть абсолютный; }

Мы вызываем функцию init () и изменяем размер, чтобы пересчитать новые точки на основе исходных процентов:

window.addEventListener (‘изменение размера’, e => {requestAnimationFrame ((= = {{init ();});});

Место для свитка

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

Поэтому мы вычисляем, какой высоте документа соответствует конечное значение последнего ключевого кадра:

document.body.style.height = Math.max.apply (Math, absoluteKeyframes.map (object => object.end)) + ‘px’;

Интерполяция анимации

Когда у нас есть значения для начала и конца, мы сможем вычислить все, что находится между ними..

Мы пишем функцию, которая позаботится об этом:

window.addEventListener (‘scroll’, e => {requestAnimationFrame () => {for (frame of absoluteKeyframes) {const element = document.querySelector (frame.selector);
if (frame.start window.scrollY frame.end> window.scrollY) {const scrollProgress = (window.scrollY — frame.start) / (frame.end — window.scrollY);
переключатель (frame.property) {case ‘rotate’: element.style.transform = `rotate ($ {parseInt (frame.to * scrollProgress)} deg)`; перерыв; }}}}); });

При каждом событии прокрутки мы должны обходить каждый ключевой кадр (объект из исходного массива).

Затем мы проверяем, есть ли у этого объекта анимация в диапазоне текущей прокрутки. Например — если нам нужно изменить непрозрачность с 0 на 1 после прокрутки на 1000 пикселей, нет смысла что-либо вычислять, если мы все еще находимся на прокрутке 200 пикселей..

Читать также:  Доступность в HTML

В противном случае мы вычисляем, какой процент ключевого кадра (после начала и до конца) мы достигли.

Примеры:

  • scroll 100px составляет 50% в начале = 0 и конце = 200
  • scroll 100px составляет 10% в начале = 0 и конце = 1000
  • прокрутка 600 пикселей составляет 75% в начале = 300 и конце = 700

Затем у нас есть переключатель, в котором мы проверяем, какое свойство CSS мы должны анимировать, потому что для разных типов может потребоваться разная обработка..

Поскольку в первом ключевом кадре мы анимируем вращение, пока мы описали это только в переключателе.

Первые исправления

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

Затем мы добавляем дополнительные элементы на временную шкалу и обработку translateX, а также translateY.

Изменения на временной шкале

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

Не только это, но и предыдущая ситуация позволяла анимировать только 1 свойство для каждого объекта за один раз. И если бы мы описали один и тот же элемент 2 раза и, таким образом, попытались бы оживить эти 2 свойства, возникли бы другие проблемы. Это было бы излишне многословным и потребовало бы еще более сложной обработки, чтобы переопределение не затирало первое, а дополняло его..

Новый синтаксис:

const keyframes = {‘.fa-sun’: {‘rotate’: {0: 0, ‘100%’: 180}, ‘opacity’: {0: 0, ‘20% ‘: 1, ‘80%’: 1, ‘100%’: 0}},
‘.fa-cloud’: {‘translateX’: {0: -150, ‘50% ‘: -150, ‘85%’: 0}, ‘opacity’: {0: 0, ‘50% ‘: 0, ‘75% ‘: 1}}};

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

Например, мы говорим, что солнце вращается на 180 градусов для 100% прокрутки (100% высоты монитора / области просмотра)..

Синтаксис очень похож на описание linear-gradient в CSS..

При таком способе описания нам не нужно следить, какая анимация должна происходить, после чего.

Естественно, оттуда потребуются существенные изменения в обработчике прокрутки:

window.addEventListener (‘прокрутка’, e => {requestAnimationFrame (() => {const animations = {};
for (константный селектор в absoluteKeyframes) {анимации [селектор] = анимации [селектор] || {};
for (константная опора в absoluteKeyframes [селектор]) {константа fromScroll = getBiggestOfSmallerThan (Object.keys (absoluteKeyframes [селектор] [опора]), window.scrollY); const toScroll = getSmallestOfBiggerThan (Object.keys (absoluteKeyframes [селектор] [опора]), window.scrollY);
const fromValue = absoluteKeyframes [селектор] [опора] [fromScroll]; const toValue = absoluteKeyframes [селектор] [опора] [toScroll];
пусть значение = fromValue;
если (fromValue! == toValue) {const scrollProgress = (window.scrollY — fromScroll) / (toScroll — fromScroll);
значение = (toValue — fromValue) * scrollProgress + fromValue; }
switch (prop) {case ‘translateX’: case ‘translateY’: анимации [селектор] .transform = анимации [селектор] .transform || []; анимации [селектор] .transform.push (`$ {prop} ($ {value}%)`); перерыв; case ‘rotate’: анимации [селектор] .transform = анимации [селектор] .transform || []; анимации [селектор] .transform.push (`rotate ($ {value} deg)`); перерыв; case ‘opacity’: анимация [селектор] .opacity = (значение) .toFixed (2); перерыв; }}}
for (константный селектор в анимации) {константный элемент = document.querySelector (селектор);
for (свойство const в анимации [селектор]) {element.style [свойство] = (typeof (анимации [селектор] [свойство]) == ‘объект’)? анимации [селектор] [свойство] .join (»): анимации [селектор] [свойство]; }}}); });

«Наименьший меньше» и «Наименее большой»

Здесь мы также представляем функции getBiggestOfSmallerThan и getSmallestOfBiggerThan. Их цель — определить, между какой парой прогресса определения временной шкалы мы находимся..

Читать также:  Секундомер JavaScript Часть 1

Например, чтобы знать, что нам нужно анимировать солнце под углом 90 градусов, нам нужно знать, что мы находимся на 50% прогрессе от поворота его анимации. Мы определяем это, зная, в какой прокрутке мы находимся, и проверяем, между какими двумя значениями прогресса определения мы находимся. Тогда наш «старт» будет самым большим, меньшим, чем текущая прокрутка, а наш «стоп» — самым маленьким, большим, чем тот же самый.

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

Определение анимации

После того, как мы очистили функциональную логику, вы можете увидеть, как мы быстро и легко добавляем кучу анимаций всего в одну фиксацию..

Что я не полностью согласен с Дэйвом Гамашем

В своей статье он пишет, что использование функций ослабления практически обязательно..

Хотя во многих ситуациях наличие ослабления дает ощущение гораздо более естественного движения, в случае прокрутки это субъективно, хорошо или необходимо ли его использовать..

На https://magadanskiuchen.github.io/weather-parallax-demo/ вы видите настраиваемый параллакс, в котором нет такого ослабления и который, я не думаю, выглядит плохо. По крайней мере, в плане анимации; в противном случае с эстетической точки зрения мы сказали, что это некрасиво.

Преимущества и недостатки

Чем этот пользовательский JS параллакса, который мы здесь написали, лучше, чем предыдущие несколько уроков (особенно те, которые используют ScrollMagic и GSAP)?

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

Наша альтернатива действительно мала, мы можем включить ее в JS, который мы все равно отправляем посетителям, а парсинг JS из браузера смехотворно мал. Скрипт Evaluate в Chrome занимает 0,43 мс. Для сравнения — разбор HTML на странице занимает в 10-12 раз больше времени! Оценка jQuery (если он где-то есть) занимает в 40 раз больше времени.

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

Первое упущение, которое я знаю, является существенным и доступно в этом руководстве, — это возможность анимировать сразу несколько свойств преобразования. На данный момент, если нам нужно сделать translateX, translateY, rotate или что-то подобное одновременно — это не сработает..

Для этого библиотеки, подобные GSAP, сначала обрабатывают указанные вами значения и используют их для вычисления матрицы, которая применяется к элементу transform: matrix. Не то чтобы это ядерная физика, и мы не можем расширить наш код, чтобы добавить его, но я не уверен, стоит ли оно того..

В заключение о параллаксах

Поэтому в заключение серии могу сказать:

  1. для самых простых вещей просто используйте background-position: fixed
  2. для отображения элемента при прокрутке к нему — используйте AOS.
  3. для элементарной анимации параллакса (например, избранного изображения, которое вы видите в верхней части статьи) вы можете добавить 2-3 строки пользовательского JS
  4. для всего более сложного — используйте ScrollMagic и GSAP.
Понравилась статья? Поделиться с друзьями:
Что нужно знать пользователю?