轮播图的几个关键点:翻页按钮,分页器,悬停静止,无限轮播,清理动画叠加。
文中使用了构造函数的方式把所有细节解构去耦合,欢迎大家点评
HTML
<div class="swiper swiper1">
<div class="wrapper">
<div class="slider">1</div>
<div class="slider">2</div>
<div class="slider">3</div>
<div class="slider">4</div>
<div class="slider">5</div>
</div>
<div class="btn">
<span class="prev"><</span>
<span class="next">></span>
</div>
</div>
CSS
.swiper { position: relative; width: 700px; height: 400px; background: #efefef; overflow: hidden; margin-bottom: 10px; }
.wrapper { position: relative; left: 0; display: flex; width: 9999999%; }
.slider { display: flex; align-items: center; justify-content: center; }
.btn { position: absolute; top: 50%; left: 0; width: 100%; z-index: 1; }
.btn span { position: absolute; top: 0; font-size: 25px; color: #666; height: 70px; width: 50px; line-height: 70px; text-align: center; margin-top: -35px; cursor: pointer; background: rgba(102, 102, 102, .2); transition: all .3s ease; }
.btn span:hover { background: rgba(102, 102, 102, .6); }
.btn span.next { right: 0; }
.pagination { position: absolute; left: 0; bottom: 10px; display: flex; justify-content: center; width: 100%; }
.pagination span { width: 8px; height: 8px; margin: 0 5px; border-radius: 50%; background: #fff; cursor: pointer;}
.pagination span.active { background: #00f; }
JS
定义一个构造函数 Swiper
const Swiper = function ({el, pagination, delay}) {
this._el = el; // 最外层容器 swiper1
this._prev = el.querySelector(".prev"); // 上一页按钮
this._next = el.querySelector(".next"); // 下一页按钮
this._slider = el.querySelectorAll(".slider"); // 获取 swiper1 下的所有 slider
this._wrapper = el.querySelector(".wrapper"); // 获取 swiper1 下的 wrapper
this._width = el.offsetWidth; // 获取 swiper1 宽度
this._height = el.offsetHeight; // 获取 swiper1 高度
this._size = this._slider.length;
this._index = 0; // 记录当前展示slider的下标
this._timer = null; // 轮播定时器
this._animate = null; // 动画定时器
this._isHover = false; // 是否悬浮 swiper1 上
this._pagination = pagination || false; // 是否启用分页器
this._delay = delay || 3000; // 轮播间隔时长
this.init();
};
初始化 this.init
Swiper.prototype.init = function () {
this.style();
this.auto();
this.mouseEnter();
};
设置基础样式 复制头尾实现无限轮播 this.style
Swiper.prototype.style = function () {
this._slider.forEach(item => {
item.style.width = this._width + "px";
item.style.height = this._height + "px";
});
const firstDom = this._slider[0].cloneNode(true);
const lastDom = this._slider[this._size - 1].cloneNode(true);
this._wrapper.appendChild(firstDom);
this._wrapper.insertBefore(lastDom, this._el.querySelector(".slider"));
this._wrapper.style.left = -this._width + "px"; // 归位到第一张
if (this._pagination) {
const pagination = document.createElement("div");
pagination.className = "pagination";
this._slider.forEach((item, i) => {
const span = document.createElement("span");
if (i === this._index) span.className = "active";
span.onclick = function () {
this.index(i);
}.bind(this)
pagination.appendChild(span);
});
this._el.appendChild(pagination);
}
}
设置当前active 调整分页选中 this.active
Swiper.prototype.active = function () {
this._el.querySelector(`.pagination span.active`).className = "";
const index = this._index < 0 ? this._size : this._index >= this._size ? 1 : this._index + 1;
this._el.querySelector(`.pagination span:nth-child(${index})`).className = "active";
};
自动轮播 this.auto
Swiper.prototype.auto = function () {
if (this._timer) clearTimeout(this._timer);
this._timer = setTimeout(() => {
if (this._isHover) {
clearTimeout(this._timer);
this.auto();
return;
}
this.next();
}, this._delay)
};
轮播图移动核心处理 this.move
Swiper.prototype.move = function () {
if (this._index > this._size) {
this._index = 1;
this.styleLeft(-1 * this._width);
}
if (this._index < -1) {
this._index = this._size - 2;
this.styleLeft(-(this._size) * this._width);
}
this.start((-this._index - 1) * this._width);
this.active();
this.auto();
};
轮播动画 this.animate
Swiper.prototype.animate = function (start, end) {
const step = (start - end > 0 ? -1 : 1) * (Math.abs(start - end) / 10); // 设置一个滚动基数
if (this._animate) clearInterval(this._animate);
this._animate = setInterval(() => {
start += step;
if (step < 0 && end - start > step || step > 0 && end - start < step) {
clearInterval(this._animate);
this.styleLeft(end);
} else {
this.styleLeft(start);
}
}, 40)
};
计算动画开始时 left偏移量 this.start
Swiper.prototype.start = function (end) {
const start = parseFloat(this._wrapper.style.left);
this.animate(start, end);
};
设置 Swiper left偏移量 this.styleLeft
Swiper.prototype.styleLeft = function (left) {
this._wrapper.style.left = left + "px";
};
上一页 this.prev
Swiper.prototype.prev = function () {
this._index--;
this.move();
}
下一页 this.next
Swiper.prototype.next = function () {
this._index++;
this.move();
};
分页器切换 this.index
Swiper.prototype.index = function (index) {
this._index = index;
this.move();
};
添加鼠标事件 this.mouseEnter
Swiper.prototype.mouseEnter = function () {
this._el.onmouseenter = () => {
this._isHover = true;
};
this._el.onmouseleave = () => {
this._isHover = false;
};
if (this._prev) {
this._prev.onclick = () => {
this.prev();
}
}
if (this._next) {
this._next.onclick = () => {
this.next();
}
}
};
调用
new Swiper({
el: document.querySelector(".swiper1"),
pagination: true,
delay: 2000
});