2122 字
11 分钟
手写轮播图
Demo1:单纯的轮播动画 🤖
<!-- 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:添加基本控制 🤖
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轮播功能🤖
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实现最简易的轮播功能🤖
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>