轮播图实现

1,113 阅读2分钟

效果图

Tips:小圆点,鼠标移入轮播暂停、移出轮播继续、移入显示左右箭头、移出隐藏左右箭头等等效果未予实现,个人认为这些意义不大,重点在于理解轮播图背后的实现原理。

轮播图的原理

在已有图片的列表中各加一张图,头部加最后一张图,尾部加第一张图:

<ul class="loop">
    <li><img src="./banner1.jpg" /></li>
    <li><img src="./banner2.jpg" /></li>
    <li><img src="./banner3.jpg" /></li>
 </ul>

当然是通过js添加,加了之后的结构是:

<ul class="loop">
    <li><img src="./banner3.jpg" /></li>
    <li><img src="./banner1.jpg" /></li>
    <li><img src="./banner2.jpg" /></li>
    <li><img src="./banner3.jpg" /></li>
    <li><img src="./banner1.jpg" /></li>
 </ul>

默认显示banner1,在点击上一张的时候去判断边界值,在左移到banner3之后,悄悄将过渡效果关闭并将图片右移至第2banner3图片处(用户无感知),点击下一张图片的边界值也类比处理。

注意点

1.首尾各加一张图的目的是为了初次点击时能有过渡效果且好背后进行移动的暗箱操作
2.一定要等过渡完成后才进行移动的暗箱操作,所以会使用延时器setTimeout,且时延 === 过渡时间
3.为防止用户点击速度过快,一定要进行节流处理

小拓展

为什么大家实现轮播图的时候获取dom元素都写的是document.querySelector,而不是document.getElementBy系列?

querySelector 返回的是一个 Static Node List,而 getElementsBy 系列的返回的是一个 Live Node List。前者是快照,在之后再添加新的元素,不会影响已查询的结果;后者可以理解为一个对象地址,地址固定但内容不固定,会跟随你之后的节点操作而动态改变。

完整代码

<!DOCTYPE html>
<html>
<head>
	<title>手写轮播图</title>
	<style type="text/css">
		body {
			text-align: center;
		}
		.wrap {
			width: 400px;
			border: 1px solid #eeeeee;
			margin: 100px auto;
			overflow: hidden;
		}
		.loop {
			display: flex;
			list-style: none;
			padding: 0;
			margin: 0;
		}
		.loop li img {
			width: 400px;
			height: 300px;
			vertical-align: bottom;
		}
	</style>
</head>
<body>
	<div class="wrap">
		<ul class="loop">
			<li><img src="./banner1.jpg" /></li>
			<li><img src="./banner2.jpg" /></li>
			<li><img src="./banner3.jpg" /></li>
		</ul>
	</div>
	<button onclick="prev()">上一张</button>
	<button onclick="next()">下一张</button>
	<script type="text/javascript">
		{
			// 在前后各加一张图,目的是为了能无缝滑动
			const loop = document.querySelector('.loop')
			const li = document.querySelectorAll('.loop li')
			loop.insertBefore(li[li.length -1].cloneNode(true), li[0])
			loop.appendChild(li[0].cloneNode(true))
			loop.style.transform = 'translateX(-400px)'
			// 定义一些变量
			let now = 1 //当前在哪一张
			let timer = null // 定时器

			function prev() {
				if(!timer) { // 作节流处理
					loop.style.transition = '.3s';
					loop.style.transform = `translateX(-${--now * 400}px)`
					timer = setTimeout(() => {
						if (!now) {
							now = li.length
							loop.style.transition = 'none';
							loop.style.transform = `translateX(-${now * 400}px)`
						}
						timer = null
					}, 300)
				}
			}
			function next() {
				if(!timer) { // 节流处理
					loop.style.transition = '.3s';
					loop.style.transform = `translateX(-${++now * 400}px)`
					timer = setTimeout(() => {
						if (now > li.length) {
							now = 1
							loop.style.transition = 'none';
							loop.style.transform = `translateX(-${now * 400}px)`
						}
						timer = null
					}, 300)
				}
			}
			// 自动轮播
			setInterval(next, 2000)
		}
	</script>
</body>
</html>