Cuando comencé a aprender JavaScript, realmente quería entender cómo funcionan los controles deslizantes y cómo se pueden voltear con deslizar el dedo o con el mouse, pero no encontré materiales con una buena explicación de exactamente lo que necesito. Después de un tiempo, logré hacer algo similar. Y ahora quiero escribir un artículo sobre esto, para que otras personas que quieran entender cómo funciona todo y hacer eventos táctiles para el control deslizante (y no solo) hagan que sea más fácil resolverlo. Intentaré establecer en orden y apoyar las explicaciones con ejemplos ilustrativos.
No me considero un gran especialista en JavaScript, siempre hay algo que aprender, así que si sabes cómo escribir algunos fragmentos de código mejor / más fácil / más eficientemente, asegúrate de escribir en los comentarios.
Contenido:
- ¿Qué funcionalidad haremos?
- Escribir HTML y CSS
- ¿Cómo va a funcionar?
- Escribir javascript
- Total
- Código de la versión completa
Puedes ver un ejemplo de inmediato .
1. ¿Qué funcionalidad haremos?
Escribamos con 0 un control deslizante simple que tendrá las siguientes funciones:
- implementamos tocar y arrastrar volteando diapositivas;
- cambiar diapositivas con las teclas de flecha;
- bloquee las teclas de flecha ← y → en la primera y última diapositiva, respectivamente.
.
- , - , -, , , .. . . - , .
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 .
- El control deslizante se puede "agarrar" cuando la diapositiva aún no ha terminado de moverse.
Para corregir este comportamiento, debe declarar una variable allowSwipe
y ajustar la prohibición de deslizamiento sobre ella.
No lo describiré en detalle. Simplemente presente este código a continuación .
Y en el ejemplo, todas estas condiciones ya estarán hechas. Además, para mayor claridad, el cursor en el control deslizante cambia.
Código completo (deslizar, arrastrar, flechas)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();
});
El código más completolet 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