「公众号:追梦web前端课堂」
我们以京东首页轮播图为例来讲解
第一步:需求分析
- 自动循环轮播
- 过渡动画为渐隐渐现
- 控制流结构 有 圆点按钮 和 上一张、下一张 按钮
- 鼠标经过圆点切换到当前图片并且轮播停止,鼠标离开轮播开始。
第二步:写出轮播图的静态页面结构
- 轮播图一般为列表结构,可以用ul
- 轮播图的图片位置是在重叠在一起的,可以用 css 绝对定位
- 在轮播图容器中只能显示一张图片,其他图片都隐藏。由于 轮播动画是渐隐渐现。所以可以把所有的图片透明度设为0,通过 使用修饰符来改变图片的状态。
- 样式名 可以采用 BEM 命名
HTML结构
<!--轮播容器-->
<div id="my-slider" class="slider-list">
<!-- 图片列表 -->
<ul>
<li class="slider-list__item slider-list__item--active">

</li>
<li class="slider-list__item">

</li>
<li class="slider-list__item">

</li>
</ul>
</div>```
<!--轮播容器-->
<div id="my-slider" class="slider-list">
<!-- 图片列表 -->
<ul>
<li class="slider-list__item slider-list__item--active">
<img src="./img/1.jpg" alt="" class = "slider-list__img">
</li>
<li class="slider-list__item">
<img src="./img/2.jpg" alt="" class ="slider-list__img">
</li>
<li class="slider-list__item">
<img src="./img/3.jpg" alt="" class = "slider-list__img">
</li>
</ul>
</div>
CSS代码
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
#my-slider {
width: 600px;
height:400px
}
/* 图片列表 */
.slider-list {
position: relative;
height: 100%;
}
.slider-list ul {
height: 100%;
}
.slider-list__img {
width: 100%;
height: 100%;
}
.slider-list__item {
position: absolute;
opacity: 0;
}
.slider-list__item--active {
transition: opacity 1s;
opacity: 1;
}
第三步:结构写好之后,开始具体的实现
实现思路:
- 主要通过 修饰符 slider-list__item--active 来改变图片的显示隐藏状态。( 显示图片 我们可以 添加 该修饰符, 隐藏图片 我们可以移除该修饰符)
- 切换动画 通过 transition 来实现
- 循环轮播 要实现的 一个小难点,就是如何 从最后一张 过渡到 第1张。
- 那 最笨的办法就是 当 轮播图片到最后一张的时候,把 当前图片的位置强制设置为第一张。
- 当然了,我们还有更好的办法来实现。这里是有1个小技巧,我们可以通过 余数定理 来实现。假如 轮播图片是4张。那么 轮播图是以 4 为周期循环的。任何数 除以 4 的余数 一定在 4 的范围内。而余数代表是当前 图片的位置。知道了 图片的位置,就可以得到 当前图片的节点,那么我们就可以很容易的操作 图片的状态了。
- 这里有2个公式,大家可以套用
- 上一张图片的下标 = ( 当前图片的下标 - 1 + 图片的个数 ) % 图片的个数
- 下一张图片的下标= ( 当前图片的下标 + 1 ) % 图片的个数
JS代码
document.addEventListener("DOMContentLoaded",(e) => {
// 获取轮播容器
const container = document.getElementById("my-slider");
// 获取轮播的图片集合
const items = container.querySelectorAll(".slider-list__item");
// 定时器变量
let timer = null;
// 多长时间轮播一张图片
const interval = 2000;
// 获取当前的图片dom节点
function getCurrentItem(){
return container.querySelector(".slider-list__item--active")
}
// 获取当前的图片位置
function getCurrentItemIndex(){
return Array.from(items).indexOf(getCurrentItem())
}
/**
* 获取控制点的位置
* @param {String} target 鼠标经过的节点
* @return {Number} 鼠标经过节点的位置
*/
function getControllerIndex(target){
const nextItemIndex = Array.from(buttons).indexOf(target);
return nextItemIndex
}
//下一张图片
function next() {
// 获取下一张图片的位置
const nextItemIndex = ( getCurrentItemIndex() + 1) % items.length;
// 切换
slideTo(nextItemIndex)
}
/**
* 切换到指定位置的图片
* @param {number} index 图片的位置
* @return {undefined}
*/
function slideTo( index ) {
// 获取当前展示的图片 节点
const currentItem = getCurrentItem(),
// 获取下一次要展示的图片节点
nextItem = items[index];
// 隐藏 当前的图片
currentItem.classList.remove("slider-list__item--active")
// 显示 指定 index 的图片
nextItem.classList.add("slider-list__item--active")
}
//自动轮播
function autoStart() {
timer = setInterval(function(){
// 切换到下一张
next()
},interval)
}
//开始轮播
autoStart()
});
到此,图片轮播的核心功能就实现了。
接下来 我们来实现 控制结构 小圆点 和 上一张/下一张按钮的功能
首先来实现 上一张/ 下一张 的 功能
分析
- 当 点击 上一张 或者 下一张 的时候 需要 先 停止 自动轮播 、 然后 切换到下一张 再开启自动轮播
- 注意 这里 需要用到节流函数(我这里没有实现,大家上网查一下)
- 由于 我是用的 a 标签来模拟的按钮,所以 绑定事件的时候一定要阻止 a标签的默认事件
在轮播容器中 增加 上一张/下一张的结构
HTML结构
<!--上一张-->
<a href="" class="slide-list__previous"></a>
<!--下一张-->
<a href="" class="slide-list__next"></a>
CSS代码
/* 上一张 下一张 样式 */
.slide-list__next,
.slide-list__previous {
position: absolute;
top: 50%;
width: 30px;
height: 35px;
background: rgba(0, 0, 0, 0.5);
transform: translateY(-50%);
color: white;
text-align: center;
line-height: 35px;
text-decoration: none;
}
.slide-list__next {
right: 0;
}
.slide-list__previous::before {
content: "<";
}
.slide-list__next::before {
content: ">";
}
JS代码
// 获取上一张按钮
const previousButton = container.querySelector(".slide-list__previous")
// 获取下一张按钮
const nextButton = container.querySelector(".slide-list__next")
// 给上一张按钮绑定事件
previousButton.addEventListener("click",(e)=>{
stop()
previous()
autoStart()
e.preventDefault()
})
// 给下一张按钮绑定事件
nextButton.addEventListener("click",(e)=>{
stop()
next()
autoStart()
e.preventDefault()
})
接下来实现 圆点的功能
HTML结构
<!-- 新增小圆点 -->
<div class="slider-list__controller">
<span class="slider-list__controller-button slider-list__controller-button--active"></span>
<span class="slider-list__controller-button"></span>
<span class="slider-list__controller-button"></span>
<span class="slider-list__controller-button"></span>
</div>
CSS代码
/* 新增小圆点样式 */
.slider-list__controller {
position: absolute;
left: 20px;
bottom: 30px;
}
.slider-list__controller-button {
display: inline-block;
width: 10px;
height: 10px;
background: rgba(255, 255, 255,.4);
border-radius: 50%;
}
.slider-list__controller-button--active {
background: orange;
}
分析:
- 当自动轮播开始的时候或者点击上一张/下一张按钮时,图片的切换 和 小圆点的切换 要 一一 对应,并且当前小圆点要高亮显示。
- sliderTo 函数中 也需要增加 小圆点高亮的逻辑
JS代码
//获取 小圆点 controller 容器
const controller = container.querySelector(".slider-list__controller");
// 获取 小圆点集合
const buttons = controller.querySelectorAll(".slider-list__controller-button");
// 鼠标经过事件停止自动轮播, 并切换到当前位置
controller.addEventListener("mouseover",(e)=>{
const nextItemIndex = getControllerIndex(e.target)
// 如果存在
if(nextItemIndex > -1){
// 停止轮播
stop()
slideTo(nextItemIndex)
}
})
// 鼠标离开时候启动 自动轮播
controller.addEventListener("mouseout",(e)=>{
const nextItemIndex = getControllerIndex(e.target)
//如果存在
if(nextItemIndex > -1){
// 自动播放
autoStart()
}
})
function slideTo( index ) {
// 获取当前展示的图片 节点
const currentItem = getCurrentItem(),
// 获取下一次要展示的图片节点
nextItem = items[index],
// 获取 当前展示 圆点的 节点
currentButton = buttons[getCurrentItemIndex()],
// 获取 下一次要展示的圆点节点
nextButton = buttons[index]
// 隐藏 当前的图片
currentItem.classList.remove("slider-list__item--active")
// // 显示 指定 index 的图片
nextItem.classList.add("slider-list__item--active")
// 取消 当前圆点的高亮状态
currentButton.classList.remove("slider-list__controller-button--active")
// 添加 要展示圆点的高亮状态
nextButton.classList.add("slider-list__controller-button--active")
}
至此,一个简单的轮播图就实现完成了。
但是,当1个页面中有2个 轮播图的情况下。我们写的代码 很难复用。会造成的代码的重复。
还有, 当我们需要添加 除了 圆点控制器外的 其他控制器的话,就需要 更改 slideTo函数。会造成控制器与 图片轮播的代码强耦合。
那下一篇,我们来基于类 重新组织的我们的代码。使我们的轮播组件能够可复用和可扩展。
附:第一版本 完整代码:
HTML代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮播组件开发</title>
<link rel="stylesheet" href="./slider.css">
</head>
<body>
<div id="my-slider" class="slider-list">
<!-- 图片 -->
<ul>
<li class="slider-list__item slider-list__item--active">

</li>
<li class="slider-list__item">

</li>
<li class="slider-list__item">

</li>
</ul>
<!--小圆点-->
<div class="slider-list__controller">
<span class="slider-list__controller-button slider-list__controller-button--active"></span>
<span class="slider-list__controller-button"></span>
<span class="slider-list__controller-button"></span>
</div>
<!--上一张-->
<a href="" class="slide-list__previous"></a>
<!--下一张-->
<a href="" class="slide-list__next"></a>
</div>
<script src="./slider1.js"></script>
</body>
</html>```
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>轮播组件开发</title>
<link rel="stylesheet" href="./slider.css">
</head>
<body>
<div id="my-slider" class="slider-list">
<!-- 图片 -->
<ul>
<li class="slider-list__item slider-list__item--active">
<img src="./img/1.jpg" alt="" class = "slider-list__img">
</li>
<li class="slider-list__item">
<img src="./img/2.jpg" alt="" class ="slider-list__img">
</li>
<li class="slider-list__item">
<img src="./img/3.jpg" alt="" class = "slider-list__img">
</li>
</ul>
<!--小圆点-->
<div class="slider-list__controller">
<span class="slider-list__controller-button slider-list__controller-button--active"></span>
<span class="slider-list__controller-button"></span>
<span class="slider-list__controller-button"></span>
</div>
<!--上一张-->
<a href="" class="slide-list__previous"></a>
<!--下一张-->
<a href="" class="slide-list__next"></a>
</div>
<script src="./slider1.js"></script>
</body>
</html>
CSS代码
* {
margin:0;
padding:0
}
ul{
list-style:none
}
#my-slider {
width: 600px;
height:400px
}
/* 图片列表 */
.slider-list{
position:relative;
}
.slider-list ul{
height:100%
}
.slider-list__item {
position:absolute;
opacity: 0;
height:100%;
}
.slider-list__img {
width:100%;
height:100%;
}
.slider-list__item--active {
transition: opacity 1s;
opacity: 1;
}
/* 上一张 下一张 样式 */
.slide-list__next,
.slide-list__previous {
position: absolute;
top: 50%;
width: 30px;
height: 35px;
background: rgba(0, 0, 0, 0.5);
transform: translateY(-50%);
color: white;
text-align: center;
line-height: 35px;
text-decoration: none;
}
.slide-list__next {
right: 0;
}
.slide-list__previous::before {
content: "<";
}
.slide-list__next::before {
content: ">";
}
.slide-list__next::before {
content: ">";
}
/* 新增小圆点样式 */
.slider-list__controller {
position: absolute;
left: 20px;
bottom: 30px;
}
.slider-list__controller-button {
display: inline-block;
width: 10px;
height: 10px;
background: rgba(255, 255, 255,.4);
border-radius: 50%;
}
.slider-list__controller-button--active {
background: orange;
}
js代码
document.addEventListener("DOMContentLoaded",(e) => {
// 获取轮播容器
const container = document.getElementById("my-slider");
// 获取轮播的图片集合
const items = container.querySelectorAll(".slider-list__item");
// 定时器变量
// let timer = null;
// 多长时间轮播一张图片
const interval = 5000;
// 获取上一张按钮
const previousButton = container.querySelector(".slide-list__previous")
// 获取下一张按钮
const nextButton = container.querySelector(".slide-list__next")
//获取 小圆点 controller 容器
const controller = container.querySelector(".slider-list__controller");
// 获取 小圆点集合
const buttons = controller.querySelectorAll(".slider-list__controller-button");
// 给上一张按钮绑定事件
previousButton.addEventListener("click",(e)=>{
stop()
previous()
autoStart()
e.preventDefault()
})
// 给下一张按钮绑定事件
nextButton.addEventListener("click",(e)=>{
stop()
next()
autoStart()
e.preventDefault()
})
// 鼠标经过事件停止自动轮播, 并切换到当前位置
controller.addEventListener("mouseover",(e)=>{
const nextItemIndex = getControllerIndex(e.target)
// 如果存在
if(nextItemIndex > -1){
// 停止轮播
stop()
slideTo(nextItemIndex)
}
})
// 鼠标离开时候启动 自动轮播
controller.addEventListener("mouseout",(e)=>{
const nextItemIndex = getControllerIndex(e.target)
//如果存在
if(nextItemIndex > -1){
// 自动播放
autoStart()
}
})
// 获取当前的图片dom节点
function getCurrentItem(){
return container.querySelector(".slider-list__item--active")
}
// 获取当前的图片位置
function getCurrentItemIndex(){
return Array.from(items).indexOf(getCurrentItem())
}
/**
* 获取控制点的位置
* @param {String} target 鼠标经过的节点
* @return {Number} 鼠标经过节点的位置
*/
function getControllerIndex(target){
const nextItemIndex = Array.from(buttons).indexOf(target);
return nextItemIndex
}
//下一张图片
function next() {
// 获取下一张图片的位置
const nextItemIndex = ( getCurrentItemIndex() + 1) % items.length;
// 切换
slideTo(nextItemIndex)
}
/**
* 切换到指定位置的图片
* @param {number} index 图片的位置
* @return {undefined}
*/
function slideTo( index ) {
// 获取当前展示的图片 节点
const currentItem = getCurrentItem(),
// 获取下一次要展示的图片节点
nextItem = items[index],
// 获取 当前展示 圆点的 节点
currentButton = buttons[getCurrentItemIndex()],
// 获取 下一次要展示的圆点节点
nextButton = buttons[index]
// 隐藏 当前的图片
currentItem.classList.remove("slider-list__item--active")
// // 显示 指定 index 的图片
nextItem.classList.add("slider-list__item--active")
// 取消 当前圆点的高亮状态
currentButton.classList.remove("slider-list__controller-button--active")
// 添加 要展示圆点的高亮状态
nextButton.classList.add("slider-list__controller-button--active")
}
//自动轮播
function autoStart() {
let timer = setInterval(function(){
// 切换到下一张
next()
},interval)
}
//开始轮播
autoStart()
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。