Когда я начинал изучать JavaScript, мне очень хотелось понять как работают и делаются слайдеры, которые можно перелистывать свайпами или мышью, но материалов с хорошим объяснением именно того, что мне надо, я не нашел. Через какое-то время мне удалось сделать нечто подобное. И теперь я хочу написать об этом статью, чтобы другим людям, которые хотят понять как это все работает и сделать touch события для слайдера (и не только), было проще разобраться. Я постараюсь излагать по порядку и подкреплять объяснения наглядными примерами.
Я не считаю себя большим специалистом в JavaScript, всегда есть чему учиться, поэтому если знаете как написать какие-то фрагменты кода лучше/проще/эффективнее — обязательно напишите в комментарии.
Содержание:
- Какой функционал будем делать?
- Пишем HTML и CSS
- Как это будет работать?
- Пишем JavaScript
- Итого
- Полная версия кода
Можно сразу посмотреть пример.
1. Какой функционал будем делать?
Напишем с 0 простенький слайдер, который будет иметь следующие функции:
- реализуем touch и drag перелистывание слайдов;
- переключение слайдов с помощью стрелок-переключателей;
- блокировка стрелок-переключателей ← и → на первом и последнем слайде соответственно.
Звучит совсем просто и я постараюсь писать тоже просто и понятно.
Если кому-нибудь будет интересно, то мы можем добавлять потом какой-то функционал, вроде точек-переключателей, бесконечной прокрутки слайдов, вертикальной ориентации, переключение слайдов с помощью клавиатуры и т.д. В конце концов можно завернуть в компонент. Напишите об этом в комментарии. Если будет хотя бы несколько комментариев за какой-то функционал, то реализуем его.
2. Пишем HTML и CSS
Что должен представлять из себя слайдер с точки зрения HTML и CSS?
Видимая часть слайдера (1 слайд) будет размером 200х200 пикселей. Снизу расположим стрелки-переключатели слайдов.
В итоге у нас будет вот это:
Рассмотрим подробнее изнутри.
Несколько слайдов должны идти по порядку и располагаться горизонтально в одну линию. Для этого нужно поместить их в отдельный блок, класс которого будет .slider-track
и зададим ему display: flex
. Он должен вмещать в себя все слайды горизонтально, то есть быть достаточной ширины, в этом примере зададим для слайдов flex-shrink: 0
, чтобы они не сжимались.
Далее желательно скрыть все слайды кроме текущего. Для этого придется обернуть наш track в еще один блок, назовем его .slider-list
. Ширина и высота у него будет равна ширине и высоте одного слайда, также у него будет overflow: hidden
. Таким образом виден будет только один слайд, а остальные скрыты.
Теперь нужно создать стрелки-переключатели. Для бо́льшего удобства мы положим их в отдельный блок .slider-arrows
и расположим его снизу слайдера. Но за пределами .slider-list
(overflow: hidden
) их не будет видно, поэтому можно:
- установить для
.slider-list
padding-bottom и в образовавшееся пустое место разместить блок со стрелками; - обернуть
.slider-list
в еще один блок, который можно назвать просто.slider
и внутри него (или за его пределами) располагать стрелки в удобном месте.
Для бо́льшей простоты все размеры будут установлены через CSS.
HTML будет выглядеть так:
<div class="slider">
<div class="slider-list">
<div class="slider-track">
<div class="slide">1</div>
<div class="slide">2</div>
<div class="slide">3</div>
<div class="slide">4</div>
<div class="slide">5</div>
</div>
</div>
<div class="slider-arrows">
<button type="button" class="prev">←</button>
<button type="button" class="next">→</button>
</div>
</div>
←
и →
это спецсимволы HTML-разметки, которые представляют собой стрелки, направленные в левую и правую сторону соответственно.
CSS будет таким:
.slider {
position: relative;
width: 200px;
height: 200px;
margin: 50px auto 0;
/* Чтобы во время перетаскивания слайда ничего не выделить внутри него */
user-select: none;
/* Чтобы запретить скролл страницы, если мы начали двигать слайдер по оси X */
touch-action: pan-y;
}
/* Если где-то внутри слайдера будут изображения,
то нужно задать им pointer-events: none,
чтобы они не перетаскивались мышью */
.slider img {
poiner-events: none;
}
.slider-list {
width: 200px;
height: 200px;
overflow: hidden;
}
.slider-list.grab {
cursor: grab;
}
.slider-list.grabbing{
cursor: grabbing;
}
.slider-track {
display: flex;
}
.slide {
width: 200px;
height: 200px;
/* Чтобы слайды не сжимались */
flex-shrink: 0;
/* Увеличиваем и центрируем цифру внутри слайда */
font-size: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #000;
}
.slider-arrows {
margin-top: 15px;
text-align: center;
}
.next,
.prev {
background: none;
border: none;
margin: 0 10px;
font-size: 30px;
cursor: pointer;
}
.next.disabled,
.prev.disabled {
opacity: .25;
pointer-events: none;
}
И выглядеть это будет так:
Без overflow: hidden
:
С этим просто, теперь перейдем к обсуждению логики работы слайдера.
3. Как это будет работать?
Двигать слайдер проще и лучше всего через transform: translate3d(x, y, z)
, чтобы не вызывать лишние перерисовки в браузере. Также можно использовать transform: translateX(x)
в комбинации с will-change: transform
. Считывать style
мы будем при помощи встроенного метода строк match()
.
Первым делом нужно отслеживать номер текущего слайда в переменной slideIndex
. Мы уже условились, что все слайды одной ширины (200px). Перелистываться слайды будут очень просто — в свойство transform: translate3d(x, y, z)
в позицию x
устанавливается следующее выражение: номер слайда * ширина слайда
. Так и будет происходить переключение слайдов и напрямую это будет работать на стрелках-переключателях.
Со свайпами посложнее, разберем подробнее.
Мы будем использовать три основных события в браузере. При касании пальцем срабатывает событие touchstart
(при зажатии мыши mousedown
), при движении пальцем по экрану — touchmove
(mousemove
), при отпускании пальца — touchend
(mouseup
).
Нам нужно будет работать с координатами курсора event.clientX
(место касания экрана) следующим образом:
Создадим 3 основных функции для работы со свайпами —
swipeStart
,swipeAction
иswipeEnd
.
При первом касании записываем координаты касания (курсора) по оси X (переменные
posX1
иposInit
, гдеposX1
в дальнейшем будет меняться, аposInit
статичная).
При движении курсора, вычитаем текущие координаты из
posX1
, записывая результат в переменнуюposX2
, которая потом будет изменятьstyle.transform
, чтобы двигать слайды. И перезаписываемposX1
текущими координатами.
Разберем по порядку подробнее:
posX1
иposInit
— координаты, полученные при первом касании (текущие координаты курсораevent.clientX
). Это первое касание экрана вswipeStart
, например, если ширина нашего экрана 320px, то касание по центру установитposInit
иposX1
в 160. ВswipeAction
переменнаяposX1
будет перезаписываться.
posX2
— разностьposX1
иevent.clientX
. Будет считаться каждый раз при движении по экрану вswipeAction
, например, если мы сдвинули палец чуть-чуть вправо, то "текущие координаты" = 161, значит 160 — 161 = -1, будет смещение на -1px. Нагляднее можно увидеть ниже:
Таким образом, при движении пальцем по экрану, мы каждый раз считаем смещение курсора по оси X, относительно его предыдущего положения и переменная
posX2
всегда будет содержать количество пикселей, на которое мы сдвинули палец по экрану. Обычно это число от 0.5 до 10, в зависимости от размаха (если двинуть палец очень резко, то будут бо́льшие числа, а если палец двигать медленно, то меньшие).
При прекращении свайпа, мы вычитаем из начальной позиции курсора текущую и сравниваем полученное значение (
posFinal
) со значением "порога" сдвига слайда (posThreshold
), который мы определим заранее.
Разберем подробнее с примерами. Некоторые действия мы пока опустим. Они не столь важны для этого объяснения, рассмотрим только самые основные:
posFinal
=posInit
–posX1
(так мы получим количество пикселей, на которое провели пальцем вswipeAction
, например, если ширина слайдера 200px, то если мы проведем пальцем от середины слайдера до его края и отпустим,posFinal
будет равен 100).
posThreshold
= ширина слайда (200px) * 0,3 = 60. С этим числом мы будем сравниватьposFinal
, например, еслиposFinal
> 60, то переключаем слайд, иначе возвращаем в начальное положение. Само условие:
if (posFinal >= posThreshold) nextSlide() или prevSlide(); else currentSlide();
Если со вторым условием
else
более-менее понятно, то с первым нужно разобраться подробнее.
Допустим мы превысили порог
posThreshold
и будем переключать слайд, но теперь нужно понять в какую сторону мы двинули слайд, чтобы вызватьprev()
илиnext()
действие. Для этого мы будем сравниватьposInit
иposX1
. Пусть ширина нашего экрана 320px, чи́сла ширины идут слева направо, то есть 0px-320px (в самой левой точке экрана 0, в самой правой точке экрана 320). Представим, что слайдер расположен по центру, прикладываем палец в самый центр и получаемposInit
160. Теперь, если мы будем вести палец влево (допустим до края экрана), тоposX1
будет уменьшаться с 160 до 0. Если будем вести палец вправо до края экрана, тоposX1
будет увеличиваться c 160 до 320. Итак, мы приложили палец в центр и провели немного влево, при окончании свайпаposInit
160, аposX1
100, значит мы прошли порогposThreshold
и нам нужно показывать следующий слайд. Получается группа условий:
if (posInit > posX1) nextSlide(); else if (posInit < posX1) prevSlide();
Еще возможен вариант, когда мы прислонили палец и сразу убрали, вроде события
click
. В таком случаеposInit
===posX1
. Этот вариант мы тоже должны предусмотреть, но об этом позже.
Надеюсь у меня получилось объяснить правильно и понятно. Теперь приступим к написанию JavaScript.
4. Пишем JavaScript
Ниже я опишу кратко алгоритм еще раз, но помимо основного алгоритма работы слайдера, будет еще не мало разных условий для "фиксов" не нужного нам поведения, которое будет приводить к разным странностям, либо будет ломать слайдер. Сначала мы сделаем просто переключение слайдов свайпами, а фиксить будем уже потом. Я постараюсь делать все объяснения и приводить примеры, сохраняя всю последовательность.
Кратко об основном еще раз:
.slider-track
будет двигаться при помощиtransfrom: translate3d(Xpx, 0px, 0px)
;- изначально зададим ему этот стиль
transfrom: translate3d(0px, 0px, 0px)
в объектstyle
, чтобы можно было его считывать; - мы будем считывать текущую трансформацию из
style.transform
с помощью встроенного метода строкmatch()
и изменять ее в зависимости от движения курсора; - в самом начале назначим функцию
swipeStart
на.slider-list
с помощью слушателя событийtouchstart
иmousedown
; - в функции
swipeStart
мы будем назначать уже наdocument
функцииswipeAction
иswipeEnd
с помощью слушателя событийtouchmove
(mousemove
) иtouchend
(mouseup
) соответственно, позже удалять; - во время свайпа при проходе определенного числового порога
posThreshold
, мы будем увеличивать или уменьшатьslideIndex
и вызывать функцию переключения слайда; - если порог
posThreshold
пройден не был, то вернем слайдер в начальное положение; - при клике на стрелки-переключатели, слайды будут переключаться.
Полная версия кода без разрывов на пояснения будет снизу.
Полная версия кода с фиксами нежелательного поведния еще ниже
Первым делом получим наши элементы со страницы. При такой очевидной HTML-разметке хорошим вариантом было бы получить элементы через один querySelector и свойства DOM, но для простоты возьмем все через querySelector. Также объявим все нужные для работы переменные и основную функцию, которая будет переключать слайды. Пояснение я дам ниже.
let slider = document.querySelector('.slider'),
sliderList = slider.querySelector('.slider-list'),
sliderTrack = slider.querySelector('.slider-track'),
slides = slider.querySelectorAll('.slide'),
arrows = slider.querySelector('.slider-arrows'),
prev = arrows.children[0],
next = arrows.children[1],
slideWidth = slides[0].offsetWidth,
slideIndex = 0,
posInit = 0,
posX1 = 0,
posX2 = 0,
posFinal = 0,
posThreshold = slideWidth * .35,
trfRegExp = /[-0-9.]+(?=px)/,
slide = function() {
sliderTrack.style.transition = 'transform .5s';
sliderTrack.style.transform = `translate3d(-${slideIndex * slideWidth}px, 0px, 0px)`;
// делаем стрелку prev недоступной на первом слайде
// и доступной в остальных случаях
// делаем стрелку next недоступной на последнем слайде
// и доступной в остальных случаях
prev.classList.toggle('disabled', slideIndex === 0);
next.classList.toggle('disabled', slideIndex === --slides.length);
} ...
trfRegExp
— переменная с инициализацией регулярного выражения, которое мы будем использовать для считывания свойства transform
у нашего .slider-track
.
Если для событий мыши нужные нам координаты курсора лежат в event.clientX
, то для тач событий они лежат в массиве touches — event.touches[0].clientX
. Значит обращаться к свойствам event
нужно через условие и т.к. нам нужно получать координаты в двух местах, то можно обернуть условие в функцию getEvent
. Мы будем проверять event.type
на содержание подстроки touch и в зависимости от результата возвращать первый элемент массива touch или просто event. И сразу же пишем функции дальше.
getEvent = function() {
return event.type.search('touch') !== -1 ? event.touches[0] : event;
// p.s. event - аргумент по умолчанию в функции
},
// или es6
getEvent = () => event.type.search('touch') !== -1 ? event.touches[0] : event,
swipeStart = function() {
let evt = getEvent();
// берем начальную позицию курсора по оси Х
posInit = posX1 = evt.clientX;
// убираем плавный переход, чтобы track двигался за курсором без задержки
// т.к. он будет включается в функции slide()
sliderTrack.style.transition = '';
// и сразу начинаем отслеживать другие события на документе
document.addEventListener('touchmove', swipeAction);
document.addEventListener('touchend', swipeEnd);
document.addEventListener('mousemove', swipeAction);
document.addEventListener('mouseup', swipeEnd);
},
swipeAction = function() {
let evt = getEvent(),
// для более красивой записи возьмем в переменную текущее свойство transform
style = sliderTrack.style.transform,
// считываем трансформацию с помощью регулярного выражения и сразу превращаем в число
transform = +style.match(trfRegExp)[0];
posX2 = posX1 - evt.clientX;
posX1 = evt.clientX;
sliderTrack.style.transform = `translate3d(${transform - posX2}px, 0px, 0px)`;
// можно было бы использовать метод строк .replace():
// sliderTrack.style.transform = style.replace(trfRegExp, match => match - posX2);
// но в дальнейшем нам нужна будет текущая трансформация в переменной
} ...
Пояснение к функции swipeAction
:
В этой функции мы изменяем свойство transform
. Разберем подробнее:
С помощью регулярного выражения ищем в строке translate3d(0px, 0px, 0px)
первое вхождение подстроки "ЧИСЛОpx
". Изменяем его и устанавливаем полученное число обратно в свойство transform
. Оно отвечает за сдвиг по оси Х.
Регулярное выражение выглядит так: [-0-9.]+(?=px)
, разберем его подробнее:
[-0-9.]
— эта группа говорит, что мы ищем или "тире" или "цифру от 0 до 9" или "точку";+
— после предыдущей группы говорит, что любой из этих символов может быть 1 или более раз, это позволит нам найти различные сочетания, например: 5, 101.10, -19, -12.5 и т.д.;(?=px)
— гворит, что мы ищем предыдущую группу цифр, только если за ними следует "px".
И на этом моменте наш .slider-track
уже можно двигать, но не забываем предварительно задать ему sliderTrack.style.transform = 'translate3d(0px, 0px, 0px)'
, чтобы в функции swipeAction
было что менять. Результат:
Но когда мы завершаем свайп (отпускаем мышь или убираем палец от экрана) — функция swipeAction
продолжает выполняться, .slider-track
двигается за мышью по оси Х. Мы уже привязали функцию swipeEnd
к нужным событиями, но до объявления функции не дошли, объявим функцию и сделаем пояснение:
swipeEnd = function() {
// финальная позиция курсора
posFinal = posInit - posX1;
document.removeEventListener('touchmove', swipeAction);
document.removeEventListener('mousemove', swipeAction);
document.removeEventListener('touchend', swipeEnd);
document.removeEventListener('mouseup', swipeEnd);
// убираем знак минус и сравниваем с порогом сдвига слайда
if (Math.abs(posFinal) > posThreshold) {
// если мы тянули вправо, то уменьшаем номер текущего слайда
if (posInit < posX1) {
slideIndex--;
// если мы тянули влево, то увеличиваем номер текущего слайда
} else if (posInit > posX1) {
slideIndex++;
}
}
// если курсор двигался, то запускаем функцию переключения слайдов
if (posInit !== posX1) {
slide();
}
};
Теперь touch и drag события для слайдера полностью работают, слайды переключаются. Напоминаю: если posFinal
будет больше posThreshold
, то переключаем слайд, иначе, он возвращается в изначальное положение. Благодаря функции Math.abs()
знак минус опускается.
Теперь надо сделать переключение стрелками. Привязываем обработчик событий клика на блок со стрелками, проверяем на какой из них произошел клик и вызыаем функцию переключения слайдов.
arrows.addEventListener('click', function() {
let target = event.target;
if (target === next) {
slideIndex++;
} else if (target === prev) {
slideIndex--;
} else {
return;
}
slide();
});
sliderTrack.style.transform = 'translate3d(0px, 0px, 0px)';
slider.addEventListener('touchstart', swipeStart);
slider.addEventListener('mousedown', swipeStart);
Теперь слайдер будет переключаться и с помощью стрелок. Обработчики событий снизу привязывают начальные функции на слайдер.
Итого
И вот слайдер полностью работает. Слайды перелистываются tocuh событиями, drag событиями и стрелками-переключателями. Но есть еще некоторое поведение, которое может его сломать, посмотрим на примерах ниже:
- Когда мы скроллим страницу вверх или вниз с телефона, мы прислоняем палец к экрану и делаем им взмах. В момент взмаха, палец может уходить в сторону и если этот взмах был на слайдере, то наше действие будет сдвигать слайдер в сторону:
В этом случае нужно отслеживать координаты курсора и по оси Y, затем разрешать или запрещать свайп, в зависимости от того какое действие мы совершаем (свайп слайдера или скролл страницы).
- Мы можем тянуть слайдер в сторону, когда с другой стороны пусто:
В этом случае нужно на первом и последнем слайде отслеживать в какую сторону мы тянем слайд и завершать работу функции, если на первом слайде мы тянем вправо, а на последнем слайде тянем влево.
- Слайды можно тащить в любую сторону, пока позволяет ширина экрана:
В этом случае также нужно определять в какую сторону мы тянем слайд и в зависимости от стороны, сравнивать текущую трансформацию с трансформацией следующего или предыдущего слайда. Чтобы каждый раз не считать трансформацию следующего и предыдущего слайда в функции swipeAction
, нужно в функции swipeStart
обновлять их 1 раз.
- Слайдер можно "схватить", когда слайд еще не закончил перемещение.
Для исправления этого поведения нужно объявить переменную allowSwipe
и регулировать ей запрет свайпа.
Описывать это подробно я уже не буду. Просто выложу этот код ниже.
И в примере все эти условия уже будут сделаны. Также для наглядности меняется курсор на слайдере.
let slider = document.querySelector('.slider'),
sliderList = slider.querySelector('.slider-list'),
sliderTrack = slider.querySelector('.slider-track'),
slides = slider.querySelectorAll('.slide'),
arrows = slider.querySelector('.slider-arrows'),
prev = arrows.children[0],
next = arrows.children[1],
slideWidth = slides[0].offsetWidth,
slideIndex = 0,
posInit = 0,
posX1 = 0,
posX2 = 0,
posFinal = 0,
posThreshold = slides[0].offsetWidth * 0.35,
trfRegExp = /([-0-9.]+(?=px))/,
getEvent = function() {
return (event.type.search('touch') !== -1) ? event.touches[0] : event;
},
slide = function() {
if (transition) {
sliderTrack.style.transition = 'transform .5s';
}
sliderTrack.style.transform = `translate3d(-${slideIndex * slideWidth}px, 0px, 0px)`;
prev.classList.toggle('disabled', slideIndex === 0);
next.classList.toggle('disabled', slideIndex === --slides.length);
},
swipeStart = function() {
let evt = getEvent();
posInit = posX1 = evt.clientX;
sliderTrack.style.transition = '';
document.addEventListener('touchmove', swipeAction);
document.addEventListener('mousemove', swipeAction);
document.addEventListener('touchend', swipeEnd);
document.addEventListener('mouseup', swipeEnd);
},
swipeAction = function() {
let evt = getEvent(),
style = sliderTrack.style.transform,
transform = +style.match(trfRegExp)[0];
posX2 = posX1 - evt.clientX;
posX1 = evt.clientX;
sliderTrack.style.transform = `translate3d(${transform - posX2}px, 0px, 0px)`;
},
swipeEnd = function() {
posFinal = posInit - posX1;
document.removeEventListener('touchmove', swipeAction);
document.removeEventListener('mousemove', swipeAction);
document.removeEventListener('touchend', swipeEnd);
document.removeEventListener('mouseup', swipeEnd);
if (Math.abs(posFinal) > posThreshold) {
if (posInit < posX1) {
slideIndex--;
} else if (posInit > posX1) {
slideIndex++;
}
}
if (posInit !== posX1) {
slide();
}
};
sliderTrack.style.transform = 'translate3d(0px, 0px, 0px)';
slider.addEventListener('touchstart', swipeStart);
slider.addEventListener('mousedown', swipeStart);
arrows.addEventListener('click', function() {
let target = event.target;
if (target === next) {
slideIndex++;
} else if (target === prev) {
slideIndex--;
} else {
return;
}
slide();
});
let slider = document.querySelector('.slider'),
sliderList = slider.querySelector('.slider-list'),
sliderTrack = slider.querySelector('.slider-track'),
slides = slider.querySelectorAll('.slide'),
arrows = slider.querySelector('.slider-arrows'),
prev = arrows.children[0],
next = arrows.children[1],
slideWidth = slides[0].offsetWidth,
slideIndex = 0,
posInit = 0,
posX1 = 0,
posX2 = 0,
posY1 = 0,
posY2 = 0,
posFinal = 0,
isSwipe = false,
isScroll = false,
allowSwipe = true,
transition = true,
nextTrf = 0,
prevTrf = 0,
lastTrf = --slides.length * slideWidth,
posThreshold = slides[0].offsetWidth * 0.35,
trfRegExp = /([-0-9.]+(?=px))/,
getEvent = function() {
return (event.type.search('touch') !== -1) ? event.touches[0] : event;
},
slide = function() {
if (transition) {
sliderTrack.style.transition = 'transform .5s';
}
sliderTrack.style.transform = `translate3d(-${slideIndex * slideWidth}px, 0px, 0px)`;
prev.classList.toggle('disabled', slideIndex === 0);
next.classList.toggle('disabled', slideIndex === --slides.length);
},
swipeStart = function() {
let evt = getEvent();
if (allowSwipe) {
transition = true;
nextTrf = (slideIndex + 1) * -slideWidth;
prevTrf = (slideIndex - 1) * -slideWidth;
posInit = posX1 = evt.clientX;
posY1 = evt.clientY;
sliderTrack.style.transition = '';
document.addEventListener('touchmove', swipeAction);
document.addEventListener('mousemove', swipeAction);
document.addEventListener('touchend', swipeEnd);
document.addEventListener('mouseup', swipeEnd);
sliderList.classList.remove('grab');
sliderList.classList.add('grabbing');
}
},
swipeAction = function() {
let evt = getEvent(),
style = sliderTrack.style.transform,
transform = +style.match(trfRegExp)[0];
posX2 = posX1 - evt.clientX;
posX1 = evt.clientX;
posY2 = posY1 - evt.clientY;
posY1 = evt.clientY;
// определение действия свайп или скролл
if (!isSwipe && !isScroll) {
let posY = Math.abs(posY2);
if (posY > 7 || posX2 === 0) {
isScroll = true;
allowSwipe = false;
} else if (posY < 7) {
isSwipe = true;
}
}
if (isSwipe) {
// запрет ухода влево на первом слайде
if (slideIndex === 0) {
if (posInit < posX1) {
setTransform(transform, 0);
return;
} else {
allowSwipe = true;
}
}
// запрет ухода вправо на последнем слайде
if (slideIndex === --slides.length) {
if (posInit > posX1) {
setTransform(transform, lastTrf);
return;
} else {
allowSwipe = true;
}
}
// запрет протаскивания дальше одного слайда
if (posInit > posX1 && transform < nextTrf || posInit < posX1 && transform > prevTrf) {
reachEdge();
return;
}
// двигаем слайд
sliderTrack.style.transform = `translate3d(${transform - posX2}px, 0px, 0px)`;
}
},
swipeEnd = function() {
posFinal = posInit - posX1;
isScroll = false;
isSwipe = false;
document.removeEventListener('touchmove', swipeAction);
document.removeEventListener('mousemove', swipeAction);
document.removeEventListener('touchend', swipeEnd);
document.removeEventListener('mouseup', swipeEnd);
sliderList.classList.add('grab');
sliderList.classList.remove('grabbing');
if (allowSwipe) {
if (Math.abs(posFinal) > posThreshold) {
if (posInit < posX1) {
slideIndex--;
} else if (posInit > posX1) {
slideIndex++;
}
}
if (posInit !== posX1) {
allowSwipe = false;
slide();
} else {
allowSwipe = true;
}
} else {
allowSwipe = true;
}
},
setTransform = function(transform, comapreTransform) {
if (transform >= comapreTransform) {
if (transform > comapreTransform) {
sliderTrack.style.transform = `translate3d(${comapreTransform}px, 0px, 0px)`;
}
}
allowSwipe = false;
},
reachEdge = function() {
transition = false;
swipeEnd();
allowSwipe = true;
};
sliderTrack.style.transform = 'translate3d(0px, 0px, 0px)';
sliderList.classList.add('grab');
sliderTrack.addEventListener('transitionend', () => allowSwipe = true);
slider.addEventListener('touchstart', swipeStart);
slider.addEventListener('mousedown', swipeStart);
arrows.addEventListener('click', function() {
let target = event.target;
if (target.classList.contains('next')) {
slideIndex++;
} else if (target.classList.contains('prev')) {
slideIndex--;
} else {
return;
}
slide();
});
codepen.io