49行JS代码,搞一个轮播图

637 阅读3分钟

功能

  • 点击两边的翻页按钮,就翻页
  • 鼠标移入暂停自动播放,鼠标移出继续自动播放
  • 点击下面的波尔多绿点,就切换

结构

  • carousel:轮播图
  • pre和next:翻页按钮
  • dot:下面的点状指示器
  • card:被切换的卡片
  • play-ground:把card包裹起来,整体移动
  • screen:一个显示区域,让你只能看到一张card
 <div id="carousel">
        <div id="pre" class="page-btn">上一页</div>
        <div id="wrapper">
            <div id="screen">
                <div id="play-ground" class="play-ground">
                    <div class="card" style="background-color:goldenrod;">1</div>
                    <div class="card" style="background-color: rgb(252, 84, 84);">2</div>
                    <div class="card" style="background-color:skyblue;">3</div>
                    <div class="card" style="background-color:pink;">4</div>
                    <div class="card" style="background-color: rgb(117, 209, 99);">5</div>
                </div>
            </div>

            <div id="pagenation">
                <span data-id="1" class="dot checked"></span>
                <span data-id="2" class="dot"></span>
                <span data-id="3" class="dot"></span>
                <span data-id="4" class="dot"></span>
                <span data-id="5" class="dot"></span>
            </div>
        </div>
        <div id="next" class="page-btn">下一页</div>
    </div>

思路

先让轮播图动起来

  • play-ground把每个card都框起来,然后左右移动这个play-ground就相当于移动了card
  • 搞一个全局变量index,表示当前play-ground的偏移量系数,初始值为1,这样比较符合人类习惯
index = 1;
// 函数play用来改变play-ground的偏移量
function play() {
    //Card.item(0).offsetWidth表示的是:一个card的宽度
    PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";
}
play();

让轮播图可以被你动起来

  • index自增一次,偏移量就会增加一次,也就是play-ground往左平移了一个card的宽度,同理,index自减一次...你懂的
  • 当你点击pre和next这俩按钮,index就会被改变,继而play-ground的偏移量也就改变,这个功能实现以后,你就可以把轮播图一左一右切着玩儿了;当然,点击dot也要能切换
Pre.addEventListener('click', () => {
    index--;
    play();
})
Next.addEventListener('click', () => {
    index++;
    play();
})
Dot.forEach(dot => {
    dot.addEventListener('click', (e) => {
    index = e.target.dataset.id;
    play();
});

让轮播图可以循环播放

  • 比如play-ground里面现在有5个card,切到第5个你再切换,就没有card了,所以要让它循环起来
  • if判断index大于5的话就立马回到1,index小于1的时候,就立马回到5。这样就实现了循环
if (index == 6) { index = 1; };
if (index == 0) { index = 5; };
PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";

// 更新dot样式:被点击的dot会加上特别好看的绿色背景,其余的保持白色
Dot.forEach(item => (item.classList.remove('checked')));
Dot[index - 1].classList.add('checked');

让轮播图可以自己动起来

  • 搞一个全局变量timer,用作定时器,让它定时的去改变index的值,默认是自动切换到下一张,也就是index++
  • 鼠标移入screen的时候定时器就要暂停,这样你才有时间去看card的内容,鼠标移出screen就继续自动播放
// 函数autoPlay用来实现自动播放
function autoPlay() {
    index++;
    play();
}
timer = setInterval(autoPlay, 3000);
Screen.onmouseenter = () => {
    clearInterval(timer);
}
Screen.onmouseleave = () => {
    timer = setInterval(autoPlay, 3000);
};

到此,轮播图可以任意切换、循环播放、自动播放

轮播图当然要加上动画才更香嘛!无缝切换😎

加上动画

// CSS过渡,简单好使
PlayGround.style.transition = "all 0.8s ease-in-out";
PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";

无缝切换

意思就是到了最后一张card,此时再往下切换时,显示的要是第1张card

  • 复制card5节点,然后插入到card1前面,这样,点击上一张以后你会看到card5和card1拼接在了一起,同理,复制card1节点,然后插入到card5后面,你懂的...
// 使用 cloneNode()复制节点
const nextclone = Card[0].cloneNode(true);
const preclone = Card[Card.length - 1].cloneNode(true);
PlayGround.insertBefore(preclone, PlayGround.firstChild);
PlayGround.appendChild(nextclone);

实现真正的循环

  • 当判断index超出终点之后,index会被赋值为1,即回到起点,但是看到的动画效果却是从终点一下子退回到起点,看起来就是这个亚子,这就把我难到了😯

  • 解决办法就是:
    • 当index > 5以后(此时来到了插入到最后的那个card1),这个card1的动画结束后以后,立即移除play-ground的动画,让play-gorund的偏移量,经过javascript强大的DOM处理和i7八代处理器极速的运算,瞬间移动到真正的card1处,此时index被赋值为1(顺便恢复动画)——这就完成了一轮无缝循环
 function play() {
    PlayGround.style.transition = "all 0.8s ease-in-out";
    PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";
    // 添加 transitionend 事件监听
    PlayGround.addEventListener('transitionend', () => {
        if (index == 6) { index = 1; };
        if (index == 0) { index = 5; };
        PlayGround.style.transition = "all ease-in-out";// 移除动画
        PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";
        Dot.forEach(item => (item.classList.remove('checked')));
        Dot[index - 1].classList.add('checked');
    });
}

至此,轮播图就完成了!😎

放个源代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>轮播图</title>
</head>

<body>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #carousel {
            width: 800px;
            height: 500px;
            border: grey 2px solid;
            display: flex;
        }

        .page-btn {
            width: 100px;
            height: 500px;
            text-align: center;
            font-weight: bold;
            line-height: 500px;
        }

        #wrapper {
            width: 600px;
            height: 500px;
        }

        #screen {
            width: 600px;
            height: 400px;
            display: flex;
            overflow: hidden;
        }

        .play-ground {
            position: relative;
            display: flex;
        }

        #pagenation {
            width: 100%;
            height: 100px;
            text-align: center;
        }

        .card {
            min-width: 600px;
            height: 400px;
            text-align: center;
            line-height: 400px;
            font-size: 5rem;
        }

        .dot {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            border: teal 1px solid;
        }

        .checked {
            background-color: teal;
        }
    </style>
    <div id="carousel">
        <div id="pre" class="page-btn">上一页</div>
        <div id="wrapper">
            <div id="screen">
                <div id="play-ground" class="play-ground">
                    <div class="card" style="background-color:goldenrod;">1</div>
                    <div class="card" style="background-color: rgb(252, 84, 84);">2</div>
                    <div class="card" style="background-color:skyblue;">3</div>
                    <div class="card" style="background-color:pink;">4</div>
                    <div class="card" style="background-color: rgb(117, 209, 99);">5</div>
                </div>
            </div>

            <div id="pagenation">
                <span data-id="1" class="dot checked"></span>
                <span data-id="2" class="dot"></span>
                <span data-id="3" class="dot"></span>
                <span data-id="4" class="dot"></span>
                <span data-id="5" class="dot"></span>
            </div>
        </div>
        <div id="next" class="page-btn">下一页</div>
    </div>

    <script>
        const PlayGround = document.getElementById('play-ground');
        const Card = document.querySelectorAll('.card');
        const Next = document.getElementById('next');
        const Pre = document.getElementById('pre');
        const Dot = document.querySelectorAll('.dot');
        const Screen = document.getElementById('screen');
        const nextclone = Card[0].cloneNode(true);
        const preclone = Card[Card.length - 1].cloneNode(true);
        PlayGround.insertBefore(preclone, PlayGround.firstChild);
        PlayGround.appendChild(nextclone);
        index = 1;
        play();
        timer = setInterval(autoPlay, 2000);
        Screen.onmouseenter = () => {
            clearInterval(timer)
        }
        Screen.onmouseleave = () => {
            timer = setInterval(autoPlay, 2000);
        };
        Pre.addEventListener('click', () => {
            index--;
            play();
        })
        Next.addEventListener('click', () => {
            index++;
            play();
        })
        Dot.forEach(dot => {
            dot.addEventListener('click', (e) => {
                index = e.target.dataset.id;
                play();
            });
        })
        function play() {
            PlayGround.style.transition = "all 0.8s ease-in-out";
            PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";
            PlayGround.addEventListener('transitionend', () => {
                if (index == 6) { index = 1; };
                if (index == 0) { index = 5; };
                PlayGround.style.transition = "all ease-in-out";
                PlayGround.style.left = (index) * -Card.item(0).offsetWidth + "px";
                Dot.forEach(item => (item.classList.remove('checked')));
                Dot[index - 1].classList.add('checked');
            });
        }
        function autoPlay() {
            index++;
            play();
        }
    </script>
</body>

</html>