JSFiddle - React, Tailwind, and code Playground
by HDL52
HTML
<div class="wrap">
<ul class="list">
<!--这里是图片列表-->
<li class="item"><img src="https://raw.githubusercontent.com/Hongda-OSU/PicGo-2.3.1/master/imgied0v0dgsgyqeoh7804b0ca54buj0qd.jpg" /></li>
<li class="item"><img src="https://raw.githubusercontent.com/Hongda-OSU/PicGo-2.3.1/master/imge39twvhffwemdsipqncbwko5vayrxgd.jpg" /></li>
<li class="item"><img src="https://raw.githubusercontent.com/Hongda-OSU/PicGo-2.3.1/master/img1u6qw3ad64bz8nsedrd2wddgrax5wew.jpg" /></li>
<li class="item"><img src="https://raw.githubusercontent.com/Hongda-OSU/PicGo-2.3.1/master/imgodr8vukcwco0h34p0ngc84462vs454j.jpg" /></li>
</ul>
<ul class="dot">
<!--这里是下面的小点-->
<li class="active"></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
CSS
body {
margin: 0;
background: #fefefe;
}
.wrap {
width: 100vw; /* 最好是固定宽 */
overflow: hidden;
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.1);
position: relative;
background: #000;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.list {
display: flex;
float: left;
}
.list img {
width: 100vw;
vertical-align: top;
filter: saturate(2);
}
.dot {
width: 100vw;
display: flex;
justify-content: center;
position: absolute;
bottom: 20px;
}
.dot li {
width: 10px;
height: 10px;
border-radius: 10px;
background: #fff;
margin: 0 5px;
transition: 0.2s;
box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.3);
}
.dot li.active {
width: 20px;
}
JavaScript
let wrap = document.querySelector(".wrap") // 获取外框
let list = wrap.querySelector(".list") // 图片列表
let dots = wrap.querySelectorAll(".dot li") // 小点
let startpoint = {} // 记录起点位置
let distance = {} // 记录移动距离
let startOffset = 0 // 开始时图片列表的位置
let translateX = 0 // 当前移动距离
let now = 0 // 当前图片索引
let imgW = wrap.offsetWidth // 外框宽度
let proportion = 0.3 // 切换图片的比例
let isMove = false // 是否在移动
let interval // 自动轮播的定时器
// 复制图片列表,实现无缝滚动
list.innerHTML += list.innerHTML
// 设置列表宽度,容纳两倍的图片
list.style.width = `${list.children.length * 100}vw`
// 初始化自动轮播
startAutoSlide()
// 监听 pointerdown 事件
wrap.addEventListener("pointerdown", (ev) => {
ev.preventDefault() // 阻止默认行为,避免页面滚动
clearInterval(interval) // 暂停自动轮播
startpoint = { x: ev.clientX, y: ev.clientY } // 记录起始点
isMove = true // 标记为移动状态
// 取消过渡动画
list.style.transition = "none"
startOffset = now * -imgW
})
// 监听 pointermove 事件
wrap.addEventListener("pointermove", (ev) => {
if (!isMove) return // 未在移动时不执行
distance = { x: ev.clientX - startpoint.x, y: ev.clientY - startpoint.y }
// 判断横向移动是否大于纵向移动,避免页面跟随滑动
if (Math.abs(distance.x) > Math.abs(distance.y)) {
translateX = startOffset + distance.x
list.style.transform = `translateX(${translateX}px)`
}
})
// 监听 pointerup 事件
wrap.addEventListener("pointerup", () => {
if (!isMove) return // 未触发移动时不执行
// 根据滑动距离判断是否切换图片
if (Math.abs(distance.x) > imgW * proportion) {
now -= Math.sign(distance.x) // 根据滑动方向更新索引
}
// 限制索引范围
now = Math.max(0, Math.min(now, list.children.length / 2 - 1))
updateSlide() // 更新图片位置
isMove = false // 重置移动状态
// 重新启动自动轮播
startAutoSlide()
})
// 更新图片位置及小点状态
function updateSlide() {
list.style.transition = "transform 0.3s ease" // 恢复动画
list.style.transform = `translateX(${-now * imgW}px)` // 移动到目标位置
// 更新小点的状态
dots.forEach((dot, index) => {
dot.classList.toggle("active", index === now % dots.length)
})
// 边界处理,实现无缝滚动
if (now === 0 || now === dots.length) {
setTimeout(() => {
...