网页轮播图的实现方式

轮播图

轮播图是Web开发中常见的组件之一,通常第三方框架都自带这个组件,或是通过使用第三方插件实现,无需程序员自己重新编码,但在面试中会被问道其实现原理。轮播图的实现方式有很多种,本文轮播图实现代码来自[黑马程序员的视频教程],纯js实现,支持无缝滚动。

动画

在实现轮播图前,我们需要封装一个动画函数。

function animate(obj, target) {
	obj.timer = setInterval(function () {
		var step = (target - obj.offsetLeft) / 10;
		if (obj.offsetLeft >= target) clearInterval(obj.timer);
			obj.style.left = obj.offsetLeft + step + "px";
	}, 15);
}
// 调用
var div = document.querySelector("div");
div.addEventListener("click", function () {
	animate(div, 300);
});

这里会存在一个Bug,就是多次点击div盒子触发animate()函数,导致有多个定时器的步长累加,速度随点击次数增加。
解决办法:
1.创建定时器前先清除

// 清除定时器
clearInterval(obj.timer);
// 创建定时器
obj.timer = setInterval(function () {
	...
}

2.通过if判断定时器是否被创建,没有创建则执行创建定时器代码

if (typeof obj.timer == "undefined") {
	obj.timer = setInterval(function () {
		...
	}
}

步长(step)计算

动画需要按照先快后慢的方式运行。这里需要用到步长公式:
步长 = (目标值-当前位置)/10
步长公式理解:分母不变,分子越小,结果越小;从而结果由大到小变化,前面的结果越大,得到步长越大,速度越快;后面的结果越小,速度越慢。

var step = (target - obj.offsetLeft) / 10;

因为步长公式运算会存在小数,所以 if (obj.offsetLeft >= target) clearInterval(obj.timer); 判断其实并没有起作用,因为step = 0.4,target = 300,obj.offsetLeft= 296,

陷入了死循环。

image

解决办法:
步长为正数,先上取整。为负数向下取整。
假设step=0.4,如果往下取整那么step=0,那么obj.offsetLeft >= target等式不成立(因为obj.offsetLeft比target小0.4),计时器就陷入了死循环。
同理,当为负数时,只有向下取整。(注意等式条件为:obj.offsetLeft == target)

step = step > 0 ? Math.ceil(step) : Math.floor(step);

动画的完整代码:

// 动画函数 (对象,目标位置,回调函数)
function animate(obj, target, callback) {
    // 清除定时器
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
            // 回调函数
            callback && callback();
        }
        obj.style.left = obj.offsetLeft + step + "px";
    }, 15);
}

轮播图实现原理

多个图片并排排列,为了解决无缝切换的问题,最后一张为第一张图片。通过动画使focus1~4这四张图片不断显示,从而达到轮播的效果。
静态图示:
image
动态图示:
image

步骤

移动步长的计算公式

移动步长 = 索引 * mian盒子宽度;因为向左移动,所以取负。

-index * mainWidth
  • 第一张移动 0 ,表示不移动,因为当前显示的图片就是第一张。
    image
  • 第二张移动 1*mainWidth = 1个mainWidth单位,表示移动1张图片的距离,显示的内容就是第2张。
    image
  • 第三张移动 2*mainWidth = 2个mainWidth单位,表示移动2张图片的距离,显示的内容就是第3张。依次类推
    因为index是我们设置的属性(li.setAttribute('index', i);)所以最大值就是图片数量-1。

1.鼠标经过,左右按钮显示,动画停止;鼠标离开,左右按钮显示,动画自动播放。

    var left = document.querySelector('.left');     // 左翻页按钮
    var right = document.querySelector('.right');   // 右翻页按钮
    var main = document.querySelector('.main');     // 用户可视区域
    var mainWidth = main.offsetWidth;               // mian盒子宽度

    // 鼠标经过
    main.addEventListener('mouseenter', function () {
        left.style.display = 'block';
        right.style.display = 'block';
        // 清除定时器,自动翻页动画停止
        clearInterval(timer);
        timer = null; 
    });
    // 鼠标离开
    main.addEventListener('mouseleave', function () {
        left.style.display = 'none';
        right.style.display = 'none';
        // 自动翻页动画开始
        timer = setInterval(function () {
            right.click();
        }, 2000);
    });
    // 定时器,自动翻页
    var timer = setInterval(function () {
        right.click();
    }, 2000);

2.点击小圆圈实现滚动图片。

    var ul = main.querySelector('ul');  // 图片列表
    var ol = main.querySelector('.circle'); // 圆圈列表

    // 动态创建圆圈,数量根据 图片个数 决定
    for (var i = 0; i < ul.children.length; i++) {
        var li = document.createElement('li');
        li.setAttribute('index', i);
        ol.appendChild(li);
        // 绑定点击事件
        li.addEventListener('click', function () {
            // 所有圆圈去除类名 current 
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 当前圆圈添加类名 current
            this.className = 'current';
            // 获得索引。用户点击了第几个图片
            var index = this.getAttribute('index');
            num = index;
            circle = index;
            // 向左滚动
            animate(ul, -index * mainWidth);
        })
    }
    // 克隆第一个图片放到ul的最后
    ol.children[0].className = 'current';
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);

3.点击左、右翻页按钮实现翻页,小圆圈跟随变动。

var num = 0;        // 当前滚动图片索引
    var circle = 0;     // 圆圈索引
    var flag = true;    //  是否滚动完成的标志

    // 右翻页
    right.addEventListener('click', function () {
        if (flag) {
            flag = false;
            // 如果滚动到最后一张,即克隆的第一张
            if (num == ul.children.length - 1) {
                ul.style.left = 0;
                num = 0;
            }
            num++;
            animate(ul, -num * mainWidth, function () {
                flag = true;
            });
            circle++;
            // 圆圈到了最后一个
            if (circle == ol.children.length) {
                circle = 0;
            }
            circleChange();
        }
    });
    // 左翻页
    left.addEventListener('click', function () {
        if (flag) {
            flag = false;
            // 如果是第一张
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * mainWidth + 'px';
            }
            num--;
            animate(ul, -num * mainWidth, function () {
                flag = true;
            });
            circle--;
            if (circle < 0) {
                circle = ol.children.length - 1;
            }
            circleChange();
        }
    });
    // 圆圈状态切换
    function circleChange() {
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        ol.children[circle].className = 'current';
    }

代码下载:https://wybing.lanzouw.com/iAjpe0jok2ab

完整代码

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>轮播图</title>
    <link rel="stylesheet" href="./css/index.css">
    <script src="./js/animate.js"></script>
    <script src="./js/index.js"></script>
</head>
<body>
    <div class="main">
        <!-- 左按钮 -->
        <a href="javascript:;" class="left">&lt;</a>
        <!-- 右按钮 -->
        <a href="javascript:;" class="right">&gt;</a>
        <!-- 滚动区 -->
        <ul>
            <li>
                <a href="#"><img src="img/focus1.jpg"></a>
            </li>
            <li>
                <a href="#"><img src="img/focus2.jpg"></a>
            </li>
            <li>
                <a href="#"><img src="img/focus3.jpg"></a>
            </li>
            <li>
                <a href="#"><img src="img/focus4.jpg"></a>
            </li>
        </ul>
        <!-- 小圆 -->
        <ol class="circle">
        </ol>
    </div>
</body>
</html>

index.css

*{
	padding: 0;
	margin: 0;
}
ul,ol{
	list-style: none;
}
a{
	text-decoration: none;
}

.main{
	position: relative;
	width: 590px;
	height: 470px;
	margin: 150px auto;
	overflow: hidden;
}

.main ul{
	position: absolute;
	left: 0;
	top: 0;
	width: 1000%;
}

.main ul li{
	float:left;
}

.left,.right{
	display: none;
	position: absolute;
	top: 50%;
	margin-top: -20px;
	width: 24px;
	height: 40px;
	background: rgba(0, 0, 0, .3);
	text-align: center;
	line-height: 40px;
	color: #fff;
	font-size: 18px;
	z-index: 2;
}
.right{
	right: 0;
}

.circle {
	position: absolute;
	bottom: 15px;
	left:50%;
    transform: translateX(-50%);
}

.circle li {
	float: left;
	width: 8px;
	height: 8px;
	border: 2px solid orange;
	margin: 0 3px;
	border-radius: 50%;
	cursor: pointer;
}
.current {
    background-color: orange;
}

animate.js

// 动画函数 (对象,目标位置,回调函数)
function animate(obj, target, callback) {
    // 清除定时器
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
            // 回调函数
            callback && callback();
        }
        obj.style.left = obj.offsetLeft + step + "px";
    }, 15);
}

index.js

window.addEventListener('load', function () {

    var left = document.querySelector('.left');     // 左翻页按钮
    var right = document.querySelector('.right');   // 右翻页按钮
    var main = document.querySelector('.main');     // 用户可视区域
    var mainWidth = main.offsetWidth;               // mian盒子宽度

    // 鼠标经过
    main.addEventListener('mouseenter', function () {
        left.style.display = 'block';
        right.style.display = 'block';
        // 清除定时器,自动翻页动画停止
        clearInterval(timer);
        timer = null; 
    });
    // 鼠标离开
    main.addEventListener('mouseleave', function () {
        left.style.display = 'none';
        right.style.display = 'none';
        // 自动翻页动画开始
        timer = setInterval(function () {
            right.click();
        }, 2000);
    });

    
    var ul = main.querySelector('ul');  // 图片列表
    var ol = main.querySelector('.circle'); // 圆圈列表

    // 动态创建圆圈,数量根据 图片个数 决定
    for (var i = 0; i < ul.children.length; i++) {
        var li = document.createElement('li');
        li.setAttribute('index', i);
        ol.appendChild(li);
        // 绑定点击事件
        li.addEventListener('click', function () {
            // 所有圆圈去除类名 current 
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 当前圆圈添加类名 current
            this.className = 'current';
            // 获得索引。用户点击了第几个图片
            var index = this.getAttribute('index');
            num = index;
            circle = index;
            // 向左滚动
            animate(ul, -index * mainWidth);
        })
    }
    // 克隆第一个图片放到ul的最后
    ol.children[0].className = 'current';
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);

    var num = 0;        // 当前滚动图片索引
    var circle = 0;     // 圆圈索引
    var flag = true;    //  是否滚动完成的标志

    // 右翻页
    right.addEventListener('click', function () {
        if (flag) {
            flag = false;
            // 如果滚动到最后一张,即克隆的第一张
            if (num == ul.children.length - 1) {
                ul.style.left = 0;
                num = 0;
            }
            num++;
            animate(ul, -num * mainWidth, function () {
                flag = true;
            });
            circle++;
            // 圆圈到了最后一个
            if (circle == ol.children.length) {
                circle = 0;
            }
            circleChange();
        }
    });
    // 左翻页
    left.addEventListener('click', function () {
        if (flag) {
            flag = false;
            // 如果是第一张
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * mainWidth + 'px';
            }
            num--;
            animate(ul, -num * mainWidth, function () {
                flag = true;
            });
            circle--;
            if (circle < 0) {
                circle = ol.children.length - 1;
            }
            circleChange();
        }
    });
    // 圆圈状态切换
    function circleChange() {
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        ol.children[circle].className = 'current';
    }
    // 定时器,自动翻页
    var timer = setInterval(function () {
        right.click();
    }, 2000);
})

参考

posted @ 2022-12-30 05:00  乌药ice  阅读(260)  评论(0编辑  收藏  举报