全文思维导图如下:
轮播图
一.实现效果
1.最终视觉图:
2.效果功能:
- 鼠标不进入时隐藏左右箭头且自动播放
- 可通过圆圈点击切图,或箭头切图
- 可循环,且连续点击会等一张播放完才切图
二.实现原理
1.主要构成部分
- 多张图片【放在ul下的小 li 里面】
- 左右箭头【使用arrow-r,arrow-l 类,里面放小图标】
- 索引圆圈【放在ol下的小li里面,且动态生成】
2.涉及知识点(随附代码)
图片循环播放原理
克隆第一张图片【深克隆,使用cloneNode(ture)方法】,从而最后一张播放完后下一张又会是第一张,同时,当走到了最后一张时,将ul的位置跳到最左;同理,走到第一张时,跳到最右,从而悄无声息地实现 "伪循环"。
缓动动画
- 实现原理
可传参数三个分别是 obj, target, callback ,第一个表示动画作用对象,第二个表示要移动的目标位置,第三个表示移动后的回调函数。
首先是计算移动步长,此处是以目标位置减去当前对象距左边的距离,然后除以10,随着当前对象距左边距离越来越接近目标位置,步长会越来越短,从而实现 缓动效果。
然后每次移动都让当前对象的左距变为 当前左距+步长 即可实现 移动。
- 注意事项
(1)求步长(step)时,注意根据其正负来决定取整时是取大(ceil)还是取小(floor)
(2)在移动过程中,注意对象左距的改变(通过obj.style.left)最后要加单位 'px'
(3)此处封装的动画函数是针对左右移动的,如果要上下移动,只需更改为上距即可
- 具体代码
// 封装缓动动画效果函数
function animate(obj, target, callback) {
clearInterval(obj.timer);
//设置计时器
obj.timer = setInterval(function () {
// 步长值写到定时器的里面
var step = (target - obj.offsetLeft) / 10;
// 对步长进行取整,以防止出现小数问题,
// 并考虑到后退时会产生正负,取整时要考虑是取大还是取小
step = step > 0 ? Math.ceil(step) : Math.floor(step);
//ceil [天花板] 、floor [地板]
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
//回调函数写到计时器结束里面
// if (callback) {
// //如果有函数实参传进来,就调用函数
// callback();
// }
// 上面两行有更高级的写法,即使用或与运算符,如下:
callback && callback();
} else {
obj.style.left = obj.offsetLeft + step + 'px';
}
}, 30)
}
计时器
代码见上animate函数,此处使用的setInterval(function, time),传入参数分别是执行函数和执行间隔时间,该计时器将会每间隔time时间执行一次function函数;
在animate.js中每次都先clearInterval,是为了保证动画被反复执行时(如多次点击箭头)都会先删除上一次生成的计时器,防止计时器的叠加;
且为保证每一个对象有自己独有的计时器,所以自定义了timer属性,将obj.timer = setInterval(){} ,就可以保证多个对象不会共用一个计时器。
圆圈效果
结构:在圆角矩形(ol)中放置圆圈(小 li )
特点:小圆圈是动态添加的,使用js判断图片张数创建节点,然后添加到矩形中
注意:因为考虑到后续图片的移动,所以要在创建节点时就给li添加一个自定义的属性用于索引(自定义属性名用data-开头)
(使用这行代码增加属性用来索引 var index = this.getAttribute('data-index');)
以下是圆角矩形(类名为.circle)的CSS代码实现:
.main .circle {
display: inline-block;
position: absolute;
/*下面三行用来定位和居中*/
left: 50%;
bottom: 10px;
transform: translateX(-50%);
/*下面三行分别用来调色、形状和上下居中*/
background-color: rgba(100, 100, 100, 0.5);
border-radius: 7px;
font-size: 0;
}
以下是内部小圆圈的CSS代码实现【.current类是当前显示图片】
.circle li {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
background: #fff;
margin: 2px;
}
ol .current{
background-color: skyblue;
}
显示图标
此处使用的是阿里图标,点击右边可跳转 iconfont-阿里巴巴矢量图标库,使用方法之一
步骤如下:
①下载并引入font文件夹(解压后内容如下)
②引入到html中,
<link rel="stylesheet" href="../font_icon/iconfont.css">
③直接使用类名引入图标,注意要引入两个!一个是iconfont,一个是icon-icon-xxx【到iconfont.css里面找】
<a href="javascript:;" class="arrow-l iconfont icon-icon-arrow-left"></a>
节流阀(补充)
使用一个flag标志即可,放在回调函数中,变换数值
三.具体代码
1.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>网页轮播图</title>
<link rel="stylesheet" href="../font_icon/iconfont.css">
<script src="animate.js"></script>
<script src="lbt.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
li {
float: left;
list-style: none;
}
a {
/*取消链接的下划线*/
text-decoration: none;
}
.main {
position: relative;
height: 250px;
width: 400px;
/*text-align: center;*/
margin: 160px auto;
/*background-color: skyblue;*/
overflow: hidden;
}
.main ul {
position: absolute;
height: 100%;
width: 4000px;
}
/*
可以让小li浮动,来让图片横排
但是也要调整ul的宽度,否则装不下去
*/
img {
width: 400px;
height: 250px;
}
/*可以实现代码的复用*/
.arrow-r, .arrow-l {
display: none;
position: absolute;
right: 0;
top: 50%;
margin-top: -20px;
width: 24px;
height: 40px;
line-height: 40px;
text-align: center;
color: #fff;
font-size: 18px;
background: rgba(0, 0, 0, .3);
/*因为对ul使用了positon定位,所以存在覆盖优先级问题*/
z-index: 2;
}
.arrow-r:hover{
color: lightcoral;
}
.arrow-l:hover{
color: lightcoral;
}
.arrow-l {
left: 0;
}
.main .circle {
display: inline-block;
position: absolute;
/*下面三行用来定位和居中*/
left: 50%;
bottom: 10px;
transform: translateX(-50%);
/*下面三行分别用来调色、形状和上下居中*/
background-color: rgba(100, 100, 100, 0.5);
border-radius: 7px;
font-size: 0;
}
.circle li {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
background: #fff;
margin: 2px;
}
ol .current{
background-color: skyblue;
}
</style>
</head>
<body>
<div class="main">
<!-- 左侧箭头-->
<!-- 类中应包括iconfont 和 标识类-->
<a href="javascript:;" class="arrow-l iconfont icon-icon-arrow-left"></a>
<!-- 右侧箭头-->
<a href="javascript:;" class="arrow-r iconfont icon-icon-arrow-right"></a>
<!-- 轮播图-->
<ul>
<li>
<a href="#"><img src="../images/1.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="../images/2.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="../images/3.jpg" alt=""></a>
</li>
<li>
<a href="#"><img src="../images/4.jpg" alt=""></a>
</li>
</ul>
<!-- 小圆圈-->
<ol class="circle">
</ol>
</div>
</body>
</html>
2.轮播图js
window.onload = function () {
// 1。获取元素
var arrow_l = document.querySelector('.arrow-l');
var arrow_r = document.querySelector('.arrow-r');
var focus = document.querySelector('.main');
//因为ul可能有很多,所以应该查找focus下的ul
var ul = focus.querySelector('ul');
var ol = focus.querySelector('ol');
var num = 0;//用于作为图片索引的
// var circle = 0;//用于作为圆圈索引的,此处我用num兼容了该作用
// 2.鼠标经过就显示左右箭头并停止自动播放
focus.addEventListener('mouseenter', function () {
arrow_l.style.display = 'block';
arrow_r.style.display = 'block';
clearInterval(timer);
timer = null;
})
//鼠标离开就开启自动播放
focus.addEventListener('mouseleave', function () {
arrow_l.style.display = 'none';
arrow_r.style.display = 'none';
timer = setInterval(function (){
arrow_r.click();
},3000);
})
// 3.根据ul下的小li的个数来生成小圆圈
for (var i = 0; i < ul.children.length; i++) {
var li = document.createElement('li');
// 为每个小li添加属性,设置索引号
li.setAttribute('data-index', i);
// 4.在生成小li时就给它绑定单击事件
li.addEventListener('click', function () {
var index = this.getAttribute('data-index');
num = index;//此处将index的值赋给num,实现箭头点击和小圆圈的同步
var target = -focus.offsetWidth * index;
// console.log(index);
// console.log(target);
// 5.利用排他思想,突出被选中的小圆圈
for (var j = 0; j < ol.children.length; j++) {
//取消其他圆圈之前点击时加上的类名
ol.children[j].className = '';
}
this.className = 'current';
animate(ul, target);
})
//把li插入到ol中
ol.appendChild(li);
}
ol.children[0].className = 'current';
// 6.克隆第一张图片(li)放到ul最后面(放在循环外就不会生成多余的小圆圈)
var first = ul.children[0].cloneNode(true);//true表示深克隆(不仅克隆节点,还克隆内容)
ul.appendChild(first);//将克隆的图片加到ul中
// 7.点击右侧按钮,图片滚动一张
//设置一个变量作为节流阀,用于控制点击箭头不要滚动过快
var flag = true;
arrow_r.addEventListener('click', function () {
if (flag){
flag = false; //关闭节流阀
// 如果走到了最后一张图片,则将ul快速回到第一张
if (num == ul.children.length - 1) {
console.log(num);
ul.style.left = 0;
num = 0;
}
// 此处不能使用else,否则num的值对不上
num++;
animate(ul, -focus.offsetWidth * num,function (){
flag = true; //动画执行完就打开节流阀
});
circlechange(num);
}
})
//8.点击左侧按钮,同上理
arrow_l.addEventListener('click', function () {
if (flag){
flag = false;
// 如果走到了第一张图片,则将ul快速回到最后一张
if (num == 0) {
num = ul.children.length - 1;
//注意!一定要在最后加'px'
ul.style.left = - num * focus.offsetWidth + 'px';
}
// 此处不能使用else,否则num的值对不上
num--;
animate(ul, -focus.offsetWidth * num,function (){
flag = true;
});
circlechange(num);
}
})
//这段改变圆圈的代码是可以复用的
function circlechange(num){
// 清除其他小圆圈
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 将自己此时的小圆圈变色
if (num == ol.children.length) {
// 此时num = 4,表示第五张图片
ol.children[0].className = 'current';
} else {
ol.children[num].className = 'current';
}
}
//9.自动播放功能(当鼠标移动在上面就停止自动播放)
var timer = setInterval(function (){
//arrow_r.click()可以实现自动点击
arrow_r.click();
},3000);
}
3.缓动动画js
// 封装缓动动画效果函数
function animate(obj, target, callback) {
clearInterval(obj.timer);
//设置计时器
obj.timer = setInterval(function () {
// 步长值写到定时器的里面
var step = (target - obj.offsetLeft) / 10;
// 对步长进行取整,以防止出现小数问题,
// 并考虑到后退时会产生正负,取整时要考虑是取大还是取小
step = step > 0 ? Math.ceil(step) : Math.floor(step);
//ceil [天花板] 、floor [地板]
if (obj.offsetLeft == target) {
clearInterval(obj.timer);
//回调函数写到计时器结束里面
// if (callback) {
// //如果有函数实参传进来,就调用函数
// callback();
// }
// 上面两行有更高级的写法,即使用或与运算符,如下:
callback && callback();
} else {
obj.style.left = obj.offsetLeft + step + 'px';
}
}, 30)
}