效果 : Rock Slider
结构
以上图为例,整个轮播图我们把他放在一个class为slider的div里,可以看到整个轮播由三个部分构成:
- 图片部分
- 前进、后退按钮
- 小圆点
几张图片就放几个img标签,前进后退按钮可以用a标签,注意把默认事件去掉,小圆点可以用个无序列表,根据以上构想,可以写出如下html代码
<div class="slider">
<img class="fade" src="./img/1.jpg" alt="" />
<img class="fade" src="./img/2.jpg" alt="" />
<img class="fade" src="./img/3.jpg" alt="" />
<img class="fade" src="./img/4.jpg" alt="" />
<a class="prev" onclick="prev()"><</a>
<a class="next" onclick="next()">></a>
<ul class="nav">
<li class="dot selected" onclick="turnToSlide(0)"></li>
<li class="dot" onclick="turnToSlide(1)"></li>
<li class="dot" onclick="turnToSlide(2)"></li>
<li class="dot" onclick="turnToSlide(3)"></li>
</ul>
</div>
解释一下类名,img的fade是用来写切换时的过度动画,prev,next是写前进后退的样式,nav是控制无序列表排列方式,dot是每个小圆点未选中的样式,selected是选中后的样式。
布局
个人感觉轮播图的逻辑不是很难,难就难在布局上
这里要用到一个思想,叫“子绝父相”,意思是父亲的position属性为relative,儿子的position为absolute,然后再来调整儿子的位置就很方便了,所以我们将slider的position设为relative,其直系子元素的position设为absolute。
很容易看出,前进后退的按钮是靠边缘垂直居中,所以把left/right设为0,然后再垂直居中,给个top属性50%,但是这里的top的距离是以元素的左上角为参考物,所以为了真正的居中我们还要将元素向上平移自身高度的50%,可以使用transform: translateY(-50%);,具体的一些样式细节我就不赘述了,代码都很容易看懂。
然后再来看导航的小圆点,是靠底部垂直居中,那么我们将bottom设一个较小的值,留点距离,然后left:50;,transform: translateX(-50%);,具体的样式可以根据自己的喜好调整。
代码如下:
.slider {
top: 50%;
left: 50%;
transform: translate(-50%);
width: 60vw;
position: relative;
}
@media screen and (max-width: 900px) {
.slider {
width: 100vw;
}
}
.slider img {
display: none;
width: 100%;
border-radius: 4px;
}
.prev,
.next {
position: absolute;
top: 50%;
width: 20px;
height: 30px;
transform: translateY(-50%);
color: white;
font-size: 24px;
font-weight: bold;
cursor: pointer;
padding: 10px;
user-select: none;
transition: 0.6s ease;
}
.prev {
border-radius: 0 3px 3px 0;
left: 0;
}
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
.prev:hover,
.next:hover {
background-color: rgba(0, 0, 0, 0.8);
}
.nav {
position: absolute;
width: 12vmax;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: space-evenly;
bottom: 1vmin;
}
.dot {
list-style-type: none;
cursor: pointer;
background-color: #fff;
width: 1.5vmax;
height: 1.5vmax;
border-radius: 50%;
transition: background-color 0.6s ease;
}
.selected {
background-color: #ff5000;
}
.fade {
animation-name: fade;
animation-duration: 1.5s;
-webkit-animation-name: fade;
-webkit-animation-duration: 1.5s;
}
@keyframes fade {
from {
opacity: 0.4;
}
to {
opacity: 1;
}
}
@-webkit-keyframes fade {
from {
opacity: 0.4;
}
to {
opacity: 1;
}
}
这里值得一提的是:为了手机用户的体验,做了简单的移动端适配,用了媒体查询和响应式单位。
至于@keyframes动画,有不理解的可以去MDN上看看文档。
动作
最后来看看逻辑的部分。
初始化的时候,将每个图片的display属性设为none,然后播放到哪一张图片将其display设为block。页面载入的时候播放第一张图片。
要实现前进、后退、点击小圆点显示指定图片这三个功能,我们至少要实现next、prev、turnToSlide这三个函数,这三个的逻辑都是点击后播放指定的图片,那么我们可以考虑实现一个播放的函数showSlide,根据图片的index来播放,然后因为轮播图是有自动播放的功能,我们要考虑在里面加个定时器(使用了定时器要注意清空),实现定时播放。
根据以上设计,我们写出如下代码
function Slider () {
let slideIndex = 0
let timer,
flag = 0
function deleteTimer () {
clearTimeout(timer)
timer = null
}
function next () {
slideIndex++
showSlide()
}
function turnToSlide (index) {
if (index === slideIndex) return
slideIndex = index
showSlide()
}
function prev () {
slideIndex--
showSlide()
}
function showSlide () {
deleteTimer()
const slides = document.querySelectorAll('img')
const dots = document.querySelectorAll('.dot')
if (slideIndex >= slides.length) {
slideIndex = 0
}
if (slideIndex < 0) {
slideIndex = slides.length - 1
}
slides[flag].style.display = 'none'
dots[flag].classList.remove('selected')
slides[slideIndex].style.display = 'block'
dots[slideIndex].classList.add('selected')
flag = slideIndex
timer = setTimeout(next, 5000)
}
return {
prev,
next,
showSlide,
turnToSlide
}
}
const slider = Slider()
slider.showSlide()
为了防止全局变量污染,我把整个轮播图相关的方法和变量放在了一个Slider的的函数里,然后只暴露四个要被调用的方法。
在图片切换方面,目前主流写法都是使用for循环将所有的图片的display设为none再将要播放的图片的display设为block。可以这样做,但没有必要。我们可以借助一个标记变量flag来记录上次播放的图片的index,这样我在播放新图片的时候将index为flag的图片的display设为none,要播放的图片的display设为block就行了。
源码地址: 7sings/Slider: a simple slider writen with html,css and javascript (github.com)
后记
2021年9月7日:优化了移动端用户体验,在触摸屏设备上,隐藏了前进后退的点击按钮,通过左滑(加载下一张),右滑(加载前一张)实现了前进后退功能。由于本人比较懒,代码就没有在文章里更新,更新后的代码见上方仓库地址。
To be continued...