<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>carousel</title>
<style>
.carousel {
position: relative;
width: 600px;
height: 300px;
}
.carousel .container {
display: flex;
position: relative;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
.carousel .container .img-box {
width: 100%;
height: 100%;
position: relative;
left: 0;
display: flex;
transition: left 0.5s ease-in-out;
}
.carousel .container .img-box img {
flex-shrink: 0;
height: 100%;
width: 100%;
object-fit: cover;
}
.carousel .left-right-btn-group .btn {
position: absolute;
inset: 0 auto;
width: 30px;
height: 30px;
margin: auto 0;
border-radius: 50%;
border: none;
cursor: pointer;
background-color: rgba(255, 255, 255, 0.3);
color: white;
opacity: 0.5;
transition: opacity 0.5s ease;
}
.carousel .left-right-btn-group .btn:hover {
opacity: 1;
}
.carousel .left-right-btn-group .left {
left: 10px;
}
.carousel .left-right-btn-group .right {
right: 10px;
}
.carousel .index-selector-btn-group {
display: flex;
gap: 10px;
position: absolute;
left: 0;
right: 0;
bottom: 10px;
margin: 0 auto;
width: max-content;
}
.carousel .index-selector-btn-group .btn {
width: 20px;
height: 1px;
border: none;
cursor: pointer;
background-color: gray;
}
.carousel .index-selector-btn-group .active {
background-color: #fff;
}
</style>
</head>
<body>
<div class="carousel">
</div>
<script>
(() => {
class Carousel {
constructor(options) {
this.imgUrlList = options.imgUrlList;
this.app = options.el;
this.container = null;
this.width = options.width || 600;
this.height = options.height || 300;
this.autoPlay = options.autoPlay || true;
this.autoPlayDuration = options.autoPlayDuration || 3000;
this.curIndex = 0;
this.oImgBox = null;
this.timer = null;
this.init();
this.effect = []
this.isTransitioning = false
}
init() {
this.render();
this.bindEvents();
}
bindEvents() {
this.oImgBox.addEventListener('transitionend', this.handleTransitionEnd.bind(this));
this.app.addEventListener('mouseenter', this.stop.bind(this));
this.app.addEventListener('mouseleave', this.start.bind(this));
}
render() {
this.container = document.createElement('div');
this.container.className = 'container';
this.oImgBox = document.createElement('div');
this.oImgBox.className = 'img-box';
const imgElements = this.imgUrlList.map((imgUrl, index) => {
return `<img src="${imgUrl}" />`;
});
this.oImgBox.innerHTML = imgElements.join('');
this.container.appendChild(this.oImgBox);
this.app.appendChild(this.container);
}
next() {
this.curIndex++;
this.goTo(this.curIndex);
}
prev() {
this.curIndex--;
this.goTo(this.curIndex);
}
goTo(index) {
this.curIndex = index;
this.oImgBox.style.left = this.curIndex * -600 + 'px';
}
start() {
if(this.autoPlay===false){
return;
}
this.stop();
this.timer = setInterval(() => {
this.next();
}, this.autoPlayDuration);
}
stop() {
clearInterval(this.timer);
}
handleTransitionEnd() {
if (this.curIndex == this.imgUrlList.length - 1) {
this.curIndex = 0;
this.oImgBox.style.transition = 'none';
this.oImgBox.style.left = this.curIndex * -600 + 'px';
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.oImgBox.style.transition = 'left 0.5s ease-in-out';
});
})
}else if (this.curIndex <= 0) {
this.curIndex = this.imgUrlList.length - 1;
this.oImgBox.style.transition = 'none';
this.oImgBox.style.left = this.curIndex * -600 + 'px';
requestAnimationFrame(() => {
requestAnimationFrame(() => {
this.oImgBox.style.transition = 'left 0.5s ease-in-out';
});
})
}
this.trigger();
}
eff(fn) {
this.effect.push(fn);
}
trigger() {
this.effect.forEach(fn => fn(this.curIndex));
}
plugins(...plugins) {
plugins.forEach(plugin => {
plugin.render && plugin.render(this);
plugin.action && plugin.action(this);
});
}
}
const selectorBtn = {
render(carousel) {
const oBtnGroup = document.createElement('div');
oBtnGroup.className = 'index-selector-btn-group';
carousel.app.appendChild(oBtnGroup);
const btns = carousel.imgUrlList.slice(0, carousel.imgUrlList.length - 1);
btns.forEach((item, index) => {
const oBtn = document.createElement('button');
oBtn.className = 'btn';
if (index === 0) {
oBtn.classList.add('active');
}
oBtnGroup.appendChild(oBtn);
})
},
updateActive(carousel, curIndex) {
console.log(curIndex);
if(curIndex === carousel.imgUrlList.length -1)curIndex = 0;
const btnGroup = carousel.app.querySelector('.index-selector-btn-group');
const btns = btnGroup.querySelectorAll('.btn');
btns.forEach((btn, index) => {
btn.className = 'btn';
if (index === curIndex) {
btn.classList.add('active');
}
})
},
action(carousel) {
carousel.eff((index) => {
this.updateActive(carousel, index);
})
}
}
const rightBtn = {
render(carousel) {
const oBtnGroup = document.createElement('div');
oBtnGroup.className = 'left-right-btn-group';
carousel.app.appendChild(oBtnGroup);
const oRightBtn = document.createElement('button');
oRightBtn.className = 'btn right';
oRightBtn.innerHTML = '>';
oBtnGroup.appendChild(oRightBtn);
},
action(carousel) {
const oRightBtn = carousel.app.querySelector('.right');
oRightBtn.addEventListener('click', () => {
carousel.stop();
carousel.next();
carousel.start();
});
}
}
const leftBtn = {
render(carousel) {
const oBtnGroup = document.createElement('div');
oBtnGroup.className = 'left-right-btn-group';
carousel.app.appendChild(oBtnGroup);
const oLeftBtn = document.createElement('button');
oLeftBtn.className = 'btn left';
oLeftBtn.innerHTML = '<';
oBtnGroup.appendChild(oLeftBtn);
},
action(carousel) {
const oLeftBtn = carousel.app.querySelector('.left');
oLeftBtn.addEventListener('click', () => {
carousel.stop();
carousel.prev();
carousel.start();
});
}
}
const imgUrlList = [
'https://unsplash.com/photos/0dC0h6CjOMM/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjU0NTkwNjM2&force=true',
'https://unsplash.com/photos/iBmuPRBJj8M/download?ixid=MnwxMjA3fDB8MXxhbGx8MTY0fHx8fHx8Mnx8MTY1NDU4ODkwNg&force=true',
'https://unsplash.com/photos/rZMiCdPAlss/download?ixid=MnwxMjA3fDB8MXxhbGx8MTg0fHx8fHx8Mnx8MTY1NDU4ODkxMg&force=true',
'https://unsplash.com/photos/faKvebx79FA/download?ixid=MnwxMjA3fDB8MXxzZWFyY2h8MTh8fG5lb258ZW58MHwwfHx8MTY1NDU5MDcwMw&force=true',
'https://unsplash.com/photos/0dC0h6CjOMM/download?ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjU0NTkwNjM2&force=true',
];
const oCarousel = document.querySelector('.carousel');
const carousel = new Carousel({
el: oCarousel,
imgUrlList,
});
carousel.plugins(selectorBtn, rightBtn, leftBtn);
carousel.start();
})();
</script>
</body>
</html>