2122 字
11 分钟
手写轮播图
Demo1:单纯的轮播动画 :robot:
<!-- demo1 简单轮播功能 未实现控制 -->
<!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>Document</title>
<style type="text/css">
body {
margin: 0;
padding: 0;
}
#carousel {
margin: auto;
width: 600px;
overflow: hidden;
position: relative;
}
#carousel img {
width: 600px;
}
#carousel>ul {
display: flex;
position: absolute;
}
#carousel>ul,
#carousel>ul>li {
list-style: none;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<!-- 轮播图容器 -->
<div id="carousel">
<ul>
<li><img src="https://www.zpzpup.com/assets/image/gd01.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd02.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd03.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd04.jpg" alt=""></li>
</ul>
</div>
<script type="text/javascript">
const imgArr = [] // 图片数组
let curIndex = 0 // 当前显示的图片
let timer = null // 定时器
function slide(slideContainer) {
const width = imgArr[curIndex].width // 获取图片宽度,得到每次需要滑动的距离
let step = 10 // 切换的步长
let curConLeft = document.querySelector('#carousel > ul').offsetLeft // 获取ul的left
let distanceMoved = 0 // 已经移动的距离
let slideInterval = setInterval(() => {
if (Math.abs(width - distanceMoved) > step) { // 判断已移动距离和应移动距离的差 与 步长的关系 → 是否还要继续执行动画
curConLeft -= step // 大于步长则不断移动
slideContainer.style.left = `${curConLeft}px` // 移动
distanceMoved += step // 更新已移动距离
} else {
clearInterval(slideInterval) // 若最后移动距离不足步长,则清除动画定时器
slideContainer.style.left = `${curConLeft - width + distanceMoved}px` // 直接完成此次动画
distanceMoved = 0 // 重设移动距离为0
if(++curIndex === imgArr.length) { // index加1,判断是否为最后一张来作边缘处理
curIndex = 0
slideContainer.style.left = 0 // 重置ul
}
}
}, 10)
}
(function start() {
// 轮播图配置
const config = {
height: '300px', // 高度
interval: 2000 // 轮播时间间隔
}
document.getElementById('carousel').style.height = config.height
document.querySelectorAll('#carousel img').forEach(v => imgArr.push(v))
const slideContainer = document.querySelector('#carousel > ul')
const li = document.createElement('li')
const img = document.createElement('img')
img.src = imgArr[0].src
li.appendChild(img)
slideContainer.appendChild(li) // 将第一张图片追加到轮播图的最后,作边缘处理
timer = setInterval(() => {
slide(slideContainer)
}, config.interval)
})()
</script>
</body>
</html>
Demo2:添加基本控制 :robot:
TIP注意:
- 对第一张和最后一张的边缘处理
- 对浏览器事件的处理,避免切换页面动画出现问题
- 用户手动操作时,注意先清除定时器
<!-- demo2 轮播图添加基本控制 -->
<!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>Document</title>
<link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_1582902_u0zm91pv15i.css">
<style type="text/css">
body {
margin: 0;
padding: 0;
}
#carousel {
margin: auto;
width: 600px;
overflow: hidden;
position: relative;
}
#carousel img {
width: 600px;
}
#carousel>ul {
display: flex;
position: absolute;
}
#carousel>ul,
#carousel>ul>li {
list-style: none;
margin: 0;
padding: 0;
}
/* 控制按钮的样式 */
#leftArrow,
#rightArrow {
position: absolute;
left: 5px;
top: 43%;
width: 25px;
height: 30px;
background-color: #eee;
display: flex;
justify-content: center;
align-items: center;
opacity: 0.7;
font-size: 20px;
cursor: pointer;
}
#rightArrow {
left: auto;
right: 5px;
}
#sliderBtn {
position: absolute;
width: 100%;
bottom: 0;
display: flex;
justify-content: flex-end;
}
.unitBtn {
width: 10px;
height: 10px;
background-color: #eee;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
.active {
background-color: #4C98F7;
}
</style>
</head>
<body>
<!-- 轮播图容器 -->
<div id="carousel">
<ul>
<li><img src="https://www.zpzpup.com/assets/image/gd01.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd02.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd03.jpg" alt=""></li>
<li><img src="https://www.zpzpup.com/assets/image/gd04.jpg" alt=""></li>
</ul>
<!-- 按钮组 -->
<div id="leftArrow" class="iconfont icon-arrow-lift"></div>
<div id="rightArrow" class="iconfont icon-arrow-right"></div>
<div id="sliderBtn"></div>
</div>
<script type="text/javascript">
const imgArr = [] // 图片数组
let curIndex = 0 // 当前显示的图片
let timer = null // 定时器
let clickAllow = true // 是否允许用户点击
const btnList = [] // 右下角的切换按钮组
function slide(slideContainer, targetIndex = curIndex + 1) {
let width = 0
// 计算切换图片需要滑动的距离
if (targetIndex > curIndex) { // 正向切换则计算当前图片到目标图片的宽度
for (let i = curIndex; i < targetIndex; ++i) {
width += imgArr[i].width
}
} else { // 逆向切换
if (targetIndex === -1) { // 特殊处理第一张图片
width = imgArr[imgArr.length - 1].width
} else {
for (let i = targetIndex; i < curIndex; ++i) {
width += imgArr[i].width
}
}
}
clickAllow = false // 禁止用户点击
let step = width / 60 // 动态设置步长
step = targetIndex < curIndex ? -step : step // 根据正向/逆向 设置step
let curConLeft = slideContainer.offsetLeft // 获取ul的left
let distanceMoved = 0 // 已经移动的距离
let slideInterval = setInterval(() => { // 开启定时器实现切换动画
if (Math.abs(width - distanceMoved) > Math.abs(step)) { // 判断已移动距离和应移动距离的差 与 步长的关系 → 是否还要继续执行动画
curConLeft -= step // 大于步长则不断移动
slideContainer.style.left = `${curConLeft}px` // 移动
distanceMoved += Math.abs(step) // 更新已移动距离
} else {
clearInterval(slideInterval) // 若最后移动距离不足步长,则清除动画定时器
let directMove = step > 0 ? (curConLeft - width + distanceMoved) : (curConLeft + width - distanceMoved)
slideContainer.style.left = `${directMove}px` // 直接完成此次动画
distanceMoved = 0 // 重设移动距离为0
curIndex = targetIndex // 设为当前index
if (curIndex === imgArr.length) { // 判断是否为最后一张来作边缘处理
curIndex = 0 // 最后一章则重置index
slideContainer.style.left = `-${imgArr[0].width}px` // 重置ul
} else if (curIndex === -1) {
curIndex = imgArr.length - 1 // 第一张则重置index
slideContainer.style.left = `-${slideContainer.offsetWidth - imgArr[imgArr.length - 1].width - imgArr[0].width}px`
}
switchBtnActive() // 激活样式
clickAllow = true // 允许点击
}
}, 10)
}
// 激活按钮的样式切换
function switchBtnActive() {
btnList.forEach(v => {
v.className = "unitBtn" // 设置其他按钮为默认样式
})
btnList[curIndex].className = "unitBtn active" // 设置当前按钮为激活样式
}
// 边缘处理
function edgeHandler(slideContainer) {
const li = document.createElement('li')
const img = document.createElement('img')
img.src = imgArr[0].src
li.appendChild(img)
slideContainer.appendChild(li) // 将第一张图片追加到轮播图的最后,作边缘处理
const li2 = document.createElement('li')
const img2 = document.createElement('img')
img2.src = imgArr[imgArr.length - 1].src
li2.appendChild(img2)
slideContainer.insertBefore(li2, slideContainer.firstChild) // 将最后一张图片追加到轮播图最前,做边缘处理
slideContainer.style.left = `-${imgArr[0].width}px` // 重置ul
}
// 对一些浏览器事件处理
function eventHandler(carousel, slideContainer, config) {
document.addEventListener('visibilitychange', () => { // 浏览器页面切换会导致动画出现问题,监听页面切换事件
if (document.visibilityState === 'hidden') { // 页面隐藏,清除定时器
clearInterval(timer)
} else {
timer = setInterval(() => {
slide(slideContainer)
}, config.interval);
}
})
carousel.addEventListener('mouseover', () => { // 鼠标移入,轮播动画停止
clearInterval(timer)
})
carousel.addEventListener('mouseleave', () => { // 鼠标移出,轮播动画继续
timer = setInterval(() => { // 重设定时器
slide(slideContainer)
}, config.interval);
})
}
// 对按钮组的处理
function createBtnGroup(carousel,slideContainer,config){
document.getElementById('leftArrow').addEventListener('click', () => {
clearInterval(timer) // 清除定时器,避免手动时干扰
if (clickAllow) { // 如果允许点击,则切换上一张
slide(slideContainer,curIndex-1)
}
timer = setInterval(() => { // 重设定时器
slide(slideContainer)
}, config.interval);
})
document.getElementById('rightArrow').addEventListener('click', () => {
clearInterval(timer)
if(clickAllow) {
slide(slideContainer,curIndex + 1)
}
timer = setInterval(() => {
slide(slideContainer)
},config.interval)
})
const sliderBtn = document.getElementById('sliderBtn') // 获取按钮组容器的引用
imgArr.forEach((v,i) => {
const btn = document.createElement('div') // 制作按钮
btn.className = i === 0 ? "unitBtn active" : "unitBtn" // 初始化按钮组样式
btn.addEventListener('click',() => {
clearInterval(timer)
if(clickAllow) { // 允许点击则进行切换
slide(slideContainer,i)
}
timer = setInterval(() => {
slide(slideContainer)
}, config.interval);
})
btnList.push(btn) // 加入按钮组
sliderBtn.appendChild(btn) // 添加进容器内
})
}
(function start() {
// 轮播图配置
const config = {
height: '300px', // 高度
interval: 1500 // 轮播时间间隔
}
const carousel = document.getElementById('carousel')
carousel.style.height = config.height
document.querySelectorAll('#carousel img').forEach(v => imgArr.push(v))
const slideContainer = document.querySelector('#carousel > ul')
edgeHandler(slideContainer) // 边缘情况处理
eventHandler(carousel,slideContainer,config) // 浏览器事件处理
createBtnGroup(carousel,slideContainer,config) // 按钮组处理
timer = setInterval(() => {
slide(slideContainer)
}, config.interval)
})()
</script>
</body>
</html>
Demo3: 纯CSS轮播功能:robot:
TIP使用Css3中animation制作自定义动画
<!DOCTYPE html>
<html>
<head>
<title>轮播图</title>
<meta charset="utf-8">
<meta name="referrer" content="no-referrer">
</head>
<link rel="stylesheet" type="text/css" href="https://at.alicdn.com/t/font_1582902_u0zm91pv15i.css">
<style type="text/css">
body{
margin: 0;
padding: 0px;
}
#carousel{
margin: auto; /* 居中 */
width: 600px; /* 设置宽度 */
position: relative; /* 相对定位 */
overflow: hidden; /* 超出隐藏 */
height: 300px;
}
#carousel img{
width: 600px; /* 设定大小 按比例缩放 */
}
#carousel > ul {
display: flex; /* 图片处理为一行 */
position: absolute; /* 设置绝对定位,实现相对于#carousel的绝对定位 */
}
#carousel > ul,
#carousel > ul > li{
padding: 0;
margin: 0;
list-style:none;
}
#carousel > ul{
animation: switch 10s ease 1s infinite alternate; /* 设定动画播放 */
}
#carousel > ul:hover{
animation-play-state: paused; /* 暂停动画 */
}
@keyframes switch{ /* 制定动画规则 */
0%,13%{
left: 0;
}
27%,41%{
left: -600px;
}
55%,69%{
left: -1200px;
}
83%,100% {
left: -1800px;
}
}
</style>
<body>
<!-- 轮播图容器 -->
<div id="carousel">
<ul> <!-- 图片容器 -->
<li>
<img src="http://www.sdust.edu.cn/__local/9/7A/B1/F29B84DEF72DD329997E8172ABA_664BA3EF_32466.jpg">
</li>
<li>
<img src="http://www.sdust.edu.cn/__local/B/F3/E4/693AB931C9FFB84646970D53BFE_C506394A_4282CA.jpg">
</li>
<li>
<img src="http://www.sdust.edu.cn/__local/F/7A/AA/E1459849AA8AB0C89854A41BD41_BF3BD857_BC0D8.jpg">
</li>
<li>
<img src="http://www.sdust.edu.cn/__local/1/95/CB/EDC1450B8FD1B8A25FAAC726AA4_A36D4253_16C91.jpg">
</li>
</ul>
</div>
</body>
</html>
Demo4: 利用scrollIntoView
实现最简易的轮播功能:robot:
TIP这个demo是之前自己发现的一个api:
*scrollIntoView*
,试着用了一下,感觉很方便,就是可能兼容性不太好
<!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>Carousel</title>
<style>
#carousel {
width: 300px;
height: 300px;
overflow: hidden;
margin: 10vh auto;
border: 3px solid #000;
position: relative;
}
.carousel-item {
width: 300px;
height: 300px;
background-color: aquamarine;
color: #000;
font-weight: 600;
font-size: 50px;
position: absolute;
text-align: center;
line-height: 300px;
}
</style>
</head>
<body>
<div id="carousel">
<div class="carousel-item" style="left: 0px;">1</div>
<div class="carousel-item" style="left: 300px;">2</div>
<div class="carousel-item" style="left: 600px;">3</div>
<div class="carousel-item" style="left: 900px;">4</div>
<div class="carousel-item" style="left: 1200px;">1</div>
</div>
<script>
/**
* @description: 这个轮播图demo是为了 尝试新发现的api -> scrollIntoView 并测试兼容性
*/
// 自动轮播
const carousel = () => {
const item = document.getElementsByClassName('carousel-item')
const interval = 1500
let currentIndex = 0, behavior = 'smooth'
setInterval(() => {
currentIndex = currentIndex % item.length
if (currentIndex === 0) {
behavior = 'auto'
} else {
behavior = 'smooth'
}
item[currentIndex++].scrollIntoView({
behavior
})
}, interval);
}
carousel()
</script>
</body>
</html>