^ _ ^ 最近参加了一个免费的华为在线课程,有一个作业是用原生js实现轮播图。练手的同时发现工作中框架用的多。原生JS不如以前那么熟练了。特此写这篇文章记录一下。希望能帮助到一些新手盆友。来一起学习吧。
功能需求
实现以下4个需求:
-
自动无缝切换图片
-
鼠标左右拖动切换图片
-
点击小点切换对应图片
-
浏览器窗口尺寸改变不影响以上功能
实现思路
html & CSS结构分析
- 清除浏览器默认样式和设置基础样式
*{
box-sizing: border-box;
}
body, ul{
margin: 0;
padding: 0;
font-family: PingFang SC,PingFangSC-Regular,Helvetica Neue,Microsoft YaHei Regular,Microsoft YaHei,宋体,"sans-serif";
font-size: 16px;
}
- 设置一个外层容器
<div class=” page”>
.page{
overflow: hidden; //让视线范围内只显示一张图片
position: relative; //用于点的定位
}
- 设置一个内层容器
<div class=”box”>
.box{
display: flex; //设置flex布局,默认从左到右依次排列。
}
不清楚flex的盆友可以看一下阮大神的文章:Flex 布局教程:语法篇
- 设置一个包住a链接和img图片的容器
<div class="slideWrapper"> <a href="xxx" target="_blank"><img class="slide" src="xxx.jpg "</a></div>
.slideWrapper{
width: 100%;
height: 100%;
flex-shrink: 0;
}
需要注意的是除了需要设置宽高100%以外,还有设置flex-shrink: 0; 不让容器等比例缩小
- 设置点击小点的样式
.dots{
position: absolute;
bottom: 45px;
left: 50%;
transform: translateX(-50%);
}
.dots span{
margin: 0 4px;
width: 8px;
height: 8px;
display: inline-block;
border-radius: 100%;
background: #000;
opacity: .2;
text-indent: -9999px;
}
span.active {
width: 28px!important;
background: #5e7ce0!important;
border-radius: 5px!important;
opacity: 1;
}
JS逻辑实现
自动无缝切换
第一步、创建函数,通过修改内层容器“box” 的transform: translateX(?px)来切换显示图片。
let boxDom = document.querySelector('.box');
function changeBoxDomStyle(offset, duration = '0ms') {
boxDom.style.transform = `translateX(${
offset}px)`;
boxDom.style.transitionDuration = duration;
}
第二步、计算当前所需偏移量,并调用设置偏移量函数carousel
,调用设置小点激活状态函数changeDotsStyle
技巧:
- 每一次的偏移量就是图片宽度 * 索引
moveWith * index
- 表面上8张图片,实际上是 8副本 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 1副本 排序,索引是0 - 9. 总共10张图片。当index = 9 也就是切换到第一张图片副本的时候,迅速切换到真正的第一张图片。因为没有动画时间,所以用户感受不到切换。
注意一定要放到boxDom.addEventListener('transitionend', () => {})
才能保证从8 -> 1副本能够顺利执行完了,再无痕迹的切换到真正的第一张图片。至于不放在这里面直接在赋值新的style会出什么副作用,就动手试试吧。 - 转换图片的同时,也要把对应索引的小点的样式设置成active的样式
let index = 1;
let imgLen = document.querySelectorAll('.slide').length;
let moveWith = document.querySelector('.slide').offsetWidth;
let currentOffset = moveWith;
const DEFAULT_DURATION = '300ms';
function carousel() {
//当切换到1副本的时候,迅速切换到真正的第一张图片
boxDom.addEventListener('transitionend', () => {
if(index === imgLen - 1){
index = 1;
currentOffset = moveWith;
changeBoxDomStyle(-currentOffset);
}
});
//
if(index < imgLen - 1){
index++;
changeDotsStyle();
currentOffset = moveWith * index;
changeBoxDomStyle(-currentOffset, DEFAULT_DURATION);
}
}
function changeDotsStyle() {
document.querySelector('.dot_item.active').classList.remove('active');
if(index === imgLen - 1){
dotItems[0].classList.add('active');
}else{
dotItems[index - 1].classList.add('active');
}
}
第三步、创建定时器调用函数carousel
const ANIMATION_INTERVAL = 3000;
function autoPlay() {
if(isAutoPlay){
return; }
isAutoPlay = true;
animate = setInterval(carousel, ANIMATION_INTERVAL);
}
点击小点切换对应图片
给每个小点绑定点击事件,获取当前的索引,调用设置偏移量函数carousel。
注: 别忘了先停止定时器,再重起定时器
function dotsControl() {
for(dot of dotItems){
dot.addEventListener("click", (e) => {
stopAutoPlay();
index = e.target.getAttribute('data-dots-index') - 1;
carousel();
autoPlay();
});
}
}
function stopAutoPlay() {
if(!isAutoPlay){
return; }
isAutoPlay = false;
clearInterval(animate);
}
鼠标拖动切换图片
这是需求里面最难的。涉及多个鼠标事件,还有一个小坑
第一步、鼠标按下时,停止定时器。获取当前的X坐标currentX
, 修改isKeyDown
为true。
let isKeyDown = false;
boxDom.addEventListener('mousedown', (e) => {
stopAutoPlay();
isKeyDown = true;
isDragingImg = false;
currentX = e.clientX;
})