Reputation: 23
I'm a beginner in javascript. I downloaded a custom javacsript slide from this website:
https://www.cssscript.com/swiper-thumbnail-paginator/
Slide demo here: https://www.cssscript.com/demo/swiper-thumbnail-paginator/
I've also created a demo on jsfiddle: https://jsfiddle.net/t4c1nb3g/
The file consists of 5 files, namely: index.html, style.css, debounce.js, script.js, slide.js
And it has 6 images.
Currently, there is no slide loop in the demo, I want the slide to start itself (autoplay) and repeat it again and again (looping).
How can I make it loop for the slide and start itself (autoplay)?
Below is the index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Slider</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="slide-wrapper">
<ul class="slide">
<li><img src="https://i.sstatic.net/2fkLR.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/gN1Ri.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/FgqYP.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/su1na.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/vZYry.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/5dXtQ.jpg" alt=""></li>
</ul>
</div>
<div class="wrap-controls">
<div class="arrow-nav">
<button class="prev"></button>
</div>
<ul class="custom-controls">
<li><img src="https://i.sstatic.net/2fkLR.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/gN1Ri.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/FgqYP.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/su1na.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/vZYry.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/5dXtQ.jpg" alt=""></li>
</ul>
<div class="arrow-nav">
<button class="next"></button>
</div>
</div>
<script type="module" src="js/script.js"></script>
</body>
</html>
Below is the style.css file:
body {
margin: 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
ul {
padding: 0px;
margin: 0px;
list-style: none;
}
img {
display: block;
max-width: 100%;
}
.slide-wrapper {
overflow: hidden;
}
.slide {
display: flex;
}
.slide:hover {
will-change: transform;
}
.slide li {
flex-shrink: 0;
max-width: 600px;
margin: 0 20px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,.4);
opacity: .8;
transform: scale(.8);
transition: .4s;
}
.slide li.active {
opacity: 1;
transform: scale(1);
}
[data-control="slide"] {
display: flex;
justify-content: center;
margin-top: 20px;
}
[data-control="slide"] li a {
display: block;
width: 12px;
height: 12px;
background: #FB5;
border-radius: 50%;
overflow: hidden;
text-indent: -999px;
margin: 5px;
}
[data-control="slide"] li.active a, [data-control="slide"] li a:hover {
background: #E54;
}
.custom-controls {
display: flex;
justify-content: center;
margin-top: 40px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.custom-controls li {
opacity: .8;
transform: scale(.8);
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin: 2px;
box-shadow: 0 2px 2px rgba(0,0,0,.5);
transition: .3s;
cursor: pointer;
}
.custom-controls li.active {
opacity: 1;
transform: scale(1);
}
.arrow-nav {
display: flex;
justify-content: space-around;
margin: 20px 10px 0 10px;
}
.arrow-nav button {
cursor: pointer;
border: none;
border-radius: 50%;
color: white;
width: 30px;
height: 30px;
background: #999 url('../img/arrow.svg') center center no-repeat;
outline: none;
}
.arrow-nav button:hover {
background: #333 url('../img/arrow.svg') center center no-repeat;
transition: ease-in-out .3s;
}
.arrow-nav button.prev {
transform: rotate(-180deg);
}
.wrap-controls {
display: flex;
justify-content: center;
align-items: center;
}
Below is the debounce.js file:
export default function debounce(callback, delay) {
let timer;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
callback(...args);
timer = null;
}, delay);
};
}
Below is the script.js file:
import SlideNav from './slide.js';
const slide = new SlideNav('.slide', '.slide-wrapper');
slide.init();
slide.addArrow('.prev', '.next');
slide.addControl('.custom-controls');
Below is the slide.js file:
import debounce from './debounce.js';
export class Slide {
constructor(slide, wrapper) {
this.slide = document.querySelector(slide)
this.wrapper = document.querySelector(wrapper);
this.dist = { finalPosition: 0, startX: 0, movement: 0 }
this.activeClass = 'active';
this.changeEvent = new Event('changeEvent');
}
transition(active) {
this.slide.style.transition = active ? 'transform .3s' : '';
}
moveSlide(distX) {
this.dist.movePosition = distX;
this.slide.style.transform = `translate3d(${distX}px, 0, 0)`;
}
updatePosition(clientX) {
this.dist.movement = (this.dist.startX - clientX) * 1.6;
return this.dist.finalPosition - this.dist.movement;
}
onStart(event) {
let movetype;
if (event.type === 'mousedown') {
event.preventDefault();
this.dist.startX = event.clientX;
movetype = 'mousemove';
} else {
this.dist.startX = event.changedTouches[0].clientX;
movetype = 'touchmove';
}
this.wrapper.addEventListener(movetype, this.onMove);
this.transition(false);
}
onMove(event) {
const pointerPosition = (event.type === 'mousemove') ? event.clientX : event.changedTouches[0].clientX;
const finalPosition = this.updatePosition(pointerPosition);
this.moveSlide(finalPosition);
}
onEnd(event) {
const movetype = (event.type === 'mouseup') ? 'mousemove' : 'touchmove';
this.wrapper.removeEventListener(movetype, this.onMove);
this.dist.finalPosition = this.dist.movePosition;
this.transition(true);
this.changeSlideOnEnd();
}
changeSlideOnEnd() {
if (this.dist.movement > 120 && this.index.next !== undefined) {
this.activeNextSlide();
} else if (this.dist.movement < -120 && this.index.prev !== undefined) {
this.activePrevSlide();
} else {
this.changeSlide(this.index.active);
}
}
addSlideEvents() {
this.wrapper.addEventListener('mousedown', this.onStart);
this.wrapper.addEventListener('touchstart', this.onStart);
this.wrapper.addEventListener('mouseup', this.onEnd);
this.wrapper.addEventListener('touchend', this.onEnd);
}
// Slides config
slidePosition(slide) {
const margin = (this.wrapper.offsetWidth - slide.offsetWidth) / 2;
return -(slide.offsetLeft - margin);
}
slidesConfig() {
this.slideArray = [...this.slide.children].map((element) => {
const position = this.slidePosition(element);
return { position, element };
});
}
slidesIndexNav(index) {
const last = this.slideArray.length - 1;
this.index = {
prev: index ? index - 1 : undefined,
active: index,
next: index === last ? undefined : index + 1,
}
}
changeSlide(index) {
const activeSlide = this.slideArray[index];
this.moveSlide(activeSlide.position);
this.slidesIndexNav(index);
this.dist.finalPosition = activeSlide.position;
this.changeActiveClass();
this.wrapper.dispatchEvent(this.changeEvent);
}
changeActiveClass() {
this.slideArray.forEach(item => item.element.classList.remove(this.activeClass));
this.slideArray[this.index.active].element.classList.add(this.activeClass);
}
activePrevSlide() {
if (this.index.prev !== undefined) this.changeSlide(this.index.prev);
}
activeNextSlide() {
if (this.index.next !== undefined) this.changeSlide(this.index.next);
}
onResize() {
setTimeout(() => {
this.slidesConfig();
this.changeSlide(this.index.active);
}, 1000);
}
addResizeEvent() {
window.addEventListener('resize', this.onResize);
}
bindEvents() {
this.onStart = this.onStart.bind(this);
this.onMove = this.onMove.bind(this);
this.onEnd = this.onEnd.bind(this);
this.activePrevSlide = this.activePrevSlide.bind(this);
this.activeNextSlide = this.activeNextSlide.bind(this);
this.onResize = debounce(this.onResize.bind(this), 200);
}
init() {
this.bindEvents();
this.transition(true);
this.addSlideEvents();
this.slidesConfig();
this.addResizeEvent();
this.changeSlide(0);
return this;
}
}
export default class SlideNav extends Slide {
constructor(slide, wrapper) {
super(slide, wrapper);
this.bindControlEvents();
}
addArrow(prev, next) {
this.prevElement = document.querySelector(prev);
this.nextElement = document.querySelector(next);
this.addArrowEvent();
}
addArrowEvent() {
this.prevElement.addEventListener('click', this.activePrevSlide);
this.nextElement.addEventListener('click', this.activeNextSlide);
}
createControl() {
const control = document.createElement('ul');
control.dataset.control = 'slide';
this.slideArray.forEach((item, index) => {
control.innerHTML += `<li><a href="#slide${index + 1}">${index + 1}</a></li>`;
});
this.wrapper.appendChild(control);
return control;
}
eventControl(item, index) {
item.addEventListener('click', (event) => {
event.preventDefault();
this.changeSlide(index);
});
this.wrapper.addEventListener('changeEvent', this.activeControlItem);
}
activeControlItem() {
this.controlArray.forEach(item => item.classList.remove(this.activeClass));
this.controlArray[this.index.active].classList.add(this.activeClass);
}
addControl(customControl) {
this.control = document.querySelector(customControl) || this.createControl();
this.controlArray = [...this.control.children];
this.activeControlItem();
this.controlArray.forEach(this.eventControl);
}
bindControlEvents() {
this.eventControl = this.eventControl.bind(this);
this.activeControlItem = this.activeControlItem.bind(this);
}
}
Thank you very much,
best regards.
Franks.
Upvotes: 2
Views: 418
Reputation: 278
After debugging your case on Codepen, I finally figured it out.
Basically you need to know 2 changes that I made:
The first one is handling the edge cases:
When you're at the end and click Next, you should go to the beginning
When you're at the beginning and click Previous, you should go to the end
The 2nd aspect is making this slider autoplaying or looping.
For the 1st change (the edge cases), modify in your code the methods activePrevSlide
and activeNextSlide
as the following:
activePrevSlide() {
if (this.index.prev !== undefined)
this.changeSlide(this.index.prev);
else
this.changeSlide(this.slideArray.length - 1);
}
activeNextSlide() {
if (this.index.next !== undefined)
this.changeSlide(this.index.next);
else
this.changeSlide(0);
}
For the 2nd aspect, add at the end of script.js
, this statement:
setInterval(slide.activeNextSlide, 3000);
It will autoplay each 3 seconds (you can change it definitely).
function debounce(callback, delay) {
let timer;
return (...args) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
callback(...args);
timer = null;
}, delay);
};
}
class Slide {
constructor(slide, wrapper) {
this.slide = document.querySelector(slide)
this.wrapper = document.querySelector(wrapper);
this.dist = { finalPosition: 0, startX: 0, movement: 0 }
this.activeClass = 'active';
this.changeEvent = new Event('changeEvent');
}
transition(active) {
this.slide.style.transition = active ? 'transform .3s' : '';
}
moveSlide(distX) {
this.dist.movePosition = distX;
this.slide.style.transform = `translate3d(${distX}px, 0, 0)`;
}
updatePosition(clientX) {
this.dist.movement = (this.dist.startX - clientX) * 1.6;
return this.dist.finalPosition - this.dist.movement;
}
onStart(event) {
let movetype;
if (event.type === 'mousedown') {
event.preventDefault();
this.dist.startX = event.clientX;
movetype = 'mousemove';
} else {
this.dist.startX = event.changedTouches[0].clientX;
movetype = 'touchmove';
}
this.wrapper.addEventListener(movetype, this.onMove);
this.transition(false);
}
onMove(event) {
const pointerPosition = (event.type === 'mousemove') ? event.clientX : event.changedTouches[0].clientX;
const finalPosition = this.updatePosition(pointerPosition);
this.moveSlide(finalPosition);
}
onEnd(event) {
const movetype = (event.type === 'mouseup') ? 'mousemove' : 'touchmove';
this.wrapper.removeEventListener(movetype, this.onMove);
this.dist.finalPosition = this.dist.movePosition;
this.transition(true);
this.changeSlideOnEnd();
}
changeSlideOnEnd() {
if (this.dist.movement > 120 && this.index.next !== undefined) {
this.activeNextSlide();
} else if (this.dist.movement < -120 && this.index.prev !== undefined) {
this.activePrevSlide();
} else {
this.changeSlide(this.index.active);
}
}
addSlideEvents() {
this.wrapper.addEventListener('mousedown', this.onStart);
this.wrapper.addEventListener('touchstart', this.onStart);
this.wrapper.addEventListener('mouseup', this.onEnd);
this.wrapper.addEventListener('touchend', this.onEnd);
}
// Slides config
slidePosition(slide) {
const margin = (this.wrapper.offsetWidth - slide.offsetWidth) / 2;
return -(slide.offsetLeft - margin);
}
slidesConfig() {
this.slideArray = [...this.slide.children].map((element) => {
const position = this.slidePosition(element);
return { position, element };
});
}
slidesIndexNav(index) {
const last = this.slideArray.length - 1;
this.index = {
prev: index ? index - 1 : undefined,
active: index,
next: index === last ? undefined : index + 1,
}
}
changeSlide(index) {
const activeSlide = this.slideArray[index];
this.moveSlide(activeSlide.position);
this.slidesIndexNav(index);
this.dist.finalPosition = activeSlide.position;
this.changeActiveClass();
this.wrapper.dispatchEvent(this.changeEvent);
}
changeActiveClass() {
this.slideArray.forEach(item => item.element.classList.remove(this.activeClass));
this.slideArray[this.index.active].element.classList.add(this.activeClass);
}
activePrevSlide() {
if (this.index.prev !== undefined)
this.changeSlide(this.index.prev);
else
this.changeSlide(this.slideArray.length - 1);
}
activeNextSlide() {
if (this.index.next !== undefined)
this.changeSlide(this.index.next);
else
this.changeSlide(0);
}
onResize() {
setTimeout(() => {
this.slidesConfig();
this.changeSlide(this.index.active);
}, 1000);
}
addResizeEvent() {
window.addEventListener('resize', this.onResize);
}
bindEvents() {
this.onStart = this.onStart.bind(this);
this.onMove = this.onMove.bind(this);
this.onEnd = this.onEnd.bind(this);
this.activePrevSlide = this.activePrevSlide.bind(this);
this.activeNextSlide = this.activeNextSlide.bind(this);
this.onResize = debounce(this.onResize.bind(this), 200);
}
init() {
this.bindEvents();
this.transition(true);
this.addSlideEvents();
this.slidesConfig();
this.addResizeEvent();
this.changeSlide(0);
return this;
}
}
class SlideNav extends Slide {
constructor(slide, wrapper) {
super(slide, wrapper);
this.bindControlEvents();
}
addArrow(prev, next) {
this.prevElement = document.querySelector(prev);
this.nextElement = document.querySelector(next);
this.addArrowEvent();
}
addArrowEvent() {
this.prevElement.addEventListener('click', this.activePrevSlide);
this.nextElement.addEventListener('click', this.activeNextSlide);
}
createControl() {
const control = document.createElement('ul');
control.dataset.control = 'slide';
this.slideArray.forEach((item, index) => {
control.innerHTML += `<li><a href="#slide${index + 1}">${index + 1}</a></li>`;
});
this.wrapper.appendChild(control);
return control;
}
eventControl(item, index) {
item.addEventListener('click', (event) => {
event.preventDefault();
this.changeSlide(index);
});
this.wrapper.addEventListener('changeEvent', this.activeControlItem);
}
activeControlItem() {
this.controlArray.forEach(item => item.classList.remove(this.activeClass));
this.controlArray[this.index.active].classList.add(this.activeClass);
}
addControl(customControl) {
this.control = document.querySelector(customControl) || this.createControl();
this.controlArray = [...this.control.children];
this.activeControlItem();
this.controlArray.forEach(this.eventControl);
}
bindControlEvents() {
this.eventControl = this.eventControl.bind(this);
this.activeControlItem = this.activeControlItem.bind(this);
}
}
const slide = new SlideNav('.slide', '.slide-wrapper');
slide.init();
slide.addArrow('.prev', '.next');
slide.addControl('.custom-controls');
setInterval(slide.activeNextSlide, 3000);
body {
margin: 0px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
ul {
padding: 0px;
margin: 0px;
list-style: none;
}
img {
display: block;
max-width: 100%;
}
.slide-wrapper {
overflow: hidden;
}
.slide {
display: flex;
}
.slide:hover {
will-change: transform;
}
.slide li {
flex-shrink: 0;
max-width: 600px;
margin: 0 20px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0,0,0,.4);
opacity: .8;
transform: scale(.8);
transition: .4s;
}
.slide li.active {
opacity: 1;
transform: scale(1);
}
[data-control="slide"] {
display: flex;
justify-content: center;
margin-top: 20px;
}
[data-control="slide"] li a {
display: block;
width: 12px;
height: 12px;
background: #FB5;
border-radius: 50%;
overflow: hidden;
text-indent: -999px;
margin: 5px;
}
[data-control="slide"] li.active a, [data-control="slide"] li a:hover {
background: #E54;
}
.custom-controls {
display: flex;
justify-content: center;
margin-top: 40px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.custom-controls li {
opacity: .8;
transform: scale(.8);
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin: 2px;
box-shadow: 0 2px 2px rgba(0,0,0,.5);
transition: .3s;
cursor: pointer;
}
.custom-controls li.active {
opacity: 1;
transform: scale(1);
}
.arrow-nav {
display: flex;
justify-content: space-around;
margin: 20px 10px 0 10px;
}
.arrow-nav button {
cursor: pointer;
border: none;
border-radius: 50%;
color: white;
width: 30px;
height: 30px;
background: #999 url('../img/arrow.svg') center center no-repeat;
outline: none;
}
.arrow-nav button:hover {
background: #333 url('../img/arrow.svg') center center no-repeat;
transition: ease-in-out .3s;
}
.arrow-nav button.prev {
transform: rotate(-180deg);
}
.wrap-controls {
display: flex;
justify-content: center;
align-items: center;
}
<div class="slide-wrapper">
<ul class="slide">
<li><img src="https://i.sstatic.net/2fkLR.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/gN1Ri.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/FgqYP.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/su1na.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/vZYry.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/5dXtQ.jpg" alt=""></li>
</ul>
</div>
<div class="wrap-controls">
<div class="arrow-nav">
<button class="prev"></button>
</div>
<ul class="custom-controls">
<li><img src="https://i.sstatic.net/2fkLR.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/gN1Ri.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/FgqYP.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/su1na.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/vZYry.jpg" alt=""></li>
<li><img src="https://i.sstatic.net/5dXtQ.jpg" alt=""></li>
</ul>
<div class="arrow-nav">
<button class="next"></button>
</div>
</div>
Regards
Upvotes: 1