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