求star

开源不易,喜欢请点个star吧

Ocean Han
2122 字
11 分钟
手写轮播图
2023-03-01

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>
手写轮播图
https://blog.oceanh.top/posts/frontend/手写轮播图/
作者
Ocean Han
发布于
2023-03-01
许可协议
CC BY-NC-SA 4.0
最后修改时间
2024-08-10 10:08:49