一、轮播图需求分析
- 需要实现每隔一定时间进行下一张图片的自动轮播
- 需要点击左右按钮可以进行上下图片的切换
- 显示当前位置的小圆点
- 点击小圆点可以切换到对应的图片上
- 鼠标移入可视区时,停止轮播,显示左右按钮
下面我们来看一下效果图:?
二、轮播图的原理
- 图片移动实现原理:利用浮动将所有所有照片依次排成一行,给这一长串图片ul添加一个父级的遮罩(就是可视区),?每次只显示一张图,其余的都隐藏起来。对图片添加绝对定位,通过控制left属性,实现照片的移?动。
- 图片移动动画原理:从当前图片移动到下一张图片位置,计划出每次移动的固定步长,通过添加定时器的方法,每次? 向左移动相同的步长,实现动画效果。
- 图片定位停止原理:每一张照片都有相同的宽度,通过检测定每次移动后,判断这一长串图片ul 左偏移的宽度是否已经超过或者是等于 一个图片的宽度了,如果超出或者刚好等于,说明已经移动到位,可以将定时器清除并设置left属性为0(确保每次移动完成后图片左边与可视区的左边框刚好贴近),来停止动画。
- 无缝衔接原理:由于图片的基本移动原理。要想实现无缝衔接,两张图片就必须紧贴在一起。(我这里写的轮播图默认是向左移动的)所以当每一次向左轮播移动图片完成后,需要把前面那一张图片通过appendChild()方法(这个方法除了在原来的父元素上添加子元素之外还有一个功能就是,将原来的子元素删除,重新插入到父元素中子元素的最后一个。)把它移动到整个长串图片最后面,这样每一次在可视区显示的图片就相当于整个长串图片的第一张;如果是向右移动(就是移动到当前可视区显示图片的上一张图片==最后哪一张图片),先把整个长串图片ul?左偏移一个图片的宽度,再通过insertBefore()方法,把最后面哪一张图片插入到可视区显示的图片的前面去。然后再根据移动原理就可以实现图片的无缝连接拉。
-
左右点击切换原理(点击左按钮整个长串图片向右移,点击右按钮整个长串图片向左移):点击左按钮,先把图片插到可视区显示的图片的前面并设置left? 后移动;右按钮,先移动,移动完成后把图片插回到最后面去。?这里需要注意与自动轮播之间的冲突。当点击事件触发之后,停止自动轮播计时器,开始切换。当动画结束后再次添加自动轮播计时器。 -
小圆点的位置显示原理:每次触发动画时,通过全局变量标记,获取当前图片下标索引,操作清除所有小圆点,然后指定一页添加样式。点击小圆点,先判断所点击的小圆点对应图片的下标索引是在 当前可视区显示的图片下标索引的左边还是右边,然后计算两点之间的差值,通过差值和时间间隔,计算出每次移动的步长,按照移动动画的原理,移动到所点击小圆点对应的图片的位置,在可视区显示出来。?
图片演示:
? ?这是默认自动轮播图片时(或者是点击一次右按钮时)? 图片切换的一个过程:
? ?点击左按钮时图片切换的一个过程:
?
三、完整代码实现
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
img {
width: 500px;
height: 300px;
vertical-align: top;
}
/*取消图片底部3像素距离*/
.box {
width: 500px;
height: 300px;
margin: 150px auto;
background-color: pink;
border: 2px solid tan;
position: relative;
overflow: hidden;
}
.box #ul li {
float: left;
}
.box #ul {
position: absolute;
left: 0;
top: 0;
}
#left,
#right {
position: absolute;
width: 48px;
height: 48px;
border-radius: 50%;
cursor: pointer;
display: none;
}
#left {
background: url('./img/left-btn.png') no-repeat;
background-size: 100%;
left: 10px;
top: 50%;
transform: translate(0, -50%);
}
#right {
background: url('./img/right-btn.png') no-repeat;
background-size: 100%;
right: 10px;
top: 50%;
transform: translate(0, -50%);
}
.dot {
/* height: 50px; */
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%);
}
#dotul {
text-align: center;
}
#dotul li {
margin: 0 8px;
border-radius: 50%;
border: 2px solid white;
background-color: white;
font-size: 12px;
width: 15px;
height: 15px;
line-height: 15px;
display: inline-block;
cursor: pointer;
}
#dotul .selected {
background-color: tan;
}
</style>
</head>
<body>
<div class="box" id="screen">
<ul id="ul">
<li><img src="./img/1.jpg" alt="" /></li>
<li><img src="./img/2.jpg" alt="" /></li>
<li><img src="./img/3.jpg" alt="" /></li>
<li><img src="./img/4.jpg" alt="" /></li>
<li><img src="./img/5.jpg" alt="" /></li>
</ul>
<div id="left"></div>
<div id="right"></div>
<div class="dot">
<ul id="dotul">
<!-- <li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li> -->
</ul>
</div>
</div>
<script>
var box = document.querySelector('#screen');
//获取left right按钮
var left = document.querySelector('#left')
var right = document.querySelector('#right')
//设置ul的宽度
var ul = document.querySelector("#ul")
ul.style.width = ul.children[0].offsetWidth * ul.children.length + 'px';
// console.log(ul.style.width);
//每个li 的 数组
var lis = ul.children;
// console.log(lis);
// console.log(ul.childNodes);
//ul 圆点序列
var dot_ul = document.getElementById('dotul');
var timer;
var cur_left;
//节流阀
var flag = true;
//用来记录当前显示的是第几张图 刚开始设置默认为0第一张 按从0开始
var cur_img_index = 0;
box.addEventListener('mouseenter', function() {
clearInterval(in_timer)
show_btn()
});
box.addEventListener('mouseleave', function() {
clearInterval(in_timer)
in_timer = setInterval(swiper, 2500);
hide_btn()
})
// 左右两边按钮 的 显示 隐藏
function hide_btn() {
left.style.display = 'none';
right.style.display = 'none';
}
function show_btn() {
left.style.display = 'block';
right.style.display = 'block';
}
// for (let h = 0; h < lis.length; h++) {
// //鼠标进入轮播区域时 显示两边按钮
// lis[h].addEventListener('mouseenter', show_btn);
// //鼠标离开轮播区域时 隐藏两边按钮
// lis[h].addEventListener('mouseleave', hide_btn);
// }
//当鼠标移入左或右按钮时 给它设置的样式
function left_right_btn() {
this.style.display = 'block';
this.style.opacity = 0.7
}
left.addEventListener('mouseenter', left_right_btn);
right.addEventListener('mouseenter', left_right_btn);
left.addEventListener('mouseleave', function() {
this.style.opacity = 1;
});
right.addEventListener('mouseleave', function() {
this.style.opacity = 1;
});
//给 被选中的小圆点 设置 类名
function set_className(index) {
for (let o = 0; o < lis.length; o++) {
dot_ul.children[o].className = '';
}
dot_ul.children[index].className = 'selected';
}
//无缝轮播图
function swiper() {
if (flag) {
cur_left = 0;
timer = setInterval(function() {
cur_left -= 50;
ul.style.left = cur_left + "px";
// console.log(ul.offsetLeft);
if (ul.offsetLeft <= '-' + ul.children[0].offsetWidth) {
cur_img_index += 1;
if (cur_img_index > 4) {
cur_img_index = 0;
}
set_className(cur_img_index);
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
clearInterval(timer);
ul.appendChild(ul.children[0]);
ul.style.left = 0 + 'px';
flag = true;
}
}, 50);
flag = false; //***节流阀 为了防止 在未完成上一张图片轮播 就接着要轮播下一张图片
}
};
//点击左边按钮时 执行的操作
function left_swiper() {
if (flag) {
clearInterval(in_timer); //先把 实现自动轮播的定时器 清除掉 防止发生冲突
ul.style.left = "-" + ul.children[0].offsetWidth + "px"; //先把整个ul 左偏移一个li的宽度 -500px
cur_left = ul.offsetLeft;
ul.insertBefore(ul.lastElementChild, ul.children[0]); //然后通过insertBefore方法,把最后一个li节点,插入到当前显示图片(也就是第一个)的li节点前面
//然后在通过定时器的方式,对ul的left属性值按一定的值(这里我设置的是50)进行累加直到left值大于等于0时,在清除这个定时器,再从新设置ul的left属性值为0(因为累加后可能会有多出来的)
//当实现左按钮的操作后 在调用in_timer = setInterval(swiper, 2500);继续来实现自动的循环轮播
timer = setInterval(function() {
cur_left += 50;
ul.style.left = cur_left + 'px'
if (ul.offsetLeft >= 0) {
cur_img_index -= 1;
if (cur_img_index < 0) {
cur_img_index = 4;
}
set_className(cur_img_index);
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
clearInterval(timer);
ul.style.left = 0 + 'px';
flag = true;
in_timer = setInterval(swiper, 2500);
}
}, 50)
flag = false;
}
}
left.addEventListener('click', left_swiper);
function right_swiper() {
if (flag) {
clearInterval(in_timer); //先清除
cur_left = 0;
timer = setInterval(function() {
cur_left -= 50;
ul.style.left = cur_left + "px";
// console.log(ul.offsetLeft);
if (ul.offsetLeft <= '-' + ul.children[0].offsetWidth) {
cur_img_index += 1;
if (cur_img_index > 4) {
cur_img_index = 0;
}
set_className(cur_img_index);
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
clearInterval(timer);
ul.appendChild(ul.children[0]);
ul.style.left = 0 + 'px';
flag = true;
in_timer = setInterval(swiper, 2500);
}
}, 50);
flag = false;
}
};
right.addEventListener('click', right_swiper);
//1 创建 对应 图片 的小圆点 并给它们设置点击事件
//2(点击小圆点后,轮播图 展示 点击的小圆点对应的图片 )
for (let i = 0; i < lis.length; i++) {
var dot_li = document.createElement('li');
dot_li.innerHTML = i + 1;
dot_ul.appendChild(dot_li);
dot_li.addEventListener('click', function() {
if (flag) {
// 当前显示图片下标 大于 点击的小图点的下标 时
if (cur_img_index > i) {
clearInterval(in_timer);
//当前显示的图片 到 点击的图片的距离
var cur_to_target = cur_img_index - i;
ul.style.left = '-' + ul.children[0].offsetWidth * cur_to_target + 'px';
cur_left = ul.offsetLeft;
for (let j = 0; j < cur_to_target; j++) {
//从后边 依次把图片放回 当前显示的图片的前边
ul.insertBefore(ul.lastElementChild, ul.children[0])
}
timer = setInterval(function() {
cur_left += 50 * cur_to_target;
ul.style.left = cur_left + 'px'
if (ul.offsetLeft >= 0) {
clearInterval(timer);
cur_img_index = i;
set_className(cur_img_index);
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
ul.style.left = 0 + 'px';
flag = true;
in_timer = setInterval(swiper, 2500);
}
}, 50)
flag = false;
} else if (i > cur_img_index) { // 当前显示图片下标 小于 点击的小图点的下标 时
clearInterval(in_timer);
//当前显示的图片 到 点击的图片的距离
var cur_to_target = i - cur_img_index;
cur_left = 0;
timer = setInterval(function() {
cur_left -= (50 * cur_to_target);
ul.style.left = cur_left + 'px'
// console.log(ul.offsetLeft);
if (ul.offsetLeft <= '-' + (ul.children[0].offsetWidth * cur_to_target)) {
for (let k = 0; k < cur_to_target; k++) {
//**
ul.appendChild(ul.children[0]);
}
clearInterval(timer);
cur_img_index = i;
set_className(cur_img_index);
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
ul.style.left = 0 + 'px';
flag = true;
in_timer = setInterval(swiper, 2500);
}
}, 50)
flag = false;
}
}
})
}
//刚开始 给第一个小圆点 设置 类名 ----加上背景颜色
//这一段代码必须放到 创建小圆点 后 要不然会提示错误Uncaught TypeError: Cannot set property 'className' of undefined
//因为 你要是放上面 就相当于 小圆点没创建好 你就给它 设置类名 它肯定会报错的 所以放到上面循环创建对应小圆点后 就可以了
dot_ul.children[cur_img_index].className = 'selected';
// 利用定时器 来实现 无缝循环轮播
console.log('当前显示的图片下标索引(从0开始):' + cur_img_index);
var in_timer = setInterval(swiper, 2500);
</script>
</body>
</html>
git:https://gitee.com/otnr-006/js-seamless-rotation-charthttps://gitee.com/otnr-006/js-seamless-rotation-charthttps://gitee.com/otnr-006/js-seamless-rotation-chart
如果还可以优化的更好或者是存在什么问题,欢迎各位大佬补充啊!!!
ppp
|