个人博客:柚子青年。
原文地址:【手撕代码】轻量轮播图
轮播图的几个关键点:翻页按钮,分页器,悬停静止,无限轮播,清理动画叠加。
文中使用了构造函数的方式把所有细节解构去耦合,欢迎大家点评
<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>
.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 });