用JS实现轮播效果图
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>
CSS代码
* {
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;
}
JS代码
// 0. 获取 DOM
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. 通过 JS 处理前后两张 '假图'
*
* 1.1 将 ul 内最后一个 li 复制出来, 放在 ul 的开头
* 1.2 将 ul 内第一个 li 复制出来, 放在 ul 的末尾
*/
copyEle();
function copyEle() {
// 1.1 复制出来两个 li
const fakeFirst = imgBox.firstElementChild.cloneNode(true);
const fakeLast = imgBox.lastElementChild.cloneNode(true);
// 1.2 将复制出来 li 放在 对应的位置
imgBox.appendChild(fakeFirst);
imgBox.insertBefore(fakeLast, imgBox.firstElementChild);
// 1.3 处理 ul 的宽度
imgBox.style.width = imgBox.children.length * bannerWidth + "px";
// 1.4 处理 ul 的定位
imgBox.style.left = -(index * bannerWidth) + "px";
}
// 2. 根据 imgBox 内部的图片数量, 决定渲染几个 焦点
setFocus();
function setFocus() {
// 2.1 获取到 图片数量
const liNum = imgBox.children.length - 2;
// 2.2 根据图片数量, 创建出对应数量的 li 并且 插入到 DOM 节点中
for (let i = 0; i < liNum; i++) {
// 2.2.1 创建 li 节点
const newLi = document.createElement("li");
newLi.classList.add("focus_item");
// 将元素的下标存储在标签上
newLi.dataset.id = i;
// 2.2.2 给第一个 li 添加一个类名
// if (i === 0) newLi.classList.add('active')
i === 0 && newLi.classList.add("active");
// 2.2.3 将创建出来的节点, 插入到元素中
focus.appendChild(newLi);
}
}
/**
* 3. 自动轮播
* => 假设 每间隔 3000ms 切换到下一张
* + [0] 对应 第一张假图
* + [1] 对应 第 1 张
* + [2] 对应 第 2 张
* + [3] 对应 第 3 张
* + .....
* + [6] 对应 最后一张假图
*
* => 目前是在 [1] 此时的定位 -600px
* + 3000ms 后应该是在 [2], 此时定位应该是 -1200px
* + 6000ms 后应该是在 [3], 此时定位应该是 -1800px
* + 若干秒后 应该是在 [x], 此时定位应该是 -(x * 轮播图可视区域的宽度)
*
* => 自动轮播核心: 跟随定时器 改变 index(内部存储的值代表当前在 第几张图)
*/
autoPlay();
function autoPlay() {
timer = setInterval(() => {
// 修改当前页的值
index++;
// 在定时器内部改变 ul 的定位
move(
imgBox,
{
left: -(index * bannerWidth),
},
moveEnd
);
}, 1000);
}
/**
* 4. 在某一张图片运动完成之后, 需要将 ul 重新定位到 最开始的位置(第一张真图)
*/
function moveEnd() {
// 4.1 当运动结束后, 将 ul 的定位 拉回到 第一张真图的位置
if (index === imgBox.children.length - 1) {
imgBox.style.left = -600 + "px";
index = 1;
}
// 4.2 当运动到第一张假图的时候, 立马跳转到 最后一张真图
if (index === 0) {
index = imgBox.children.length - 2;
imgBox.style.left = -(index * bannerWidth) + "px";
}
// 4.3 调整焦点高亮
// 4.3.1 将 所有的 焦点 取消高亮(去掉 active 类名)
for (let i = 0; i < focus.children.length; i++) {
// focus.children[i].className = "";
focus.children[i].classList.remove("active");
}
// 4.3.2 将我们需要的 哪一个 焦点 添加高亮(添加 active 类名)
// focus.children[index - 1].className = "active";
focus.children[index - 1].classList.add("active");
// 4.4 执行到这里的时候, 说明这次运动已经完全结束了, 所以可以打开开关变量, 方便下次点击能够正常执行
flag = true;
}
/**
* 5. 移入移出事件
* 1. 移入时停止 (结束定时器)
* 2. 移出时开启 (再次调用 autoPlay 函数)
*/
mouseMove();
function mouseMove() {
// 5.1 鼠标移入 结束定时器
// banner.onmouseover = function () { clearInterval(timer) };
// banner.onmouseover = () => { clearInterval(timer) };
banner.onmouseover = () => clearInterval(timer);
// 5.2 鼠标移出 再次调用 autoPlay
banner.onmouseout = () => autoPlay();
}
/**
* 6. 添加点击事件
* + 左右按钮
* + 各个焦点
* + 因为都是 banner 的后代元素
* + 所以我们可以利用事件冒泡将事件委托给父级
*/
clickBtn();
function clickBtn() {
banner.onclick = function (e) {
// 6.1 上一页
if (e.target.className === "left") {
// 先判断开关变量, 决定能否执行下边的代码
if (flag === false) return;
// 代码运行到这个地方, 说明要开始运动了, 所以修改开关变量的值, 让他下次点击不能正常执行
flag = false;
// 修改当前页的值
index--;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
// 6.2 下一页
if (e.target.className === "right") {
// 先判断开关变量, 决定能否执行下边的代码
if (flag === false) return;
// 代码运行到这个地方, 说明要开始运动了, 所以修改开关变量的值, 让他下次点击不能正常执行
flag = false;
// 修改当前页的值
index++;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
// 6.3 点击各个焦点
if (e.target.className === "focus_item") {
// 先判断开关变量, 决定能否执行下边的代码
if (flag === false) return;
// 代码运行到这个地方, 说明要开始运动了, 所以修改开关变量的值, 让他下次点击不能正常执行
flag = false;
// 跳转到对应的图片 - 0 是因为 dataset 获取到的是一个字符串, 如果直接 +1, 那么不是加法, 而是拼接
index = e.target.dataset.id - 0 + 1;
move(imgBox, { left: -(index * bannerWidth) }, moveEnd);
}
};
}
/**
* 7. 关闭页面后(最小化), 重新打开页面时, 轮播图抖动
*
* 浏览器最小化之后 DOM 的运动就会停止
* 但是代码中的 定时器并不会停止
*
* 所以再次打开的时候, 会重新执行 DOM 的运动, 但是此时可能堆积了 很多个运动函数
*
*
* 解决: 最小化页面时, 停止定时器
* 重新打开时, 开启定时器
*/
overoutDocument();
function overoutDocument() {
document.onvisibilitychange = function () {
console.log(document.visibilityState)
/**
* document.visibilityState:
* hidden => 最小化页面
* visible => 重新打开页面
*/
if (document.visibilityState === 'hidden') {
clearInterval(timer)
}
if (document.visibilityState === 'visible') {
autoPlay()
}
}
}