前端面試之基礎題——輪播圖


前言: 輪播圖應該是每個前端人員都會的東西。寫這一篇的原因是去面試,然后面試官讓我寫輪播圖,寫是寫出來了, 但是思路很混亂,代碼很丑....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;
}

 

致辭,再次感謝~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM