刚结束了一个月的html+css+JavaScript的学习。学JavaScript一定会做一个轮播图的案例,因为太经典了。
用Gifcam做出来的gif图有点大,CSDN要求上传的图片在5M以内,5M的话就看不到轮播的效果了,也没专门去找压缩软件,所以就不放效果图了,把用到的图片放到末尾了,有需要的把html中图片路径改一下就能用了。提醒一下,用到的动画函数和轮播图的功能实现,都是通过引入js文件的形式实现的,在html里可以看到,两个文件的引用。
功能需求
- 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮
- 点击右侧按钮依次,图片往左播放一张,以此类推,左侧按钮同理。
- 图片播放的同时,下面小圆圈模块跟随一起变化。
- 点击小圆圈,可以播放相应图片。
- 鼠标不经过轮播图,轮播图也会自动播放
- 鼠标经过轮播图模块,自动停止播放。
案例分析
- 按钮的显示隐藏,根据鼠标事件设置display属性即可
- 动态生成下面的小圆圈。小圆圈的数量与图片的数量是一致的。首先可以获得ul里面图片的个数,创建节点,动态生成小圆圈,再插入节点,放到ol里面。
- 点击某个小圆圈,被点击的小圆圈颜色会与其他的不一样,这里利用排他思想就可以实现
- 点击小圆圈移动图片:此时用到下面的动画函数,将js文件引入。在创建小圆圈的同时,加上一自定义属性存储索引号,点击某个小圆圈时,就让图片滚动 小圆圈的索引号乘以图片的宽度 作为ul的移动距离。
- 无缝滚动:克隆ul的第一个li节点,放到ul的最后面。声明一个num变量,点击一次,自增1,让这个变量乘以图片的宽度,就是ul的滚动距离。当图片滚动到克隆的最后一张图片时,让ul快速的,不做动画的跳到最左侧:left:0;
- 自动播放:就像我们点击了下一张的按钮,所以我们添加一个定时器,手动的调用右侧按钮的点击事件
- 节流阀:当我们连续点击下一张或上一张按钮,图片会快速的移动,为了防止播放过快。可以利用回调函数,添加一个变量来控制,上一个动画执行完毕后,再去执行下一个动画,让事件无法连续触发,锁住函数和解锁函数。
动画函数
在上轮播图前,先说一下这个动画函数,轮播图中图片的移动就是用的这个函数。
- 函数名为animate,参数obj为目标对象(必须有定位),target为目标位置,callback为回调函数
- 上来先清除计时器,再添加计时器,确保只能有一个计时器,否则每调用一次函数,就添加一个定时器。
- 将定时器作为对象的属性,不作为变量,避免多个对象调用该函数,在内存中开辟多个空间存储变量,浪费内存资源。同时每个元素都有自己单独的定时器,不会引起歧义。
- step为每次移动的距离,由目标位置减去现在的位置再除以一个数所得,移动的距离越来越小,以此来实现缓动的效果。
- 由于步长的计算会出现小数,所以可能出现最后与目标位置相差一些距离,所以调用Math里的方法对步长进行了处理。
- 回调函数:函数作为一个参数,将这个函数作为参数传递到另一个函数里,当那个函数执行完后,再执行传进去的这个函数,这个过程就叫回调。
function animate(obj, target, callback) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
//计算步长值 注意 步长值应为整数
var step = (target - obj.offsetLeft) / 20;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
//回调函数写到定时器结束里面
//简易写法 利用短路原理
callback && callback();
} else {
obj.style.left = obj.offsetLeft + step + 'px';
}
}, 15)
}
轮播图结构的搭建
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.w {
position: relative;
width: 980px;
height: 450px;
margin: 100px auto;
overflow: hidden;
}
.w ul {
position: absolute;
left: 0;
top: 0;
width: 600%;
height: 100%;
}
.w ul li {
float: left;
width: 980px;
height: 100%;
list-style: none;
}
/* .w ul li a {
display: block;
width: 100%;
height: 100%;
}
*/
.w ul img {
width: 100%;
height: 100%;
}
.w .circle {
position: absolute;
left: 50%;
bottom: 5px;
transform: translateX(-50%);
}
.w .circle li {
list-style: none;
float: left;
width: 10px;
height: 10px;
margin: 3px;
border-radius: 50%;
background-color: #ffffff;
cursor: pointer;
}
.w>a {
display: none;
position: absolute;
top: 50%;
width: 20px;
height: 40px;
background-color: #585757;
font-size: 22px;
line-height: 36px;
text-align: center;
color: #ffffff;
text-decoration: none;
z-index: 1;
}
.w .arrow-1 {
left: 15px;
}
.w .arrow-2 {
right: 15px;
}
</style>
<script src="animate.js"></script>
<!-- lunbo的js文件里要用到animate文件里的方法 所以animate要放到上面 -->
<script src="lunbotu.js"></script>
</head>
<body>
<div class="w">
<a href="javascript:void(0);" class="arrow-1"><</a>
<a href="javascript:void(0);" class="arrow-2">></a>
<ul>
<li>
<a href="javascript:void(0);"><img src="../../../images/Bing.jpg" alt=""></a>
</li>
<li>
<a href="javascript:void(0);"><img src="../../../images/LenticularVideo.jpg" alt=""></a>
</li>
<li>
<a href="javascript:void(0);"><img src="../../../images/LoughriggTarn.jpg" alt=""></a>
</li>
<li>
<a href="javascript:void(0);"><img src="../../../images/SaltonSea.jpg" alt=""></a>
</li>
</ul>
<ol class="circle">
</ol>
</div>
</body>
</html>
轮播图的核心
window.addEventListener('load', function () {
//获取元素
var arrow_1 = document.querySelector('.arrow-1');
var arrow_2 = document.querySelector('.arrow-2');
var w = document.querySelector('.w');
var width = w.offsetWidth;
w.addEventListener('mouseenter', function () {
arrow_1.style.display = 'block';
arrow_2.style.display = 'block';
clearInterval(timer);
//清除计时器变量
timer = null;
})
w.addEventListener('mouseleave', function () {
arrow_1.style.display = 'none';
arrow_2.style.display = 'none';
timer = setInterval(() => {
//手动调用点击事件
arrow_2.click();
}, 2000);
})
var ul = w.querySelector('ul');
var ol = w.querySelector('.circle');
for (var i = 0; i < ul.children.length; i++) {
//创建一个小 li
var li = document.createElement('li');
//给li加一个自定义属性
li.setAttribute('data-index', i);
//我们可以在生成li 的同时 绑定事件 排他思想
li.addEventListener('click', function () {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].style.backgroundColor = '';
}
this.style.backgroundColor = 'darkorange';
//点击小圆圈 移动图片 移动的是ul
//ul 移动的距离 小圆圈的索引号 乘以 图片的宽度 注意移动距离是负值
//当我们点击了某个li 就拿到当前li的索引号
var index = this.getAttribute('data-index');
//当我们点击了某个li 就要把这个li的索引号 给num
num = index;
//当我们点击了某个li 也要把这个li的索引号 给circle
circle = index;
// num = circle = index;
animate(ul, -index * width);
})
//把小li插入到ol里面
ol.appendChild(li);
}
ol.children[0].style.backgroundColor = 'darkorange';
//克隆第一张图片 放到ul最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
//点击右侧按钮,图片滚动一张
var num = 0;
//circle控制小圆圈的播放
var circle = 0;
//右侧按钮
//节流阀
var flag = true;
arrow_2.addEventListener('click', function () {
if (flag) {
flag = false;
//如果走到了最后复制的一张 此时 我们的ul要快速复原
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * width, () => {
flag = true;
//打开节流阀
});
//点击右侧按钮 小圆圈跟随一起变化 可以再声明一个控制小圆圈播放的变量
circle++;
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].style.backgroundColor = '';
}
//留下当前的小圆圈的
ol.children[circle % ol.children.length].style.backgroundColor = 'darkorange';
}
})
//左侧按钮
arrow_1.addEventListener('click', function () {
if (flag) {
flag = false;
//如果走到了最后复制的一张 此时 我们的ul要快速复原
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * width + 'px';
}
num--;
animate(ul, -num * width, () => {
flag = true;
});
//点击右侧按钮 小圆圈跟随一起变化 可以再声明一个控制小圆圈播放的变量
circle--;
circle = circle < 0 ? ol.children.length - 1 : circle;
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].style.backgroundColor = '';
}
//留下当前的小圆圈的
ol.children[circle].style.backgroundColor = 'darkorange';
}
})
//自动播放
var timer = setInterval(() => {
//手动调用点击事件
arrow_2.click();
}, 2000);
})