功能需求
左右按钮显示。
点击右侧按钮一次,图片往左播放一张,以此类推, 左侧按钮同理。
图片播放的同时,下面的焦点一起变化。
点击焦点,可以播放对应的图片。
鼠标不经过轮播图, 轮播图也会自动播放图片。
鼠标经过轮播图模块的时候, 自动播放会停止。
HTML部分
<div class="banner">
<!-- 放置所有轮播图的盒子 -->
<ul class="img_box">
<li style="background-color: burlywood;">1</li>
<li style="background-color: royalblue;">2</li>
<li style="background-color: greenyellow;">3</li>
<li style="background-color: pink;">4</li>
<li style="background-color: turquoise;">5</li>
</ul>
<!-- 放置焦点的盒子 -->
<ol class="focus">
<!-- <li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li> -->
</ol>
<!-- 左右按钮 -->
<div class="left"><</div>
<div class="right">></div>
</div>
<script src="../util.js"></script> //外部引入JS文件
<script src="./index.js"></script> //外部引入JS文件
CSS部分
<style>
* {
padding: 0;
margin: 0;
}
.banner {
width: 600px;
height: 400px;
border: 5px solid black;
margin: 200px auto;
overflow: hidden;
position: relative;
}
.banner .img_box {
width: 500%;
height: 100%;
list-style: none;
display: flex;
position: absolute;
left: 0px;
}
.banner .img_box li {
width: 600px;
height: 100%;
font-size: 50px;
text-align: center;
line-height: 400px;
}
.banner .focus {
width: 200px;
height: 30px;
background-color: white;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 30px;
border-radius: 20px;
display: flex;
justify-content: space-evenly;
align-items: center;
list-style: none;
}
.banner .focus li {
width: 20px;
height: 20px;
border-radius: 50%;
background-color: rosybrown;
cursor: pointer;
}
.banner .focus .active {
background-color: red;
}
.left,
.right {
position: absolute;
transform: translateY(-50%);
font-size: 50px;
color: white;
top: 50%;
cursor: pointer;
}
.left {
left: 30px;
}
.right {
right: 30px;
}
</style>
JS部分
index.js
<script>
// 0. 获取元素
const imgBox = document.querySelector(".img_box");
const banner = document.querySelector(".banner");
const focus = document.querySelector(".focus");
// 0. 准备变量
const bannerWidth = parseInt(window.getComputedStyle(banner).width);
let index = 1; // 记录当前轮播在第几页, 默认在第一页
let timer = 0; // 记录定时器 ID, 后续方便关闭定时器
let flag = true; // 开关
// 1. 根据轮播图效果 调整 页面结构
function copyEle() {
// 将ul开头和结尾, 放两张假图
// 1.1 复制出ul内部第一个元素节点与最后一个元素节点
const firstEle = imgBox.firstElementChild.cloneNode(true);
const lastEle = imgBox.lastElementChild.cloneNode(true);
// 1.2 将第一个图插入父级末尾, 当作一个假图
imgBox.appendChild(firstEle);
// 1.3 将最后一个图插入到父级开头, 当作一个假图
imgBox.insertBefore(lastEle, imgBox.firstElementChild);
// 1.4 处理 ul 的宽度 === 内部所有 li的数量 * 可视区域的宽度
imgBox.style.width = imgBox.children.length * bannerWidth + "px";
// 1.5 处理 ul 的定位
imgBox.style.left = -(index * bannerWidth) + "px";
}
copyEle();
// 2. 根据 图片数量, 动态生成 焦点
function setFocus() {
// 现在图片有两张 假图, 所以不需要考虑假图, 直接减2即可
const imgBoxLength = imgBox.children.length - 2;
for (let i = 0; i < imgBoxLength; i++) {
// 动态生成 li
let newLi = document.createElement("li");
newLi.classList.add("new_li_item");
newLi.dataset.id = i;
if (i == 0) {
newLi.classList.add("active");
}
focus.appendChild(newLi);
}
}
setFocus();
// 3. 自动轮播
function autoPlay() {
/**
* 当前在 第 1 个 真图 -600px
* 当前在 第 2 个 真图 -1200px
* 当前在 第 3 个 真图 -1800px
* 当前在 第 x 个 真图 -(x * 视口宽度)px
*/
timer = setInterval(function () {
index++;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}, 2000);
}
// autoPlay();
// 4. 拦截轮播图, 让其滚动播放
function moveEnd() {
// 4.1 判断当前滚动到最后一张假图的时候, 让其滚动到 第一张真图
if (index == imgBox.children.length - 1) {
index = 1;
imgBox.style.left = -(index * bannerWidth) + "px";
}
// 4.2 判断当前滚动到第一张真图的时候, 让其滚动到 最后一张真图
if (index == 0) {
index = imgBox.children.length - 2;
imgBox.style.left = -(index * bannerWidth) + "px";
}
// 4.3 让焦点跟随
for (let i = 0; i < focus.children.length; i++) {
focus.children[i].classList.remove("active");
}
focus.children[index - 1].classList.add("active");
flag = true; // 运动函数执行结束, 将开关打开
}
// 5. 鼠标移入移出事件
function mouseOut() {
// 5.1 鼠标移入 banner 结束自动轮播
banner.onmouseover = () => clearInterval(timer);
// 5.2 鼠标移出时, 自动轮播
banner.onmouseout = () => autoPlay();
}
mouseOut();
// 6. 鼠标点击事件
function clickBtn() {
banner.onclick = function (e) {
if (e.target.className == "right") {
// if (flag == false) return
if (!flag) return;
flag = false; // 运动函数开始前, 关闭开关, 直到运动函数结束时打开开关
index++;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
if (e.target.className == "left") {
if (!flag) return;
flag = false; // 运动函数开始前, 关闭开关, 直到运动函数结束时打开开关
index--;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
if (e.target.className == "new_li_item") {
if (!flag) return;
flag = false; // 运动函数开始前, 关闭开关, 直到运动函数结束时打开开关
index = e.target.dataset.id - 0 + 1;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
};
}
clickBtn();
/**
* 7. 页面关闭抖动
*
* 当页面关闭时(最小化)时, 此时 DOM 不会在发生变化
*
* 但是我们的代码不会停止, 也就是定时器会一直执行
*
* 当我们再次打开页面时, 此时 DOM的真实数据, 与代码的数据已经不同
*
*
* 解决:
* 在页面关闭, 停止定时器
* 再次打开页面时, 开启定时器
*/
document.onvisibilitychange = function () {
if (document.visibilityState == "hidden") {
clearInterval(timer);
}
if (document.visibilityState == "visible") {
autoPlay();
}
};
</script>
util.js
<script>
function move(ele, options, fn = () => {}) {
let num = 0;
for (let key in options) {
const type = key;
let target = options[key];
if (type == "opacity") {
target *= 100;
}
num++;
const timer = setInterval(function () {
let count;
if (type == "opacity") {
count = window.getComputedStyle(ele)[type] * 100;
} else {
count = parseInt(window.getComputedStyle(ele)[type]);
}
let des = (target - count) / 10;
des = des > 0 ? Math.ceil(des) : Math.floor(des);
if (target === count) {
num--;
if (num == 0) {
fn();
}
clearInterval(timer);
} else {
if (type == "opacity") {
ele.style[type] = (count + des) / 100;
} else {
ele.style[type] = count + des + "px";
}
}
}, 20);
}
}
</script>