Найти в Дзене
Roma Lavrik

Пишем слайдер без библиотек. JavaScript. Часть 2. Touch Events.

Решил написать вторую часть про создание слайдера без библиотек. Для того, чтобы понимать, что здесь происходит нужно просмотреть первую часть. А еще лучше просмотреть код. Если в первая статья была для новичков, то для чтения этой части нужен более продвинутый уровень. Будет много кода. Мы будем менять и переписывать это. Также как и в прошлый раз, наливаем себе чаек☕ и начинаем. Ссылка на новую рабочую версию в конце статьи. Советую читать код по порядку. Я его разделил на этапы. Пререквизиты В предыдущей статье мы написали простой слайдер с переключением по кнопкам. Ссылка на код здесь. Но, обычно, слайдеры еще имеют прокрутку при касании или нажатии на него мышью. Для касаний на телефоне у нас есть такие события. Для начала, давайте определимся с функционалом. Двигаем мы слайдер с помощью transform: translateX. Сам алгоритм работы такой: В коде алгоритм будет понятнее. Почему мы отслеживаем прокрутку не до половины экрана, а делим на 7 частей? На телефоне мы привыкли прокручивать
Оглавление

Решил написать вторую часть про создание слайдера без библиотек. Для того, чтобы понимать, что здесь происходит нужно просмотреть первую часть. А еще лучше просмотреть код. Если в первая статья была для новичков, то для чтения этой части нужен более продвинутый уровень. Будет много кода. Мы будем менять и переписывать это. Также как и в прошлый раз, наливаем себе чаек☕ и начинаем. Ссылка на новую рабочую версию в конце статьи. Советую читать код по порядку. Я его разделил на этапы.

Пререквизиты

  • Понимание что такое ООП(геттеры, сеттеры, приватные свойства).
  • Понимание, что такое события в JavaScript.
  • Умение читать чужой код.

В предыдущей статье мы написали простой слайдер с переключением по кнопкам. Ссылка на код здесь. Но, обычно, слайдеры еще имеют прокрутку при касании или нажатии на него мышью.

Прокрутка по касанию
Прокрутка по касанию

Для касаний на телефоне у нас есть такие события.

  • touchstart
  • touchmove
  • touchend

Для начала, давайте определимся с функционалом. Двигаем мы слайдер с помощью transform: translateX. Сам алгоритм работы такой:

  1. На событие touchstart отслеживаем точку, куда мы коснулись.
  2. На событие touchmove, в зависимости в какую сторону мы двигаем палец, прибавляем или отнимаем у переменной position значение. touchStartX(начальная точка, куда мы коснулись) - currentX(текущая позиция пальца или мышки). В виде псевдокода это так: position = position + (touchStartX - currentX).
  3. На событие touchEnd смотрим: если position > sliderElemWidth / 7, тогда докручиваем до конца. Если меньше возвращаем переменной position значение, с которого мы начинали крутить.

В коде алгоритм будет понятнее. Почему мы отслеживаем прокрутку не до половины экрана, а делим на 7 частей?

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

C архитектурной точки зрения, слайдер - это отдельная сущность, которая может быть на разных страницах. Поэтому прежде, чем начнем писать логику, отойдем от безобразного стиля к ООП.

Переименуем index.js в slider.js. Создадим приватные свойства, опишем конструктор, добавим обработку событий и напишем название методов.

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

  • debounce - это функция, которая выполняет переданную ей функцию, не чаще чем в указанное кол-во секунд. Обычно, используется при scroll событиях и поисках с запросом на сервер. В нашем случае, если пользователь нажимает на стрелки быстрее, чем проходит анимация пролистывания(300ms), то смена активного индекса отработает быстрее анимации. И визуально, активный элемент будет не соответствовать выбранному слайду. Поэтому мы оборачиваем функции в this.onRightBtnClick и this.onLeftBtnClick в debounce c задержкой в 300ms
  • Мы добавили обработчик события window.resize. Это нужно для корректного отображения слайдера, если мы перейдем в портретную версию экрана. Из-за того, что на портретной версии ширина изменилась, а при открытии странице она была меньше, мы увидим баг с пролистыванием. Нам нужно будет обновить свойство sliderElemWidth и поставить его снова в transform: translateX.
  • startingPosition - это начальная позиция transform: translateX, с которой мы начнем листать. Она будет нужна для момента, когда пользователь не дотянул слайд до 1/7 экрана. Тогда у нас нет пролистывания и мы возвращаем слайд в начальное состояние.

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

Логика для переключения в портретный режим
Логика для переключения в портретный режим

Когда переходим в портретный режим, ставим позицию(this.currentPosition) равной новой ширине экрана умноженной на активный индекс. Меняем translateX на этот this.currentPosition. Смотрим, что все работает

Вот отрефакторенная версия.

Теперь перейдем к прокрутке по касаниям. В метод touchStart напишем такой код:

-3

Здесь все просто.

  • Ставим анимацию в 100ms, потому что если мы будем двигать слайдер на 300ms, будет медленно и будет казаться, что лагает.
  • Запоминаем координаты точки, куда мы коснулись.

Теперь напишем метод onTouchMove

Метод, для изменения слайда при свайпе по экрану.
Метод, для изменения слайда при свайпе по экрану.

Сначала мы смотрим координату, где находится палец сейчас. Это currentX. Чтобы понимать в какую сторону нужно двигать слайдер, мы сравниваем текущую координату(currentX) с предыдущей(this.touchStartX). Если текущая меньше, тогда мы сохраняем значение: на сколько px был сдвиг(offsetX) и двигаем слайдер влево. Если больше, то двигаем вправо. Убедимся, что все работает.

-5

Все отлично. Осталось сделать автоматическое пролистывание, когда мы убираем палец с экрана.

Для этого в метод onRelease добавим такой код.

Тут мы возвращаем анимацию в 300ms. И на 4 строчке, в переменной moveTo, высчитываем расстояние, на которое нам надо подвинуть слайдер, чтобы показалась новая картинка. Далее, смотрим, куда мы листали в последний раз: right или left. И сравниваем: сдвинулся ли слайдер больше, чем на одну седьмую экрана (6 строчка и 19 строчка). Если сдвинулся, двигаем его на расстояние в moveTo (7 и 20 строчки), а если нет, то ставим слайдер на начальную позицию(16 и 30 строчки).

Снова проверим, что все в порядке.

Рабочая логика
Рабочая логика

Все работает. Осталось сделать тоже самое для прокрутки с захватом мыши. Для этого используем события:

  • onmousedown
  • onmousemove
  • onmouseup

Все работает по такой же логике, но onmousemove работает всегда, когда мы водим мышкой, поэтому надо в onmousedown и onmouseup следить, когда мы зажали или отжали кнопку. В этом нам поможет свойство isCaptured. Чтобы понять, вернемся к коду.

Метод onMouseDown:

-7

Все тоже самое, что и в onTouchStart, только добавили переменную isCaptured.

Метод onMouseMove:

-8

Если захватили мышью, тогда двигаем. Тут появился новый метод onMove. Это точно такой же метод touchMove, просто всю логику вычислений, кроме получения currentX, мы вынесли в onMove.

Когда мы отпускаем мышь и убираем палец метод у нас один - onRelease. Только еще добавим туда this.captured = false. Это нужно для понимания, что мы отпустили слайдер. Вот финальная версия кода.

Обложка
Обложка

Сегодня мы написали полностью работающий слайдер. Основная цель была - показать идею, как работают свайпы, на базовом уровне. Для картинок можно еще добавить lazyload.

Полезные ссылки:

Рабочий код - https://github.com/exoriri/slider-article/tree/feature/drag-and-touch

Рабочая версия - https://exoriri.github.io/slider-article/

Первая часть статьи -https://zen.yandex.ru/media/id/5f1d820734c8dd636d375e3a/pishem-slaider-bez-bibliotek-javascript-chast-1-63069d5c4ca82f0083ea773a

Спасибо, если продержались и дочитали до конца. Буду рад прочитать, что вы думаете, в комментариях:)

#программирование #javascript #слайдер #it