轮播图也称为焦点图,是网页中比较常见的网页特效。
功能需求:
案例分析:
① 因为 JS 较多,先单独新建 JS 文件夹,再新建 JS 文件,引入页面中。
② 此时需要添加 load 事件。
③ 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
④ 显示隐藏 display。
// 1.获取元素
var prev = document.querySelector('.prev');
var next = document.querySelector('.next');
var focus = document.querySelector('.focus');
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function () {
prev.style.display = 'block';
next.style.display = 'block';
})
focus.addEventListener('mouseleave', function () {
prev.style.display = 'none';
next.style.display = 'none';
})
考虑到小圆圈是对应图片数量变化的,每增加一张图片又手动改代码会很麻烦,下面分析一下如何动态生成小圆圈。
动态生成小圆圈:
① 核心思路:小圆圈的个数要跟图片张数一致;
② 所以首先先得到 ul 里面图片的张数(图片放入 li 里面,所以就是 li 的个数);
③ 利用循环动态生成小圆圈(这个小圆圈要放入 ol 里面);
④ 创建节点 createElement(‘li’) ;
⑤ 插入节点 ol. appendChild(li) ;
⑥ 第一个小圆圈需要添加 current 类 。
点击小圆圈,发现点到哪个小圆圈就会变色,其他小圆圈不变色,此处联想到排他思想,因此,
小圆圈的排他思想:
① 点击当前小圆圈,就添加current类 ;
② 其余的小圆圈就移除这个current类 ;
③ 注意: 我们在刚才生成小圆圈的同时,就可以直接绑定这个点击事件了。
// 3.动态生成小圆圈 有几张图片,就生成几张小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小 li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小 li 插入到 ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function () {
// 干掉所有人 把所有的小 li 清除 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 当前的小 li 设置 current 类名
this.className = 'current';
接下来,就是点击小圆圈实现滚动图片的效果,此时用到 animate 动画函数:
① 引入 animate 动画函数的 js文件(注意,因为 index.js 依赖 animate.js 所以,animate.js 要写到 index.js 上面)
② 使用动画函数的前提,该元素必须有定位;
③ 注意是 ul 移动而不是小 li ;
④ 滚动图片的核心算法: 点击某个小圆圈 , 就让图片滚动 小圆圈的索引号乘以图片的宽度做为ul 移动距离;
⑤ 此时需要知道小圆圈的索引号, 我们可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定 义属性即可。
// 留下我自己 当前的小 li 设置 current 类名
this.className = 'current';
// 5. 点击小圆圈,移动图片,当然移动的是 ul
// ul 的移动距离 = 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小 li 就拿到当前小 li 的索引号
var index = this.getAttribute('index');
animate(ul, -index * focusWidth);
同理,接下来要实现点击左右按钮也能滚动图片的效果,点击右侧按钮一次,就让图片向左滚动一张,方法如下:
① 声明一个变量 num, 点击一次,自增1, 让这个变量乘以图片宽度,就是 ul 的滚动距离 ;
② 图片无缝滚动原理 ;
③ 把 ul 第一个 li 复制一份,放到 ul 的最后面 ;
④ 当图片滚动到克隆的最后一张图片时, 让 ul 快速的、不做动画的跳到最左侧: left 为 0 ;
⑤ 同时 num 赋值为 0,可以从新开始滚动图片了 。
但要是手动在HTML和CSS复制图片(改结构)会显得麻烦,设想通过修改JS实现自动克隆最后一张图片的效果,于是:
① 克隆 ul 第一个 li cloneNode() 括号里加 true 深克隆 复制里面的子节点 ,加 false 就是浅克隆,不复制节点 ;
② 添加到 ul 最后面 appendChild ;
接下来就是实现小圆圈跟随按钮一起变化:
点击按钮,小圆圈跟随变化
① 最简单的做法是再声明一个变量 circle,每次点击自增1,注意,左侧按钮也需要这个变量,因此要声明全局变量;
② 但是图片有7张(最后一张是复制的),小圆圈只有6个少一个,必须加一个判断条件;
③ 如果 circle == 6 就 重新复原为 0 。
考虑到图片数量是动态变化(如果想增加或减少图片),因此 判断 circle == ol. children.length 即可(即判断是否等于小圆圈的数量)
// 把 ol 里面的第一个小 li 设置类名为 current
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 点击右侧按钮,图片滚动一张
var num = 0; // num 是全局变量
// 声明一个变量 circle 控制小圆圈的播放
var circle = 0;
// 右侧按钮
next.addEventListener('click', function () {
// 如果走到了最后复制的一张图片,此时 ul 要快速复原 left 改为 0
if (num == ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, -num * focusWidth);
// 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
if (circle == ol.children.length) {
circle = 0;
}
// 先清除其余小圆圈的 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
})
做到这里,基本实现了点击按钮图片和小圆圈同步跟随变化了,但存在一个小 BUG,就是点击小圆圈后到某张图片,点击按钮应该会跳到下一张图片,但结果是会跳到这张图片的上一张图片,这个问题是因为点击按钮的动画是通过 num 变量控制的,与 小圆圈播放图片是 li 点击事件 里边index 的索引号控制的,因此要让索引号等于num 即可,为了让小圆圈、按钮、图片三者实现一个同步的变化,就必须让三者值相等,即获取了小圆圈的索引号后直接让三者相等(此处 num 和circle 是全局变量,在 li 点击事件在点击事件外已声明)
var index = this.getAttribute('index');
// 当我们点击了某个小 li 就要把这个 li 的索引号给 num 和 circle
num = circle = index;
左侧按钮
点击左侧按钮,由于 num 赋值为0,则设置前提条件,跳转到复制第一张图的最后一张图片,其索引号是 ul.children.length - 1 ,则 ul 移动的距离就可以算出来:
prev.addEventListener('click', function () {
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth);
})
同理,因为一开始赋值 circle 为 0,那 circle-- 就是负值,利用这个判断条件,当 circle < 0 时,就要将小圆圈改为最后一个小圆圈,即 ol. children.length - 1 , 而小圆圈的样式变化,即类名变化,因为和右侧按钮的代码是一样的,可以将其封装在一个函数内,进而优化代码
circle--;
// 如果circle < 0 说明第一张图片 则小圆圈要改为第6个小圆圈
if (circle < 0) {
circle = ol.children.length - 1;
}
// 调用函数
circleChange();
// 优化代码,封装函数
function circleChange() {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
}
到此为止,基本效果已经实现。
接下来就是实现动画自动播放的效果:
自动播放可以联想到定时器,则
① 添加一个定时器 ;
② 自动播放轮播图,实际就类似于点击了右侧按钮 ;
③ 此时我们使用手动调用右侧按钮点击事件 next.click() ;
④ 鼠标经过focus 就停止定时器 ;
⑤ 鼠标离开focus 就开启定时器 。
于是,定时器的函数可以这样写
// 10. 自动播放轮播图
var timer = setInterval(function () {
// 手动调用右侧按钮点击事件
next.click();
}, 2000)
鼠标经过停止定时器(清除定时器)和离开(把定时器的代码直接拉上去,不需要定义)的效果要回到之前写的代码里去修改,增加几行代码,如下:
// 2. 鼠标经过focus 就显示隐藏左右按钮以及停止和开启定时器
focus.addEventListener('mouseenter', function () {
prev.style.display = 'block';
next.style.display = 'block';
clearInterval(timer);
timer = null;// 清除定时器变量
})
focus.addEventListener('mouseleave', function () {
prev.style.display = 'none';
next.style.display = 'none';
timer = setInterval(function () {
// 手动调用右侧按钮点击事件
next.click();
}, 2000)
})
最后,轮播图还涉及到一个问题,就是点击按钮播放图片会过快,由此引入一个概念——节流阀
节流阀是为了防止轮播图按钮连续点击造成播放过快。
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量
var flag = true;
If (flag)
{flag = false; do something} 关闭水龙头
利用回调函数 动画执行完毕, flag = true 打开水龙头
因此,在点击事件里,先执行了一遍代码,但由于 flag = false,所以下面就不会去执行代码了,而在 animate 动画函数里通过回调函数重新开启 flag = true 即可
if (flag) {
flag = false;//关闭节流阀
if (num == ul.children.length - 1) {
ul.style.left = 0 + 'px';
num = 0;
}
num++;
animate(ul, -num * focusWidth, function () {
flag = true;//打开节流阀
});
// 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
if (circle == ol.children.length) {
circle = 0;
}
//调用函数
circleChange();
}
由此,网页上的轮播图效果基本完善,下面看看效果吧
有没有抖音里的图文视频的感觉呀,那个也是轮播图哦。
PS:完整的JavaScript代码如下:
window.addEventListener('load', function () {
// 1. 获取元素
var prev = document.querySelector('.prev');
var next = document.querySelector('.next');
var focus = document.querySelector('.focus');
var focusWidth = focus.offsetWidth;// 获取图片的宽度
// 2. 鼠标经过focus 就显示隐藏左右按钮
focus.addEventListener('mouseenter', function () {
prev.style.display = 'block';
next.style.display = 'block';
clearInterval(timer);
timer = null;// 清除定时器变量
})
focus.addEventListener('mouseleave', function () {
prev.style.display = 'none';
next.style.display = 'none';
timer = setInterval(function () {
// 手动调用右侧按钮点击事件
next.click();
}, 2000)
})
// 3.动态生成小圆圈 有几张图片,就生成几张小圆圈
var ul = focus.querySelector('ul');
var ol = focus.querySelector('.circle');
// console.log(ul.children.length);
for (var i = 0; i < ul.children.length; i++) {
// 创建一个小 li
var li = document.createElement('li');
// 记录当前小圆圈的索引号 通过自定义属性来做
li.setAttribute('index', i);
// 把小 li 插入到 ol 里面
ol.appendChild(li);
// 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
li.addEventListener('click', function () {
// 干掉所有人 把所有的小 li 清除 current 类名
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下我自己 当前的小 li 设置 current 类名
this.className = 'current';
// 5. 点击小圆圈,移动图片,当然移动的是 ul
// ul 的移动距离 = 小圆圈的索引号 乘以 图片的宽度 注意是负值
// 当我们点击了某个小 li 就拿到当前小 li 的索引号
var index = this.getAttribute('index');
// 当我们点击了某个小 li 就要把这个 li 的索引号给 num 和 circle
num = circle = index;
// console.log(focusWidth);
animate(ul, -index * focusWidth);
})
}
// 把 ol 里面的第一个小 li 设置类名为 current
ol.children[0].className = 'current';
// 6. 克隆第一张图片(li)放到最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7. 点击右侧按钮,图片滚动一张
var num = 0;
// 声明一个变量 circle 控制小圆圈的播放
var circle = 0;
// flag 节流阀
var flag = true;
next.addEventListener('click', function () {
// 如果走到了最后复制的一张图片,此时 ul 要快速复原 left 改为 0
if (flag) {
flag = false;//关闭节流阀
if (num == ul.children.length - 1) {
ul.style.left = 0 + 'px';
num = 0;
}
num++;
animate(ul, -num * focusWidth, function () {
flag = true;//打开节流阀
});
// 8. 点击右侧按钮,小圆圈跟随一起变化,可以再声明一个变量控制小圆圈的播放
circle++;
// 如果circle == 6 说明走到最后克隆的这张图片 我们就复原为0
if (circle == ol.children.length) {
circle = 0;
}
//调用函数
circleChange();
}
})
// 9. 左侧按钮做法
prev.addEventListener('click', function () {
if (flag) {
if (num == 0) {
num = ul.children.length - 1;
ul.style.left = -num * focusWidth + 'px';
}
num--;
animate(ul, -num * focusWidth, function () {
flag = true;
});
circle--;
// 如果circle < 0 说明第一张图片 则小圆圈要改为第6个小圆圈
if (circle < 0) {
circle = ol.children.length - 1;
}
// 调用函数
circleChange();
}
})
// 优化代码,封装函数
function circleChange() {
for (var i = 0; i < ol.children.length; i++) {
ol.children[i].className = '';
}
// 留下当前的小圆圈的current类名
ol.children[circle].className = 'current';
}
// 10. 自动播放轮播图
var timer = setInterval(function () {
// 手动调用右侧按钮点击事件
next.click();
}, 2000)
})
★★★ 欢迎批评指正!