前端面试之基础题——轮播图

前言: 轮播图应该是每个前端人员都会的东西。写这一篇的原因是去面试,然后面试官让我写轮播图,写是写出来了, 但是思路很混乱,代码很丑....so sad

一、HTML结构

<div class="wrapper-container">   // 需要显示设置宽高
    <div class="wrapper" id="wrapper">  // 继承高度 宽度 = 轮播图宽度 * 图片数量
         <img src="./images/1.webp" alt="">
         <img src="./images/2.jpg" alt="">
         <img src="./images/3.webp" alt="">
    </div>
</div>
// 比较简单 img如果需要做包裹可以加a标签或者li标签,自行看需求

二、CSS样式

.wrapper-container {
    position: relative;  // 最外层的盒子要设置position
    width: 590px;
    height: 470px;
    overflow: hidden;  // 溢出隐藏,只显示宽度内的元素
    margin-left: 300px;
}
.wrapper {
    position: absolute;
    left: 0;
    top: 0;
    width: 300%;    // 宽度自行根据轮播图片的数量
    transition: all .5s;
}
.wrapper img {
    width: 590px;
    display: block;  // 处理图片间隙
    float: left;
}

首先,我们要让一个元素动,就得先元素脱离文档流,也就是设置absolute定位。

内层盒子的宽度 = 图片的数量  * 一个图片的宽度

到这里,我们第一步的结构样式都已经搭建完成

下一步,我们让图片动起来,自动播放。

三、JS

1.自动播放

自动播放涉及两个概念,定时器函数,设置dom节点的偏移

 

// 相关变量的初始化
let index = 0, dom = document.querySelector("#wrapper"), // 内层盒子 wrapper = document.querySelector(".wrapper-container"),  // 外层盒子 timer = null,                  // 定时器标记 childLength = dom.children.length,      // 图片的数量 stepWidth = dom.children[0].offsetWidth    // 最外层盒子宽度——对应每次偏移的位置大小

// 写一个autoPlay的函数
function autoPlay () {
  index++    // 这里++或者是--看自己想轮播往哪个方向走
  index = index === childLength ? 0 : index   // 这里的判断是 如果index到了最后一张,那就让他回到第一张,达到重复轮播的目的
  dom.style.left = -index * stepWidth + 'px'  // 这里我给index取反了,是因为我的轮播从左往右轮播,假设一开始偏移值 0 ,第二次偏移值就是 -590px, 第三次就是 -590px * 2
  timer = setTimeout(autoPlay, 3000)
}

// 写好函数了,调用跑起来
timer = setTimeout(autoPlay, 3000)        // setTimeout只跑一次,执行到autoPlay函数内部由触发了一次,达到循环播放

 

到这里,一个基本的轮播就算完成了一小半了。

下面,我们来完善细节

在原有的基础上,添加当前图片的位置标记,如图

二、添加图片位置标记

在原有的结构上修改一下

 
  
<div class="wrapper" id="wrapper">  // 继承高度 宽度 = 轮播图宽度 * 图片数量
         <img src="./images/1.webp" alt="">
         <img src="./images/2.jpg" alt="">
         <img src="./images/3.webp" alt="">
</div>
<div class="indicator">
    <li class="active"></li>
    <li></li>
    <li></li>
</div>
/* 他和wrapper是 同级关系 */

样式

.indicator {
    position: absolute;
    bottom: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 3;
}
.indicator li {
    display: inline-block;
    width: 10px;
    height: 10px;
    background-color: rgba(0, 0, 0, .7);
}
.indicator li.active {
    background-color: rgba(255, 255, 255, .8);   // 当前显示的状态颜色
}

逻辑部分

let activeLi = document.querySelector(".active"),
    indicator = document.querySelector(".indicator"),
    allLi = indicator.children

// 我们添加一个domChange()函数,来控制位置标记的显示状态
function domChange() {
  activeLi = document.querySelector(".active")    // 为了清除上一个active,需要每次重新获取
  activeLi.className = ""
  allLi[index].className = 'active'    // 这里的index是全局的,是我们上次autoPlay去修改后的index,index始终是当前显示图片的索引值
}

到这里,位置标志的跟随显示应该也可以了。我们接着做下一次

三、添加前后按钮之——鼠标进入暂停autoPlay

我们给外层盒子注册两个函数——mouseenter,mouseleave

// 思路应该很清晰,当我们鼠标进入盒子的时候,让函数暂停,也就是清除定时器
// clearTimeout();
// 当鼠标离开了盒子,再开启一个相同的定时器
const handleMouseEnter = function () {
  clearTimeout(timer)
} const handleMouseLeave = function () { clearTimeout(timer) timer = setTimeout(autoPlay, 3000) } wrapper.addEventListener('mouseenter', handleMouseEnter) wrapper.addEventListener('mouseleave', handleMouseLeave)

 

做完了以上,我们现在来添加前后按钮

首先是页面结构,同样是indicator和wrapper是同级的

<div class="indicator">
    <li class="active"></li>
    <li></li>
    <li></li>
</div>
<div class="arrow-container">
    <div class="arrow-prev"><</div>
    <div class="arrow-next">></div>
</div>

样式修改

.arrow-container {
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    width: 100%;
    height: 100%;
    justify-content: space-between;
    align-items: center;
}
.arrow-prev,
.arrow-next {
    font-size: 50px;
    color: #fff;
    cursor: pointer;
}

接下来是逻辑代码

arrowPrev.onclick = function () {
    index--          // 往左走,做--
    index = index === -1 ? allLi.length - 1 : index     // 再判断是否第一张,由于先--了,所以是 0 - 1 = -1
    domChange()
}
arrowNext.onclick = function () {
    index++          // 往右走,做++
    index = index === allLi.length ? 0 : index
    domChange()
}

这时候我们对domChange函数修改一下

function domChange () {
  activeLi = document.querySelector(".active")
  activeLi.className = ''
  allLi[index].className = 'active'

  // 添加了这一句,因为很多地方都需要对这个dom进行操作
  dom.style.left = -index * stepWidth + 'px'
}

 

回到autoPaly函数中

function autoPlay () {
  index++
  index = index === childLength ? 0 : index

  // 我们去掉原来直接在这里设置的dom.style.left

  // 而是调用函数
  domChange()


  timer = setTimeout(autoPlay, 3000)
}

 

以上至此,初略写了一个带按钮,标记的轮播图。

如果你还有兴趣,比如说划过li标签的时候,让图片也动,或者是多个侧边栏,右边是轮播,然后正常显示内容。

以下是源码

 

HTML跟JS

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<link rel="stylesheet" href="./css/index.css">
</head>
<body>
	<div class="banner-container">
		<div class="nav-container">
			<div class="li-container">
				<li index="1">电子产品</li>
				<li index="2">生活用品</li>
				<li index="3">每日超市</li>
				<li index="4">电竞游戏</li>
				<li index="5">斗鱼直播</li>
			</div>
			<div class="content-container">
				<div>电子产品</div>
				<div>生活用品</div>
				<div>每日超市</div>
				<div>电竞游戏</div>
				<div>斗鱼直播</div>
			</div>
		</div>
		<div class="wrapper-container">
			<div class="wrapper" id="wrapper">
				<img src="./images/1.webp" alt="">
				<img src="./images/2.jpg" alt="">
				<img src="./images/3.webp" alt="">
			</div>
			<div class="indicator">
				<li class="active"></li>
				<li></li>
				<li></li>
			</div>
			<div class="arrow-container">
				<div class="arrow-prev"><</div>
				<div class="arrow-next">></div>
			</div>
		</div>
	</div>
	<script>
		let index = 0,
			dom = document.querySelector("#wrapper"),
			wrapper = document.querySelector(".wrapper-container"),
			timer = null,
			childLength = dom.children.length,
			stepWidth = dom.children[0].offsetWidth,
			running = false

		let activeLi = document.querySelector(".active"),
			indicator = document.querySelector(".indicator"),
			allLi = indicator.children

		let arrowPrev = document.querySelector(".arrow-prev"),
			arrowNext = document.querySelector(".arrow-next")

		let navLiWrapper = document.querySelector(".li-container"),
			contentWrapper = document.querySelector(".content-container")

		timer = setTimeout(autoPlay, 3000)

		function autoPlay () {
			if (running) {
				return
			}
			running = true
			index++
			index = index === childLength ? 0 : index
			domChange()

			setTimeout(() => running = false, 300)
			timer = setTimeout(autoPlay, 3000)
		}

		const handleMouseEnter = () => clearTimeout(timer)
		const handleMouseLeave = function () {
			clearTimeout(timer)
			timer = setTimeout(autoPlay, 3000)
		}

		wrapper.addEventListener('mouseenter', handleMouseEnter)
		wrapper.addEventListener('mouseleave', handleMouseLeave)

		Array.prototype.forEach.apply(allLi, [(value, index) => value.setAttribute("index", index)])

		const liEnter = function (e) {
			let target = e.target
			if (target.tagName === 'LI') {
				index = target.getAttribute("index")
				domChange()
			}
		}

		indicator.addEventListener('mouseover', liEnter)

		arrowPrev.onclick = function () {
			index--
			index = index === -1 ? allLi.length - 1 : index
			domChange()
		}
		arrowNext.onclick = function () {
			index++
			index = index === allLi.length ? 0 : index
			domChange()
		}

		function domChange () {
			activeLi = document.querySelector(".active")
			activeLi.className = ''
			allLi[index].className = 'active'
			dom.style.left = -index * stepWidth + 'px'
		}

		const handleNavEnter = function (e) {
			const target = e.target
			if (target.tagName !== 'LI') return
			const dom = document.querySelector('.hoverShow')
			if (dom) dom.className = ''
			const index = target.getAttribute('index')
			contentWrapper.children[index - 1].className = 'hoverShow'
		}
		const handleNavLeave = function (e) {
			const dom = document.querySelector(".hoverShow")
			if (dom) {
				dom.className = ''
			}
		}
		navLiWrapper.addEventListener("mouseover", handleNavEnter)
		navLiWrapper.addEventListener("mouseleave", handleNavLeave)
	</script>
</body>
</html>

  

 然后是CSS

* {
	padding: 0;
	margin: 0;
}
li {
	list-style-type: none;
}
.banner-container {
	width: 960px;
	height: 470px;
	margin: 50px auto;
	position: relative;
}
.nav-container {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	text-align: center;
}
.li-container {
	width: 300px;
	height: 100%;
	float: left;
	background-color: green;
}
.li-container li {
	padding: 10px 0;
	margin: 10px 0;
}
.li-container li:hover {
	cursor: pointer;
}
.content-container {
	position: relative;
	top: 0;
	left: 0;
	margin-left: 300px;
	height: 100%;
	overflow: hidden;
}
.content-container div {
	position: relative;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	display: none;
	background-color: #fff;
	z-index: 4;
}
.content-container .hoverShow {
	display: block;
}

.wrapper-container {
	position: relative;
	width: 590px;
	height: 470px;
	overflow: hidden;
	margin-left: 300px;
}
.wrapper {
	position: absolute;
	left: 0;
	top: 0;
	width: 300%;
	transition: all .5s;
}
.wrapper img {
	width: 590px;
	display: block;
	float: left;
}
.indicator {
	position: absolute;
	bottom: 10px;
	left: 50%;
	transform: translateX(-50%);
	z-index: 3;
}
.indicator li {
	display: inline-block;
	width: 10px;
	height: 10px;
	background-color: rgba(0, 0, 0, .7);
}
.indicator li.active {
	background-color: rgba(255, 255, 255, .8);
}
.indicator li:hover {
	cursor: pointer;
}

.arrow-container {
	position: absolute;
	top: 0;
	left: 0;
	display: flex;
	width: 100%;
	height: 100%;
	justify-content: space-between;
	align-items: center;
}
.arrow-prev,
.arrow-next {
	font-size: 50px;
	color: #fff;
	cursor: pointer;
}

 

致辞,再次感谢~

转载于:https://www.cnblogs.com/learnRoad/p/11270960.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值