Ketika saya mulai belajar JavaScript, saya benar-benar ingin memahami cara kerja slider dan dibuat yang dapat diputar dengan swipe atau mouse, tetapi saya tidak menemukan bahan dengan penjelasan yang baik tentang apa yang saya butuhkan. Setelah beberapa waktu, saya berhasil melakukan hal serupa. Dan sekarang saya ingin menulis artikel tentang ini, sehingga orang lain yang ingin memahami cara kerjanya dan membuat acara sentuh untuk slider (dan tidak hanya) membuatnya lebih mudah untuk mengetahuinya. Saya akan mencoba untuk mengatur agar dan mendukung penjelasan dengan contoh ilustratif.
Saya tidak menganggap diri saya seorang spesialis hebat dalam JavaScript, selalu ada sesuatu untuk dipelajari, jadi jika Anda tahu cara menulis beberapa fragmen kode lebih baik / lebih mudah / lebih efisien, pastikan untuk menulis di komentar.
Kandungan:
- Fungsi apa yang akan kita lakukan?
- Menulis HTML dan CSS
- Bagaimana cara kerjanya?
- Menulis javascript
- Total
- Kode versi lengkap
Anda bisa langsung melihat contohnya .
1. Fungsi apa yang akan kita lakukan?
Mari kita menulis dengan 0 slider sederhana yang akan memiliki fungsi-fungsi berikut:
- kami menerapkan sentuhan dan seret untuk membalik slide;
- beralih slide menggunakan tombol panah;
- kunci tombol panah โ dan โ masing-masing pada slide pertama dan terakhir.
.
- , - , -, , , .. . . - , .
2. HTML CSS
HTML CSS?
(1 ) 200200 . - .
:

.
. , .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;
touch-action: pan-y;
}
.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).

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.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;
},
getEvent = () => event.type.search('touch') !== -1 ? event.touches[0] : event,
swipeStart = function() {
let evt = getEvent();
posInit = posX1 = evt.clientX;
sliderTrack.style.transition = '';
document.addEventListener('touchmove', swipeAction);
document.addEventListener('touchend', swipeEnd);
document.addEventListener('mousemove', swipeAction);
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)`;
} ...
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 .
- Slider dapat "meraih" ketika slide belum selesai bergerak.
Untuk memperbaiki perilaku ini, Anda perlu mendeklarasikan variabel allowSwipe
dan menyesuaikan larangan geser di atasnya.
Saya tidak akan menjelaskannya secara rinci. Lay out saja kode di bawah ini .
Dan dalam contoh, semua kondisi ini sudah dibuat. Juga, untuk kejelasan, kursor pada slider berubah.
Kode lengkap (geser, seret, panah)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();
});
Kode paling lengkaplet 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