原生实现轮播图 | 青训营笔记

219 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第 3 天

前言

原生实现一个轮播图一直以来都是前端开发者的难题之一,也是目前的面试难点之一。想要实现一个轮播图,需要们有扎实的JS基础开发能力,明白了其中的原理其实开发一个了轮播图并不困难,本篇文章将会简述一个轮播图的原生实现方法。

需要实现的需求

想要开发一个成功的组件或者是软件,第一点首先要了解这个组件需要实现的需求,从需求下手才能明白自己需要完成的工作,一个简单的轮播图需要完成如下几个需求:

  • 自动轮播图片
  • 点击图片两侧的按钮可以实现图片的切换
  • 图片下方有按钮的列表,可以表明当前播放图片的序号
  • 点击下方任意一个按钮可以实现切换图片
  • 鼠标hover上时候会停止自动播放

功能实现

HTML布局

一个轮播图的框内一定存在多个图片,如何将多个图片关联起来让其具有相关性,我们这里使用列表的方式来实现。

<body>
    <div id="my-slider" class="slider-list">
    <!-- 轮播图的图片无序列表, 每一个li代表一个图片 -->
        <ul>
            <li class="slider-list_item--default">
                <img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png" />
            </li>
            <li class="slider-list_item">
                <img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg" />
            </li>
            <li class="slider-list_item">
                <img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg" />
            </li>
            <li class="slider-list_item">
                <img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg" />
            </li>
        </ul>
        <!-- 轮播图两侧的按钮,负责控制轮播图的上下页滚动 -->
        <a class="slider-list_next"></a>
        <a class="slider-list_pervious"></a>
        <!-- 轮播图底侧按钮,负责表示当前图片序号以及点击后滚动到目标图片 -->
        <div class="slider-list_control">
            <span class="slider-list_buttons--default"></span>
            <span class="slider-list_buttons"></span>
            <span class="slider-list_buttons"></span>
            <span class="slider-list_buttons"></span>
        </div>
    </div>
</body>

CSS 布局样式

把图片通过无序列表的方式放到页面上之后,我们需要思考图片以及按钮的摆放位置,首先我们根据当前主流的轮播图样式,我们需要把左右滚动的按钮放在图片两侧,一个直接跳转到指定页的按钮放在底部,最后我们需要把全部图片都放在一个图片框内

/** 图片框样式 */
#my-slider {
    position: relative;
    width: 790px;
    height: 340px;
}
.slider-list ul {
    list-style-type: none;
    position: relative;
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
}
.slider-list_item--default, .slider-list_item {
    position: absolute;
    transition: opacity 1s;
    text-align: center;
    opacity: 0;
}
.slider-list_item--default {
    transition: opacity 1s;
    opacity: 1;
}

首先通过设置position属性为absolute(绝对定位),可以让我们的图片脱离文档流,从而形成层叠关系,堆在我们提前设置好的图片框内,之后我们将需要默认展示出来的图片的opacity属性设置为1,其他的图片设置为0可以实现默认展示图片的效果。

接下来我们设置按钮的布局

.slider-list_control {
            position: relative;
            display: table;
            background-color: rgba(255, 255, 255, 0.5);
            padding: 5px;
            border-radius: 12px;
            bottom: 30px;
            margin: auto;
        }
        .slider-list_buttons--default, .slider-list_buttons {
            display: inline-block;
            width: 15px;
            height: 15px;
            border-radius: 50%;
            margin: 0 5px;
            background-color: white;
            cursor: pointer;
        }
        .slider-list_buttons--default {
            background-color: red;
        }

        .slider-list_next, .slider-list_pervious{
            display: inline-block;
            position: absolute;
            top: 50%;
            margin-top: -25px;
            width: 30px;
            height: 50px;
            text-align: center;
            font-size: 24px;
            line-height: 50px;
            overflow: hidden;
            border: none;
            background: transparent;
            color: white;
            background:rgba(0,0,0,0.2);
            cursor: pointer;
            opacity: 0;
            transition: opacity 1s;
        }
        .slider-list_pervious {
            left: 0;
        }
        .slider-list_next {
            right: 0;
        }
        .slider-list_pervious::after {
            content: '<';
        }
        .slider-list_next::after {
            content: '>';
        }
        #my-slider:hover .slider-list_pervious {
            opacity: 1; 
        }
        #my-slider:hover .slider-list_next {
            opacity: 1;
        }

这样设置完成后,一个基本的轮播图的样式就成型了,但是这个时候还不能实现轮播的效果,效果图如下所示:

不能动的轮播图示例

JS实现对应的功能

接下来就到了整个轮播图实现最关键的一步,所有的可以操作的功能都要通过JS来实现,话不多说,通过下面的代码来看看吧!

我们实现一个轮播图的功能实现需要引入JS中的类方法,对JS类不熟悉的可以去查看MDN中有关JavaScript类的相关文档

首先创建一个类名为Slider,在类的构造函数中传入两个参数,分别是id(轮播图框的元素列表),cycle(切换图片的间隔时间,默认值为3s)

constructor(id, cycle = 3000){
    //定义图片框这个容器,我们可以凭借这个容器拿到图片框中任意一个元素
    this.container = document.getElementById(id);
    this.items = this.container.querySelectorAll('.slider-list_item--default, .slider-list_item')
    this.cycle = cycle
    const controller = this.container.querySelector('.slider-list_control')
    if(controller){
        const buttons = controller.querySelectorAll('.slider-list_buttons--default, .slider-list_buttons')
        controller.addEventListener('mouseover', (event) => {
        //这行代码的意思是index会动态获取到每次鼠标移动到某个元素上的下标
        //拿到下标后判断是否为默认值,不是默认值就跳转到对应的图片
        const index = Array.from(buttons).indexOf(event.target)
              if(index >= 0){ //'>=0' 不是默认值
                 this.sliderTo(index)
                 this.stop()
              }
        })
        //鼠标移开后接着开始轮播图的播放
        controller.addEventListener('mouseout', (event) => {
               this.start()
        })

        //这个是控制按钮的颜色变化,到哪一个图按钮就会变为红色
        this.container.addEventListener('slide', (event) => {
               const index = event.detail.index
               const selected = controller.querySelector('.slider-list_buttons--default')
               if(selected){
                   selected.className = 'slider-list_buttons'
                   buttons[index].className = 'slider-list_buttons--default'
               }
         })
    }
    //这是控制两侧的按钮监听click事件,当我们点击左右两侧的按钮时,可以控制轮播图的前进后退
    const previous = this.container.querySelector('.slider-list_pervious');
         if(previous){
               previous.addEventListener('click' , (event) => {
                   this.stop()
                   this.sliderPervious()
                   this.start()
                   event.preventDefault()
               })
        }
    const next = this.container.querySelector('.slider-list_next')
        if(next) {
               next.addEventListener('click', (event) => {
                   this.stop()
                   this.sliderNext()
                   this.start()
                   event.preventDefault()
               })
       }
}

上面这段代码我们主要实现的功能包括控制底侧按钮的跳转功能,有两侧按钮的控制前进和后退的功能以及每当图片跳转后需要控制下方按钮的颜色变化。

接下来我们实现其中的跳转方法,前进后退的方法以及暂停与播放的方法

跳转功能

getSelectedItem(){
    // 返回当前被选中的元素
    const selected = this.container.querySelector('.slider-list_item--default')
    return selected
}
getSelectedItemIndex() {
    //浅拷贝这个数组item,并使用indexOf找到当前被选中的元素的下标
    return Array.from(this.items).indexOf(this.getSelectedItem())
}
sliderTo(index){
    const selected = this.getSelectedItem()
        if(selected){
            selected.className = 'slider-list_item'
        }
    const item = this.items[index]
        if(item){
            item.className = 'slider-list_item--default'
        }

    const detail = {index: index}
    //这里表示我们创造了一个slide事件
    const event = new CustomEvent('slide', {bubbles:true, detail})
    //把事件event派发给container
    this.container.dispatchEvent(event)
}

我们可以分析出来,第一个方法是用来获取当前轮播图中被选中的元素;第二个方法是用来获得当前被选中的元素的下标,底部的按钮的选中状态只有拿到下标我们才能控制底部按钮是否处于选中或非选中;第三个方法是跳转方法,这也是实现轮播图功能的最重要的一个方法,跳转方法后续会被使用在这个类中任意一个关于滚动功能的方法中。

前进后退功能

sliderNext(){
    //获取当前轮播图的下标,通过计算让下标加一,调用方法sliderto变换图片
    const currentIndex = this.getSelectedItemIndex()
    const nextIndex = (currentIndex + 1) % this.items.length
    this.sliderTo(nextIndex)
}
sliderPervious(){
    const currentIndex = this.getSelectedItemIndex()
    const perviousIndex = (currentIndex - 1 + this.items.length) % this.items.length
    this.sliderTo(perviousIndex)
}
start() {
    this.stop()
    this._timer = setInterval(() => {
        this.sliderNext()
    }, this.cycle)
}
stop() {
    clearInterval(this._timer)
}

上述代码的第一个方法就是前进的方法,通过取模来获取到当前元素下标加一的下标,并调用sliderTo方法进行跳转;第二个方法是后退的方法,实现的原理与前进差不多,但是需要注意在取下标的时候同样是取模长需要我们加上列表元素的长度避免计算出现错误;

第三个方法是启动轮播图的滚动,轮播图的滚动功能通过setInterval来实现,切换图片的周期是前面传入的cycle值;最后一个方法是暂停方法,只需要把定时器的时间清空即可。

实例化

//实例化的方式非常简单
const slider = new Slider()
slider.start()
//调用启动方法启动轮播图

小结

在JS代码当中,核心的方法有以下几个:

  • sliderTo(): 跳转到指定下标的图片位置
  • sliderNext(): 跳转到下一页
  • sliderPervious(): 跳转到上一页
  • stop(): 暂停播放
  • start(): 开始播放

总结

正如文章的开头所说,轮播图的实现看似很困难,但了解其中的原理后想要自己手动实现并不困难。当然,其实还有更好的实现方法,例如纯JS实现,通过JS来生成DOM节点,但是对开发者的能力需求也更高,通过不断学习,轮播图什么的还是可以轻松拿捏。