布局

image-20210113105953068

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div id="app">
<div id="swiper-container">
<!-- 幻灯片的结构 -->
<div class="swiper-wrapper">
<div class="swiper-slide"><img src="./img/1.jpg"></div>
<div class="swiper-slide"><img src="./img/2.jpg"></div>
<div class="swiper-slide"><img src="./img/3.jpg"></div>
<div class="swiper-slide"><img src="./img/4.jpg"></div>
<div class="swiper-slide"><img src="./img/5.jpg"></div>
</div>
<!-- 导航点 -->
<div class="swiper-pagination">
<span class="active"></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
* {
margin: 0;
padding: 0;
}

ul {
list-style: none;
}
html,
body,
#app {
width: 100%;
height: 100%;
overflow: hidden;
}
#swiper-container{
width: 100%;
height: 176px; /* no */
position: relative;

.swiper-wrapper{
width: 500%;
overflow: hidden;

.swiper-slide{
width: 20%;
float: left;
img{
width: 100%;
display: block;
}
}
}
// 导航点
.swiper-pagination{
position: absolute;
width: 100%;
height: 10px; /* no */
left:0;
bottom: 10px; /* no */
line-height: 10px;
text-align: center;
span{
display: inline-block;
width: 10px; /* no */
height: 10px; /* no */
background: #fff;
border-radius: 50%;
margin-right: 4px;
}
.active{
background: darkorange;
}
}
}

可拖拽

b754c5cd-ce9c-4868-a5dd-c87a03ae434a

为包裹图片的容器开启定位

1
2
3
.swiper-wrapper {
position: absolute;
}

接下来使用js动态改变这个容器的位置即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const app = document.querySelector('#app')

const container = document.querySelector('#swiper-container')
const wrapper = container.querySelector('.swiper-wrapper')

app.addEventListener('touchstart', function (e) {
e.preventDefault()
})

// 触摸开始事件
container.addEventListener('touchstart', function (e) {
// 获取触摸开始时的触点位置
this.x = e.touches[0].clientX

// 获取包裹元素的水平偏移量
this.left = wrapper.offsetLeft
})

// 触摸滑动时事件
container.addEventListener('touchmove', function (e) {
this._x = e.touches[0].clientX
// 计算新的left值
const newLeft = this._x - this.x + this.left
// 设置left的值
wrapper.style.left = newLeft + 'px'
})

拖拽切换

db41cda1-3c5b-4580-bdd4-7a78af88e011

向左划动切换右边的一张图片,向右滑动切换左边的一张图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 获取图片的数量
const len = container.querySelectorAll('.swiper-slide')
// 当前显示的图片的下标
let index = 0
// 获取所有小圆点
const dots = container.querySelectorAll('.swiper-pagination span')


// 触摸结束事件
container.addEventListener('touchend', function (e) {
// 获取触点结束时触点位置
this._x = e.changedTouches[0].clientX
// 向左滑动
if (this._x < this.x) {
index++
}
// 向右滑动
if (this._x > this.x) {
index--
}
// 检测是否越界
if (index < 0) {
index = 0
}
if (index > len.length - 1) {
index = len.length - 1
}

/**
* index => left
* 0 => 0
* 1 => index * -375
*/
// 计算新的left的值
const newLeft = -index * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'

// 点的切换
// 移除所有导航点的active类
dots.forEach(function (dot) {
dot.classList.remove('active')
})
// 当前索引的导航点 增加 active 类
dots[index].classList.add('active')
})

优化-过渡效果

当按下时移除过度样式,抬起时添加样式即可。

1
2
3
4
5
6
7
8
container.addEventListener('touchstart', function (e) {
// 移除过度效果
wrapper.style.transition = 'none'
})
container.addEventListener('touchend', function (e) {
// 增加过度
wrapper.style.transition = 'all .5s'
}

元素初始化设置

为元素设置初始高度宽度以及动态创建导航点的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 初始化函数
function init() {
// 容器设置 高度
window.onload = function () {
// 获取第一个图片的高度
const h = slides[0].offsetHeight
container.style.height = h + 'px'
}
// 包裹元素的宽度
wrapper.style.width = slides.length * 100 + '%'
// 设置子元素的宽度
slides.forEach((item) => {
item.style.width = 100 / slides.length + '%'
})
// 根据图片数量动态创建导航点的个数
for (var i = 0; i < slides.length; i++) {
const span = document.createElement('span')
if (i == 0) {
span.className = 'active'
}
pagination.appendChild(span)
}
dots = container.querySelectorAll('.swiper-pagination span')
}
init()

无缝滚动

无缝滚动采用的方案是新插入一组一模一样的图片,当向左滑动(第一张)时迅速切换到第二组的第一张。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 无缝滚动
wrapper.innerHTML += wrapper.innerHTML
const slidesNew = container.querySelectorAll('.swiper-slide')

container.addEventListener('touchstart', function (e) {
// 移除过度效果
wrapper.style.transition = 'none'
// 获取按下时的时间点
this.touchStartTime = Date.now()
// 判断索引下标
if (index == 0) {
index = slides.length
const newLeft = -index * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'
}
// 右侧边界
if (index == slidesNew.length - 1) {
index = slides.length - 1
const newLeft = -index * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'
}
// 获取触摸开始时的触点位置
this.x = e.touches[0].clientX

// 获取包裹元素的水平偏移量
this.left = wrapper.offsetLeft
})

自动播放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 自动播放
function autoRun() {
// 防止定时器重复
clearInterval(timer)
// 启动定时器
timer = setInterval(function () {
// 索引自增 切换幻灯片
index++
// 增加过度
wrapper.style.transition = 'all .5s'
const newLeft = -index * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'

// 点的切换
// 移除所有导航点的active类
dots.forEach(function (dot) {
dot.classList.remove('active')
})
// 当前索引的导航点 增加 active 类
dots[index % slides.length].classList.add('active')
}, 1000)
}
autoRun()

// 动画过渡完毕时间
wrapper.addEventListener('transitionend', (e) => {
// 是不是最后一张
if (index === slidesNew.length - 1) {
// 移除过渡
wrapper.style.transition = 'none'
index = slides.length - 1
const newLeft = -index * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'
}
})

封装及优化后的JavaScript代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import '../style/index.less'

const app = document.querySelector('#app')

const container = document.querySelector('#swiper-container')
const wrapper = container.querySelector('.swiper-wrapper')
// 获取所有图片
const slides = container.querySelectorAll('.swiper-slide')
// 当前显示的图片的下标
var index = 0
// 获取所有小圆点
let dots = null
// 获取小圆点的容器
const pagination = container.querySelector('.swiper-pagination')
// 定时器
let timer = null
// 无缝滚动
wrapper.innerHTML += wrapper.innerHTML
const slidesNew = container.querySelectorAll('.swiper-slide')

// 标识变量
let isHori = true
let isFirst = true

// 触摸开始事件
container.addEventListener('touchstart', function (e) {
wrapper.style.transition = 'none'
// 获取按下时的时间点
this.touchStartTime = Date.now()
// 判断索引下标
if (index == 0) {
index = slides.length
}
// 右侧边界
if (index == slidesNew.length - 1) {
index = slides.length - 1
}
container.switchSlide(index, false)
// 获取触摸开始时的触点位置
this.x = e.touches[0].clientX
this.y = e.touches[0].clientY

// 获取包裹元素的水平偏移量
this.left = wrapper.offsetLeft
// 停止定时器
clearInterval(timer)
})

// 触摸滑动时事件
container.addEventListener('touchmove', function (e) {
this._x = e.touches[0].clientX
this._y = e.touches[0].clientY

// 垂直方向上的偏移
const disY = Math.abs(this._y - this.y)
// 水平方向上的偏移
const disX = Math.abs(this._x - this.x)
if (isFirst) {
// 标识变量赋值为 假
isFirst = false
if (disY > disX) {
isHori = false
}
if (disY < disX) {
isHori = true
}
}
if (isHori) {
e.preventDefault()
} else {
return
}
// 计算新的left值
const newLeft = this._x - this.x + this.left
// 设置left的值
wrapper.style.left = newLeft + 'px'
})

// 触摸结束事件
container.addEventListener('touchend', function (e) {
isFirst = true

if (!isHori) return
// 获取触点结束时触点位置
this._x = e.changedTouches[0].clientX
// 判断距离
const disX = Math.abs(this._x - this.x)
// 判断时间
this.touchEndTime = Date.now()

if (
disX > container.offsetWidth / 2 ||
this.touchEndTime - this.touchStartTime <= 300
) {
// 向左滑动
if (this._x < this.x) {
index++
}
// 向右滑动
if (this._x > this.x) {
index--
}
}

// 检测是否越界
if (index < 0) {
index = 0
}
if (index > slidesNew.length - 1) {
index = slidesNew.length - 1
}
this.switchSlide(index)
// 启动定时器
this.autoRun()
})
// 动画过渡完毕时间
wrapper.addEventListener('transitionend', (e) => {
// 是不是最后一张
if (index === slidesNew.length - 1) {
index = slides.length - 1
container.switchSlide(index, false)
}
})

// 初始化函数
container.init = function () {
// 容器设置 高度
window.onload = function () {
// 获取第一个图片的高度
const h = slidesNew[0].offsetHeight
container.style.height = h + 'px'
}
// 包裹元素的宽度
wrapper.style.width = slidesNew.length * 100 + '%'
// 设置子元素的宽度
slidesNew.forEach((item) => {
item.style.width = 100 / slidesNew.length + '%'
})
// 根据图片数量动态创建导航点的个数
for (var i = 0; i < slides.length; i++) {
const span = document.createElement('span')
if (i == 0) {
span.className = 'active'
}
pagination.appendChild(span)
}
dots = container.querySelectorAll('.swiper-pagination span')
}
container.init()
// 自动播放
container.autoRun = function () {
// 防止定时器重复
clearInterval(timer)
// 启动定时器
timer = setInterval(function () {
// 索引自增 切换幻灯片
index++
container.switchSlide(index)
}, 1000)
}
container.autoRun()

// 切换幻灯片
container.switchSlide = function (i, isTransition) {
if (isTransition === undefined) {
isTransition = true
}
if (isTransition) {
// 增加过度
wrapper.style.transition = 'all .5s'
} else {
// 增加过度
wrapper.style.transition = 'none'
}
/**
* index => left
* 0 => 0
* 1 => index * -375
*/
// 计算新的left的值
const newLeft = -i * container.offsetWidth
// 设置left的样式
wrapper.style.left = newLeft + 'px'

// 点的切换
// 移除所有导航点的active类
dots.forEach(function (dot) {
dot.classList.remove('active')
})
// 当前索引的导航点 增加 active 类
dots[i % slides.length].classList.add('active')
index = i
}