好久没有写原生的东西了,感觉都有点陌生了,今儿写个轮播图,练练手。
支持无缝滚动,支持h5手指滑动切换,带回弹效果。先看看效果
pc
h5
-
在线预览地址,为了能让你们在线预览还特地部署了一下
http://cxm.ink:8848/caruser/ -
顺便推广一下我写的另外一个小工具,嘿嘿,h5的,看看效果
废话不多说这就上代码。
- html部分
<!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>轮播图</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="mian" id="mian">
<div class="carousel"></div>
<div class="indexBox"></div>
</div>
<script src="./index.js"></script>
</body>
</html>
- css部分
*{
padding: 0;
margin: 0;
}
body{
height: 100vh;
display: flex;
/* align-items: center; */
justify-content: center;
}
.mian{
position: relative;
width: 100%;
height: 400px;
overflow: hidden;
}
.carousel{
display: flex;
width: 100%;
height: 100%;
transform: translateX(0px);
transition: all 0.5s;
}
.carousel img{
width: 100%;
height: 100%;
flex-shrink: 0;
object-fit: cover;
}
.left,.right{
position: absolute;
top: 50%;
width: 30px;
height: 30px;
border-radius: 50%;
background-color: rgba(0, 0, 0, .3);
cursor: pointer;
transform: translateY(-12px);
background-repeat: no-repeat;
background-size: 60%;
background-position: center;
transition: all .3s;
}
.left{
left: 5px;
background-image: url('./img/left.png');
}
.right{
right: 5px;
background-image: url('./img/right.png');
}
.left:hover{
background-color: rgba(0, 0, 0, .7);
}
.right:hover{
background-color: rgba(0, 0, 0, .7);
}
.indexBox{
position: absolute;
bottom: 10px;
left: 0;
right: 0;
display: flex;
justify-content: center;
}
.indexBox span{
display: inline-block;
width: 10px;
height: 10px;
border: 1px solid #999;
border-radius: 50%;
margin: 0 1.5px;
transition: all .3s;
}
.indexBox span.actve{
background: rgb(53, 169, 241);
}
- js部分
const imgData = [
'./img/1.webp',
'./img/2.webp',
'./img/3.webp',
'./img/4.webp',
]
// 防抖函数
function debounceEvent (Callback, delayTime) {
let timer = null
return function (...agmes) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
Callback.apply(this, [...agmes])
}, delayTime)
}
}
// 节流函数
function throttleEvent (Callback, delayTime) {
// 上一次执行的时间,
let constTime = 0
// 可以认为时节流的阀门。
let valve = false
return function (...agmes) {
let createTime = new Date().getTime()
let day = (createTime - constTime) / 1000
valve = !constTime || day > delayTime ? true : false
if (valve) {
Callback.apply(this, [...agmes])
constTime = createTime
}
}
}
// 滚动容器
const carousel = document.querySelector('.carousel')
// 一些要用的公共配置属性
const config = {
cont: imgData.length,
width: carousel.clientWidth,// 图片宽度
timeNum: 3000,
_actveIndex: 0, // 当前展示的图标下标
ionTime: 600,// 过度时间
}
let time = null // 全局定时器
window.addEventListener('resize', debounceEvent(() => {
config.width = carousel.clientWidth
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
}, 500))
Object.defineProperty(config, 'actveIndex', {
get () {
return this._actveIndex
},
set (value) {
this._actveIndex = value
const indexArr = document.querySelectorAll('.indexBox span')
indexArr.forEach(item => {
item.classList.remove('actve')
})
indexArr[value] ? indexArr[value].classList.add('actve') : indexArr[0].classList.add('actve')
}
})
// 鼠标移入和移出事件
function mouseen () {
// 鼠标移入时暂停定时器
carousel.addEventListener('mouseenter', () => {
clearInterval(time)
})
// 移出开启定时器
carousel.addEventListener('mouseleave', () => {
animation()
})
}
// 上下页事件
function btnEvent () {
const left = document.querySelector('.left')
const right = document.querySelector('.right')
// 上一张
left?.addEventListener('click', throttleEvent(() => {
clearInterval(time) // 点击时关闭定时器
// 判断是否为第一张,如果是第一张,暂停过度,跳到最后一张,在开启动画往
if (config.actveIndex == 0) {
carousel.style.transition = 'none 0s'
config.actveIndex = config.cont
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
}
setTimeout(() => {
config.actveIndex--
carousel.style.transition = `all ${config.ionTime / 1000}s`
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
}, 5);
}, config.ionTime / 1000))
// 下一张
right?.addEventListener('click', throttleEvent(() => {
// 判断是否正在过度,如果在则暂停
if (config.transition) return
clearInterval(time) // 点击时关闭定时器
transformRight()
animation() //
}, config.ionTime / 1000))
}
// 下一页逻辑
function transformRight () {
carousel.style.transition = `all ${config.ionTime / 1000}s`
config.actveIndex++
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
// 判断是否是最后一张,如果是最后一张则暂停过度回到第一张
if (config.actveIndex == config.cont) {
// 在当前动画过度完成后回到第一张
setTimeout(() => {
carousel.style.transition = 'none 0s'
config.actveIndex = 0
carousel.style.transform = `translateX(-${0}px)`
}, config.ionTime);
}
}
// 定时器函数
function animation () {
if (imgData.length < 2) return
time = setInterval(() => transformRight(), config.timeNum);
}
// 跑马灯事件
function findIndexEvent () {
document.querySelector('.indexBox').onclick = function (e) {
if (e.target.nodeName == 'SPAN') {
clearInterval(time)
config.actveIndex = e.target.dataset.index
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
animation()
}
}
}
// 创建dom开启轮播动画
function createDom (imgArr) {
const arr = [...imgArr]
if (arr.length > 1) {
arr.push(arr[0]) // 为了完成无缝滚动,所以把第一张图复制一个放到最后一张
let leftdiv = document.createElement('div')
let rightdiv = document.createElement('div')
leftdiv.className = 'left'
rightdiv.className = 'right'
let mian = document.getElementById('mian')
mian.append(leftdiv)
mian.append(rightdiv)
}
const elFrag = document.createDocumentFragment()
arr.forEach(item => {
let img = document.createElement('img')
img.src = item
elFrag.append(img)
})
carousel.append(elFrag)
const spanFra = document.createDocumentFragment()
imgArr.forEach((_item, index) => {
let span = document.createElement('span')
span.dataset.index = index
if (index === 0) { span.classList.add('actve') }
spanFra.append(span)
})
document.querySelector('.indexBox').append(spanFra)
animation() // 定时器
mouseen() // 鼠标事件
btnEvent() // 左右切换事件
findIndexEvent() // 跑马灯事件
slideEvent(imgArr.length) // h5手指滑动切换
}
// h5手指滑动切换
function slideEvent (imgLength) {
let stateX // 手指按下的位置
let translate // 当前位移值
// 手指触摸开始时
carousel.addEventListener('touchstart', (e) => {
clearInterval(time)
let pageX = e.changedTouches[0].pageX
stateX = pageX
translate = config.actveIndex * config.width
})
// 手指开始滑动时
carousel.addEventListener('touchmove', (e) => {
let pageX = e.changedTouches[0].pageX
carousel.style.transition = 'none 0.1s'
if (stateX > pageX) { // 往左滑动(下一张)
carousel.style.transform = `translateX(-${translate + (stateX - pageX)}px)`
} else {
if (config.actveIndex == 0 && imgLength > 1) {
carousel.style.transition = 'none 0s'
config.actveIndex = config.cont
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
return setTimeout(() => {
carousel.style.transition = `all ${config.ionTime / 1000}s`
carousel.style.transform = `translateX(-${translate - (pageX - stateX)}px)`
}, 5);
}
if (imgLength == 1) {
return carousel.style.transform = `translateX(${(pageX - stateX)}px)`
}
carousel.style.transition = `all ${config.ionTime / 1000}s`
carousel.style.transform = `translateX(-${translate - (pageX - stateX)}px)`
}
})
// 触摸结束
carousel.addEventListener('touchend', (e) => {
let pageX = e.changedTouches[0].pageX
if (imgLength < 2) {
carousel.style.transition = `all ${config.ionTime / 1000}s`
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
return
}
if (stateX > pageX) { // 往左滑动
if (stateX - pageX > config.width / 3.5) {// 如果滑动大于下定外都自动翻页,如果小于则回弹
return transformRight()
}
} else {
if (pageX - stateX > config.width / 3.5) {
config.actveIndex--
}
}
carousel.style.transition = `all ${config.ionTime / 1000}s`
carousel.style.transform = `translateX(-${config.actveIndex * config.width}px)`
animation()
})
}
createDom(imgData)
- 点赞不迷路,老铁奥里给
顺便推广一下我自己的小程序。