Js原生实现无缝滑动轮播图(完整功能)

实现轮播图是前端学习的一门必修课,也是用来训练js能力。今天我用原生js写了个轮播图,包含无缝衔接、指示器、左右控制器等功能,跟大家分享。先看看效果图:

1. 设计html+css部分
结构很简单,图片部分用ul作容器(本人习惯ul,你们也可以div),flex布局让里面的li排成一排,因为我们要做的是滑动效果而不是简单显隐。注意,要在最后另外多一个li存放第一张图,这是为了后面实现无缝轮播。 其他的就是小圆点指示器、左右控制器了。

<body>
    <div class="content">
        <ul class="wrapper">  //图片容器
            <li><img src="./static/banner1.jpg" ></li>
            <li><img src="./static/banner2.jpg" ></li>
            <li><img src="./static/banner3.jpg" ></li>
            <li><img src="./static/banner1.jpg" ></li> //重复第一张
        </ul>
        <ul class="radius">  //小圆点指示器,后面js动态添加li
        </ul>
        <div class="prev">  //左控制器
            <span><</span>
        </div>
        <div class="next">
            <span>></span>  //右控制器
        </div>
    </div>
</body>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    .content {
        width: 300px;
        height: 300px;
        position: relative;
        overflow: hidden;
        margin: 0 auto;
    }
    .wrapper {
        width: 400%;
        height: 100%;
        margin: 0;
        padding: 0;
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        transition: none;
    }
    .wrapper li {
        flex: 1;
        list-style: none;
        margin: 0;
        padding: 0;
    }
    .wrapper li img {
        width: 100%;
        height: 100%;
    }
    .radius {
        height: 12px;
        margin: 0;
        padding: 0;
        position: absolute;
        bottom: 10px;
        left: 10px;
        display: flex;
        align-items: center;
    }
    .radius li {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background-color: white;
        opacity: 0.6;
        margin: 0 3px;
        padding: 0;
        list-style: none;
    }
    .radius-active {
        opacity: 1 !important;
        border: 2px solid rgb(255, 255, 255, 0.5);
        background-clip: padding-box;
    }
    .prev {
        width: 23px;
        line-height: 34px;
        text-align: center;
        position: absolute;
        left: 0;
        top: 50%;
        margin-top: -17px;
        background-color: darkgray;
        opacity: 0;
    }
    .next {
        width: 23px;
        line-height: 34px;
        text-align: center;
        position: absolute;
        right: 0;
        top: 50%;
        margin-top: -17px;
        background-color: darkgray;
        opacity: 0;
    }
    .prev span, .next span {
        font-weight: bold;
        color: white;
        font-size: 18px;
    }
</style>

2. Js获取需要用到的Dom节点

var content = document.getElementsByClassName('content')[0] //整个容器
var wrapper = document.getElementsByClassName('wrapper')[0] //轮播图容器
var radius = document.getElementsByClassName('radius')[0] //小圆点容器
var prev = document.getElementsByClassName('prev')[0] //左控制器
var next = document.getElementsByClassName('next')[0] //右控制器
var imgWidth = wrapper.children[0].offsetWidth //一个轮播li的宽度
var wrapIndex = 0  //用来判断位置的标识

3. 动态创建小圆点指示器
有人说“轮播几张图就给几个小圆点就好了嘛”,确实,不过站在便于以后修改、维护的角度,我习惯让小圆点根据图片数量动态创建,这也是开发好习惯。

function createLi() {
	for(let i=0; i<wrapper.children.length-1; i++) {  //轮播图x张,实际只是轮播x-1张,小圆点也是x-1个
		let li = document.createElement("li") //创建li标签
			radius.appendChild(li) //添加进radius
	}
	radius.children[0].className='radius-active' //初始化,为第一个小圆点添加active
}
createLi()

4. 动画函数
以16毫秒一帧地改变wrapperleft值。

function animate(el, target) {
    clearInterval(el.timer)  //停止上一个功能的el.timer
    el.timer = setInterval(function(){  //16毫秒地循环执行
        let move = 8;
        let present = wrapper.offsetLeft; //当前位置left值
        move = present > target ? -move : move;
        present += move;  //改变当前位置left值
        if(Math.abs(present-target) > Math.abs(move)) {
            wrapper.style.left = present + 'px'  //如果位置差大于move,则保留位置继续循环移动
        }
        else {  //如果小于,则让当前位置等于目标位置,退出计时器循环
            clearInterval(el.timer);
            wrapper.style.left = target + 'px'
        }
    },16)  
}

5. 小圆点指示器响应
清除所有active,跟随当前标识响应相应的小圆点添加active

function cirAction(wrapIndex) {
    for(let i=0; i<radius.children.length; i++) {
        radius.children[i].classList.remove("radius-active")
    }
    if(wrapIndex === wrapper.children.length-1) {
        radius.children[0].className='radius-active'
    }
    else {
        radius.children[wrapIndex].className='radius-active'
    }
}

6. next单击控制
next元素的单击控制是轮播图核心,提前封装好了可在其他功能上直接调用,减少代码量。注意当轮播到最后一张图时,要让位置标识变为"0",其实也就是闪现到第一张图(第一张和最后一张相同,不漏痕迹),这就是前面为什么要在最后加一张首图的原因。

next.onclick = function() {
    if(wrapIndex === wrapper.children.length-1){ //如果到了最后一张
        wrapIndex = 0;  //调整标识
        wrapper.style.left = 0 + 'px'; //闪现回第一张
    }
    wrapIndex++;
    animate(wrapper,-wrapIndex*imgWidth); //滑动动画
    cirAction(wrapIndex);  //小圆点跟随响应
}

7. 自动轮播

var timer = setInterval(function(){
    next.onclick()
},3500)

8. prev单击控制

prev.onclick = function() {
    if(wrapIndex === 0){
        wrapIndex = wrapper.children.length-1;
        wrapper.style.left = -wrapIndex*imgWidth + 'px';
    }
    wrapIndex--;
    animate(wrapper,-wrapIndex*imgWidth);
    cirAction(wrapIndex)
}

9. 小圆点指示器移入控制
切记要先清除timer定时器。

function cirMouse() {
    for(let i=0; i<radius.children.length; i++) {
        radius.children[i].onmouseover = function() {
            clearInterval(timer);
            animate(wrapper, -i*imgWidth);
            wrapIndex = i;
            cirAction(wrapIndex)
        }
    }
}
cirMouse()

10. 悬停和移出content容器响应
悬停时停止timer定时器,显示左右控制器;移出时再设置回timer,左右控制器消失。

content.onmouseover = function() {
    clearInterval(timer)
    next.style.opacity = "0.6";
    prev.style.opacity = "0.6";
}
content.onmouseout = function() {
    timer = setInterval(function(){
        next.onclick()
    },3500)
    next.style.opacity = "0";
    prev.style.opacity = "0";
}

这样一个满足日常开发功能的轮播图就实现了。完整代码如下,可参考交流:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Swiper Test</title>
</head>
<body>
    <div class="content">
        <ul class="wrapper">
            <li><img src="./static/banner1.jpg" ></li>
            <li><img src="./static/banner2.jpg" ></li>
            <li><img src="./static/banner3.jpg" ></li>
            <li><img src="./static/banner1.jpg" ></li>
        </ul>
        <ul class="radius">
        </ul>
        <div class="prev">
            <span><</span>
        </div>
        <div class="next">
            <span>></span>
        </div>
    </div>
</body>
<script type="text/javascript">
    window.onload= function() {
        var content = document.getElementsByClassName('content')[0]
        var wrapper = document.getElementsByClassName('wrapper')[0]
        var radius = document.getElementsByClassName('radius')[0]
        var prev = document.getElementsByClassName('prev')[0]
        var next = document.getElementsByClassName('next')[0]
        var imgWidth = wrapper.children[0].offsetWidth
        var wrapIndex = 0

        //悬停显隐
        content.onmouseover = function() {
            clearInterval(timer)
            next.style.opacity = "0.6";
            prev.style.opacity = "0.6";
        }
        content.onmouseout = function() {
            timer = setInterval(function(){
                next.onclick()
            },3500)
            next.style.opacity = "0";
            prev.style.opacity = "0";
        }

        //动态创建圆点指示器
        function createLi() {
            for(let i=0; i<wrapper.children.length-1; i++) {
                let li = document.createElement("li")
                radius.appendChild(li)
            }
            radius.children[0].className='radius-active'
        }
        createLi()

        //指示器响应
        function cirAction(wrapIndex) {
            for(let i=0; i<radius.children.length; i++) {
                radius.children[i].classList.remove("radius-active")
            }
            if(wrapIndex === wrapper.children.length-1) {
                radius.children[0].className='radius-active'
            }
            else {
                radius.children[wrapIndex].className='radius-active'
            }
        }

        //指示器控制
        function cirMouse() {
            for(let i=0; i<radius.children.length; i++) {
                radius.children[i].onmouseover = function() {
                    clearInterval(timer);
                    animate(wrapper, -i*imgWidth);
                    wrapIndex = i;
                    cirAction(wrapIndex)
                }
            }
        }
        cirMouse()

        //滑动动画
        function animate(el, target) {
            clearInterval(el.timer)
            el.timer = setInterval(function(){
                let move = 8;
                let present = wrapper.offsetLeft;
                move = present > target ? -move : move;
                present += move;
                if(Math.abs(present-target) > Math.abs(move)) {
                    wrapper.style.left = present + 'px'
                }
                else {
                    clearInterval(el.timer);
                    wrapper.style.left = target + 'px'
                }
            },16)  
        }

        //next控制
        next.onclick = function() {
            if(wrapIndex === wrapper.children.length-1){
                wrapIndex = 0;
                wrapper.style.left = 0 + 'px';
            }
            wrapIndex++;
            animate(wrapper,-wrapIndex*imgWidth);
            cirAction(wrapIndex);
        }

        //prev控制
        prev.onclick = function() {
            if(wrapIndex === 0){
                wrapIndex = wrapper.children.length-1;
                wrapper.style.left = -wrapIndex*imgWidth + 'px';
            }
            wrapIndex--;
            animate(wrapper,-wrapIndex*imgWidth);
            cirAction(wrapIndex)
        }

        //自动滑动
        var timer = setInterval(function(){
            next.onclick()
        },3500)
    }


</script>
<style>
    * {
        margin: 0;
        padding: 0;
    }
    .content {
        width: 300px;
        height: 300px;
        position: relative;
        overflow: hidden;
        margin: 0 auto;
    }
    .wrapper {
        width: 400%;
        height: 100%;
        margin: 0;
        padding: 0;
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        transition: none;
    }
    .wrapper li {
        flex: 1;
        list-style: none;
        margin: 0;
        padding: 0;
    }
    .wrapper li img {
        width: 100%;
        height: 100%;
    }
    .radius {
        height: 12px;
        margin: 0;
        padding: 0;
        position: absolute;
        bottom: 10px;
        left: 10px;
        display: flex;
        align-items: center;
    }
    .radius li {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background-color: white;
        opacity: 0.6;
        margin: 0 3px;
        padding: 0;
        list-style: none;
    }
    .radius-active {
        opacity: 1 !important;
        border: 2px solid rgb(255, 255, 255, 0.5);
        background-clip: padding-box;
    }
    .prev {
        width: 23px;
        line-height: 34px;
        text-align: center;
        position: absolute;
        left: 0;
        top: 50%;
        margin-top: -17px;
        background-color: darkgray;
        opacity: 0;
    }
    .next {
        width: 23px;
        line-height: 34px;
        text-align: center;
        position: absolute;
        right: 0;
        top: 50%;
        margin-top: -17px;
        background-color: darkgray;
        opacity: 0;
    }
    .prev span, .next span {
        font-weight: bold;
        color: white;
        font-size: 18px;
    }
</style>
</html>

你可能感兴趣的:(前端笔记)