0.示例
预览地址: https://codepen.io/klren0312/full/ymvEbr
2
1.画界面
1.画显示区域
首先就是画个固定的区域, 用来展示轮播图当前能看到的图, 其余超出的部分, 使用 overflow: hidden
隐藏.
.box {
width: 300px;
height: 200px;
overflow: hidden;
}
代码语言:javascript复制<div class="box"></div>
2.画轮播图主体
假设五张图, 将他们横向排列(图片太麻烦, 我就css画了, 有些样式可以忽略)
image.png
代码语言:javascript复制.swiper {
position: relative;
width: 1500px;
height: 200px;
}
.swiper .swiper-item {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 100%;
float: left;
box-sizing: border-box;
background: #efefef;
border: 1px solid #333;
}
// 两个div区分一下
.swiper .swiper-item:nth-child(2n) {
background: #fff;
}
代码语言:javascript复制<div class="box">
<div class="swiper">
<div class="swiper-item">1</div>
<div class="swiper-item">2</div>
<div class="swiper-item">3</div>
<div class="swiper-item">4</div>
<div class="swiper-item">5</div>
</div>
</div>
这样, 样式部分就搞定了
2.轮播JS代码
1.原理
由于轮播图已经横向排列, 所以只要控制.swiper
向x轴偏移距离, 就可以实现图片切换, 这里使用transform
的translate
属性来控制x轴偏移.可以通过transition
来设置过渡动画
问题与难点: 当轮播图到达最后一个图片时, 需要平滑切换到第一张, 如果没有过渡动画倒无所谓, 否则将会出现从最后一张快速倒回第一张的动画, 降低体验.
解决方法: 初始化的时候, 复制第一位图片放在最后一位;复制原来最后一位图片, 放到第一位.当然如果你只往右切换, 则不用将第一位放在最后一位.示例如下(数字代表图片顺序): 初始排列:
代码语言:javascript复制|1|2|3|4|5|
初始化后排列:
代码语言:javascript复制|5|1|2|3|4|5|1|
2.代码片段
代码使用ES6语法, 这些无所谓, 具体逻辑知道就行
1.构造器
构造器接收一个变量, 切换轮播图的延时.
在构造器里新建了一些常量, 轮播图的DOM, 轮播图片的DOM数组, 轮播图的个数(注意是没有初始化前的图片个数), 以及赋值延时(默认是1000ms)
随后调用初始化函数
代码语言:javascript复制constructor (delay = 1000) {
this.swiper = document.querySelector('.swiper') // 轮播图
this.swiperItemList = document.querySelectorAll('.swiper-item') // 轮播项
this.totalLength = this.swiperItemList.length // 轮播图个数
this.delay = delay
this.init()
}
2.初始化函数
复制第一位图片放在最后一位;复制原来最后一位图片, 放到第一位;即 12345
=> 5123451
.
随后, 将轮播图显示的位置定在第一张图片位置, 即1
的位置
currentPosition
变量用于标记当前滚动的图片
init () {
// 将轮播图第一项克隆, 并放在最后
const cloneFirst = document.querySelectorAll('.swiper-item')[0].cloneNode(true)
this.swiper.appendChild(cloneFirst)
// 因为 totalLength 是原始的轮播图个数, 所以可以正确定位到原来的轮播图最后一个
const cloneLast = document.querySelectorAll('.swiper-item')[this.totalLength - 1].cloneNode(true)
this.swiper.insertBefore(cloneLast, this.swiper.firstChild)
// 设置轮播图总长, 加上新加的两个
this.swiper.style.width = (this.totalLength 2) * 300 'px'
// 当前轮播图位置分布为 5 12345 1
// 初始化轮播图为 1 位置
this.currentPosition = 1
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
}
3.滚动动画
滚动时, 给.swiper
标记一个isAnimating
的class,来标明正在滚动.
给.swiper
设置x轴偏移位置, 以及添加过渡动画. 滚动的延时使用设定的delay
, 延时结束后, 清除过渡动画(过渡动画的清除, 主要给后面最后一位跳到第一位时用)和isAnimating
标记
goSlider () {
// 添加过渡效果, delay 需要一致; 位置偏移一个单位
this.swiper.style.transition = `transform ${this.delay / 1000}s ease`
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
// 添加正在切换的class, 给上面 animate 方法用来判断
this.swiper.classList.add('isAnimating')
// 切换结束, 清空状态, 清空过渡效果
setTimeout(() => {
this.swiper.style.transition = ''
this.swiper.classList.remove('isAnimating')
}, this.delay)
}
4.轮播操作
判断滚动的位置, 若在最后一位即第5
位, 则将定位改为与之相同的第0
位, 并进行偏移.然后将位数加1, 到第1
位. 此时没有过渡动画, 就实现了最后一位5
和第一位5
的快速切换, 然后过渡到第1
位;
给currentPosition
设置为1后, 进入到切换动画, 这样视觉上就是5
过渡到1
animate () {
// 如果正在切换, 则不动
if (this.swiper.classList.contains('isAnimating')) return
// 如果在第五项, 则将定位改为第 0 项, 并设置位置, 此时没有 transition 动画, 秒切
if (this.currentPosition === this.totalLength) {
this.currentPosition = 0
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
}
// 将位置设为第一项
this.currentPosition
// 轮播
setTimeout(() => {
this.goSlider()
}, 20)
}
5.轮播开始
循环调用轮播操作函数
代码语言:javascript复制start () {
setInterval(() => this.animate(),this.delay)
}
3.使用这个轮播class
代码语言:javascript复制window.onload = function () {
const slider = new SliderBox(2000)
slider.start()
}
4.整体代码
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box {
width: 300px;
height: 200px;
overflow: hidden;
}
.swiper {
position: relative;
width: 1500px;
height: 200px;
}
.swiper .swiper-item {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 100%;
float: left;
box-sizing: border-box;
background: #efefef;
border: 1px solid #333;
}
.swiper .swiper-item:nth-child(2n) {
background: #fff;
}
</style>
</head>
<body>
<div class="box">
<div class="swiper">
<div class="swiper-item">1</div>
<div class="swiper-item">2</div>
<div class="swiper-item">3</div>
<div class="swiper-item">4</div>
<div class="swiper-item">5</div>
</div>
</div>
<script>
class SliderBox {
/**
* 构造器
* @param {number} delay 切换延时
*/
constructor(delay = 1000) {
this.swiper = document.querySelector('.swiper') // 轮播图
this.swiperItemList = document.querySelectorAll('.swiper-item') // 轮播项
this.totalLength = this.swiperItemList.length // 轮播图个数
this.delay = delay
this.init()
}
/**
* 初始化配置
*/
init() {
// 将轮播图第一项克隆, 并放在最后
const cloneFirst = document.querySelectorAll('.swiper-item')[0].cloneNode(true)
this.swiper.appendChild(cloneFirst)
// 因为 totalLength 是原始的轮播图个数, 所以可以正确定位到原来的轮播图最后一个
const cloneLast = document.querySelectorAll('.swiper-item')[this.totalLength - 1].cloneNode(true)
this.swiper.insertBefore(cloneLast, this.swiper.firstChild)
// 设置轮播图总长, 加上新加的两个
this.swiper.style.width = (this.totalLength 2) * 300 'px'
// 当前轮播图位置分布为 5 12345 1
// 初始化轮播图为 1 位置
this.currentPosition = 1
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
}
/**
* 切换动画
*/
animate() {
// 如果正在切换, 则不动
if (this.swiper.classList.contains('isAnimating')) return
// 如果在第五项, 则将定位改为第 0 项, 并设置位置, 此时没有 transition 动画, 秒切
if (this.currentPosition === this.totalLength) {
this.currentPosition = 0
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
}
// 将位置设为第一项
this.currentPosition
// 轮播
setTimeout(() => {
this.goSlider()
}, 20)
}
/**
* 轮播
*/
goSlider() {
// 添加过渡效果, delay 需要一致; 位置偏移一个单位
this.swiper.style.transition = `transform ${this.delay / 1000}s ease`
this.swiper.style.transform = `translateX(${-this.currentPosition * 300}px`
// 添加正在切换的class, 给上面 animate 方法用来判断
this.swiper.classList.add('isAnimating')
// 切换结束, 清空状态, 清空过渡效果
setTimeout(() => {
this.swiper.style.transition = ''
this.swiper.classList.remove('isAnimating')
}, this.delay)
}
/**
* 开始轮播
*/
start() {
setInterval(() => this.animate(), this.delay)
}
}
</script>
<script>
window.onload = function () {
const slider = new SliderBox(2000)
slider.start()
}
</script>
</body>
</html>