功能
- 点击两边的翻页按钮,就翻页
- 鼠标移入暂停自动播放,鼠标移出继续自动播放
- 点击下面的波尔多绿点,就切换
结构
- 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>