网页轮播图
网页轮播图案例
轮播图也称为焦点图,是网页中比较常见的网页特效。
结构搭建
因为html和css阶段看的尚硅谷课程,所以采用课程中的小米商城项目,在项目搭建的过程中遇到一个经典问题
初期项目结构
最初的效果:
HTML代码:
<div class="banner-wrap">
<div class="banner w">
<ul class="img-list clearfix">
<li><a href="#"><img src="./img/banner2.png" alt=""></a></li>
<li><a href="#"><img src="./img/banner3.png" alt=""></a></li>
<li><a href="#"><img src="./img/banner1.png" alt=""></a></li>
</ul>
<!-- 设置三个导航点容器 -->
<div class="pointer">
<a class="active" href="#"></a>
<a href="#"></a>
<a href="#"></a>
</div>
<!-- 设置两个小箭头 -->
<div class="prev-next">
<a class="prev" href="#"></a>
<a class="next" href="#"></a>
</div>
</div>
</div>
最初的css代码:
.banner{
position: relative;
height: 460px;
}
.banner .img-list{
/* 因为有三张图片,所有设置吧ul宽度设置为banner的3倍 */
width: 300%;
}
/* 让banner中的li图片横向排列 */
.banner .img-list li{
float: left;
}
/* 设置轮播图 */
.banner img{
width: 100%;
vertical-align: top;
}
存在的问题并解决
- 在初期的项目中,因为没有学习JS,网页中banner的实现方法是为装有img图片的li容器设置了绝对定位,把所有的图片都覆盖住了;
- 当按需要给li的父级(img-list)设置为400%,把li的绝对定位清除并设置为向左浮动时:网页中没有出现预期的li正常横向排列,而是出现如下图所示
:
问题1:img和li宽度均变为图片本身的宽度,而非最初的banner宽度
问题分析::因为img中的wdith设置为100%,宽度由banner继承而来,而当li设置向左浮动时,img无处继承,li也被img撑开,所以宽度均变为图片本身的宽度
解决方法:为img设置宽度
,此处设置为1226px;
问题2:修改后li的位置仍为发生改变
问题分析:为元素开启了向左浮动且前边没有其他浮动元素,所以应该向左浮动且不会从父元素中移出;没有发生改变的原因是因为li浮动开启BFC时,父元素img-list高度塌陷
而绝对定位也移出,所以位置异常
解决方法:为img-list添加clearfix
类
(链接:高度塌陷和外边距重叠原理及解决方案)
html改动处代码:
<ul class="img-list clearfix">
<li><a href="#"><img src="./img/banner2.png" alt=""></a></li>
<li><a href="#"><img src="./img/banner3.png" alt=""></a></li>
<li><a href="#"><img src="./img/banner1.png" alt=""></a></li>
</ul>
css修改后代码:
.banner .img-list{
width: 400%;
}
/* 让banner中的li图片横向排列 */
.banner .img-list li{
float: left;
}
/* 设置轮播图 */
.banner img{
width: 1226px;
vertical-align: top;
}
修改后达到预期效果
完整结构和项目准备
- 在index.html中引入animate.js和index.js,因为后续index.js中会用到animate所以,
animate.js要放在index.js上边引入
- 因为js是写在外部的,所以在JS中要
添加load事件
,页面加载完毕再调用js - 动画函数js文件链接:anlimate.js
需求分析
轮播图也称为焦点图,是网页中比较常见的网页特效。
功能需求:
- 鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
- 点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理。
- 图片播放的同时,下面小圆圈模块跟随一起变化。
- 点击小圆圈,可以播放相应图片。
- 鼠标不经过轮播图,轮播图也会自动播放图片
- 鼠标经过,轮播图模块, 自动播放停止。
功能实现
以下的所有JS代码中若有不知道的class名,可返回上方查看html结构
1. 鼠标经过显示左右隐藏按钮
- 在css中将左右按钮的display设置为none来隐藏按钮
- 让鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。
JS代码:
window.addEventListener('load', function(){
// 获取元素
var prev = document.querySelector('.prev');
var next = document.querySelector('.next');
var banner = document.querySelector('.banner');
var bannerWidth = banner.offsetWidth;
// 鼠标经过banner显示隐藏左右按钮
banner.addEventListener('mouseenter', function(){
prev.style.display = 'block';
next.style.display = 'block';
})
banner.addEventListener('mouseleave', function(){
prev.style.display = 'none';
next.style.display = 'none';
})
2. 动态生成小圆圈
核心思路:小圆点的个数要与banner图片张数一致:有几张图 就生成几个小圆圈
-
首先获取 ul中包裹图片的li数量:有多少张图,就有多少li
-
以li的数量循环动态创建小圆圈: createElement(‘a’)
-
将生成的a(小圆圈)添加到 pointer中 :pointer.appendChild(a);
-
为第一个小圆圈需要添加 current 类
var ul = banner.querySelector('ul');
var pointer = document.querySelector('.pointer');
for(var i =0; i<ul.children.length; i++){
pointer.appendChild(a);
}
// 把pointer里面的第一个a设置类名为current
pointer.children[0].className = 'current';
3. 小圆圈排他思想
- 点击当前小圆圈,就添加 current 类(current类中是选中的独特样式)
- 其余的小圆圈就移除这个 current 类
- 上方生成小圆圈的同时,就可以直接绑定这个点击事件了。
//生成每一个a小圆圈同时添加点击事件
a.addEventListener('click', function(){
// (排他思想使小圆圈唯一current类名)
// 清除每一个小圆圈的current类名
for(var i = 0; i<ul.children.length-1; i++){//此处减一的原因要深究:异步
pointer.children[i].className = '';
}
// 只留下点击的小圆圈 current样式
this.className = 'current';
4.点击小圆圈滚动图片
- 此时用到准备时在index.js上方引入的animate.js
- 使用动画函数的前提,该元素必须有定位,此处是ul(即img-list)
- 滚动图片的核心算法:点击某个小圆圈,就让图片滚动小圆圈的索引号乘以图片的宽度做为 ul 移动距离
- 此时需要知道小圆圈的索引号,我们可以在生成小圆圈的时候,给它设置一个自定义属性,点击的时候获取这个自定 义属性即可
css代码:
.banner .img-list{
width: 500%;
position: absolute;
top: 0;
left: 0;
}
JS代码:
// 3.动态生成小圆圈,有几张图 就生成几个小圆圈
var ul = banner.querySelector('ul');
var pointer = document.querySelector('.pointer');
for(var i =0; i<ul.children.length; i++){
// 创建小圆圈
var a = document.createElement('a');
// 记录当前小圆圈的索引号 通过自定义属性来做
a.setAttribute('index',i);
// 添加小圆圈
pointer.appendChild(a);
//
// 4.生成每一个a小圆圈同时添加点击事件
a.addEventListener('click', function(){
// (排他思想使小圆圈唯一current类名)
// 清除每一个小圆圈的current类名
for(var i = 0; i<ul.children.length-1; i++){//此处减一的原因要深究:异步
pointer.children[i].className = '';
}
// 只留下点击的小圆圈 current样式
this.className = 'current';
// 5.点击小圆圈,移动图片,移动的是ul(img-list)
// ul的移动距离就是小圆圈的索引号 乘以 图片的宽度(负值)
// 当我们点击了哪个小圆圈a,就获得它的索引号
var index = this.getAttribute('index');
animate(ul, - index*bannerWidth);
5.按钮功能
点击一次,滚动一张图片;点击按钮时,小圆圈也随着变化
- 声明一个变量 num,点击一次,自增 1,让这个变量乘以图片宽度,就是ul 的滚动距离。
- 克隆一张图片
- 声明一个circle变量控制小圆圈
- 图片无缝滚动原理:把 ul 第一个 li 复制一份,放到 ul 的最后面,当图片滚动到克隆的最后一张图片时,让 ul 快速的、不做动画的跳到最左侧:left 为0;同时 num 赋值为0,可以从新开始滚动图片了
- 左侧按钮同理,只需修改一些细节
// 6.克隆第一张图片(li)放到ul最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7.点击右侧按钮,图片滚动一张
var num = 0;
// 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*bannerWidth)
// 8.点击右侧按钮,小圆圈也跟随变化,可以在声明一个变量circle
circle++;
// 如果circle =3,就说明走到最后一张图片了(克隆的那张)我们就复原
if(circle == pointer.children.length){
circle =0;
}
// 调用函数
circleChange();
}
})
// 9.左侧按钮设置
prev.addEventListener('click', function(){
//无缝滚动原理:复制第一张图片放到最后;判断如果走到最后一张图,则ul的left复原为0
if(num == 0){
num = ul.children.length-1;
ul.style.left = - num*bannerWidth + 'px';
}
num--;
animate(ul, - num*bannerWidth)
// 点击左侧按钮,小圆圈也跟随变化,可以声明一个变量circle
circle--;
// 如果circle小于0,说明小圆圈选中状态跳到最后一张上
if(circle < 0){
circle =pointer.children.length-1;
}
circleChange();
}
})
//将小圆圈排他封装成函数
function circleChange(){
// 先清除其他小圆圈的样式
for(var i = 0; i<pointer.children.length; i++){
pointer.children[i].className = '';
}
// 留下当前小圆圈的current
pointer.children[circle].className = 'current';
}
6.添加定时器并修改bug
bug:点击左右侧按钮和小圆圈均可实现对应的功能;但若先点击小圆圈(如点最后一个),再点击右侧按钮,此时会发现跳转到了第二张图片而不是第一张,这是因为按钮滚动图片时是用num值控制,而点击小圆圈为对其造成影响
;同理小圆点也有异常情况
解决方法:在点击小圆圈时,把索引号赋值给num和circle
// 5.点击小圆圈,移动图片,移动的是ul(img-list)
// ul的移动距离就是小圆圈的索引号 乘以 图片的宽度(负值)
// 当我们点击了哪个小圆圈a,就获得它的索引号
var index = this.getAttribute('index');
animate(ul, - index*bannerWidth);
// 点击小圆圈后,把小圆圈索引号赋值给num:右侧按钮通过num控制图片移动
num = index;//不加此代码:通过小圆点跳到最后一张图,点击右侧按钮 发现跳到第二张图,因为点击时num没发生改变,而箭头通过num控制
// 点击小圆圈后,把小圆圈索引号赋值给circle:circle控制小圆圈的样式
circle = index;
添加定时器
- 添加一个定时器
- 自动播放轮播图,实际就类似于点击了右侧按钮
- 此时我们使用手动调用右侧按钮点击事件 next.click()
- 鼠标经过 banner 就停止定时器
- 鼠标离开 banner 就开启定时器
// 10.自动播放轮播图
var timer = setInterval(function(){
// 手动调用事件
next.click();
},3000)
7. 添加节流阀
- 功能:防止轮播图按钮连续点击造成播放过快。
- 原理:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。
开始设置一个变量:var flag = true; if(flag) {flag = false; do something}:关闭水龙头
利用回调函数动画执行完毕:flag = true 打开水龙头
网页轮播图JS文件
- 实现效果:
- JS代码:
window.addEventListener('load', function(){
// 1.获取元素
var prev = document.querySelector('.prev');
var next = document.querySelector('.next');
var banner = document.querySelector('.banner');
var bannerWidth = banner.offsetWidth;
// 2.鼠标经过banner显示隐藏左右按钮
banner.addEventListener('mouseenter', function(){
prev.style.display = 'block';
next.style.display = 'block';
clearInterval(timer);
timer = null;//清除定时器变量
})
banner.addEventListener('mouseleave', function(){
prev.style.display = 'none';
next.style.display = 'none';
timer = setInterval(function(){
// 手动调用事件
next.click();
},3000)
})
// 3.动态生成小圆圈,有几张图 就生成几个小圆圈
var ul = banner.querySelector('ul');
var pointer = document.querySelector('.pointer');
for(var i =0; i<ul.children.length; i++){
// 创建小圆圈
var a = document.createElement('a');
// 记录当前小圆圈的索引号 通过自定义属性来做
a.setAttribute('index',i);
// 添加小圆圈
pointer.appendChild(a);
//
// 4.生成每一个a小圆圈同时添加点击事件
a.addEventListener('click', function(){
// (排他思想使小圆圈唯一current类名)
// 清除每一个小圆圈的current类名
for(var i = 0; i<ul.children.length-1; i++){//此处减一的原因要深究:异步
pointer.children[i].className = '';
}
// 只留下点击的小圆圈 current样式
this.className = 'current';
// 5.点击小圆圈,移动图片,移动的是ul(img-list)
// ul的移动距离就是小圆圈的索引号 乘以 图片的宽度(负值)
// 当我们点击了哪个小圆圈a,就获得它的索引号
var index = this.getAttribute('index');
animate(ul, - index*bannerWidth);
// 点击小圆圈后,把小圆圈索引号赋值给num:右侧按钮通过num控制图片移动
num = index;//不加此代码:通过小圆点跳到最后一张图,点击右侧按钮 发现跳到第二张图,因为点击时num没发生改变,而箭头通过num控制
// 点击小圆圈后,把小圆圈索引号赋值给circle:circle控制小圆圈的样式
circle = index;
})
}
// 把pointer里面的第一个a设置类名为current
pointer.children[0].className = 'current';
// 6.克隆第一张图片(li)放到ul最后面
var first = ul.children[0].cloneNode(true);
ul.appendChild(first);
// 7.点击右侧按钮,图片滚动一张
var num = 0;
// circle控制小圆圈的播放
var circle = 0;
// 节流阀
var flag = true;
next.addEventListener('click', function(){
if (flag){
flag =false;//关闭节流阀
//无缝滚动原理:复制第一张图片放到最后;判断如果走到最后一张图,则ul的left复原为0
if(num == ul.children.length -1){
ul.style.left = 0;
num = 0;
}
num++;
animate(ul, - num*bannerWidth, function(){
flag = true;//打开节流阀
});
// 8.点击右侧按钮,小圆圈也跟随变化,可以在声明一个变量circle
circle++;
// 如果circle =3,就说明走到最后一张图片了(克隆的那张)我们就复原
// if(circle == pointer.children.length){
// circle =0;
// }
// 优化:
circle = circle == pointer.children.length? 0 :circle;
// 调用函数
circleChange();
}
})
// 9.左侧按钮设置
prev.addEventListener('click', function(){
if(flag){
flag = false;
//无缝滚动原理:复制第一张图片放到最后;判断如果走到最后一张图,则ul的left复原为0
if(num == 0){
num = ul.children.length-1;
ul.style.left = - num*bannerWidth + 'px';
}
num--;
animate(ul, - num*bannerWidth, function(){
flag = true;
});
// 点击左侧按钮,小圆圈也跟随变化,可以声明一个变量circle
circle--;
// 如果circle小于0,说明小圆圈选中状态跳到最后一张上
// if(circle < 0){
// circle =pointer.children.length-1;
// }
circle = circle<0? pointer.children.length-1 : circle;
circleChange();
}
})
function circleChange(){
// 先清除其他小圆圈的样式
for(var i = 0; i<pointer.children.length; i++){
pointer.children[i].className = '';
}
// 留下当前小圆圈的current
pointer.children[circle].className = 'current';
}
// 10.自动播放轮播图
var timer = setInterval(function(){
// 手动调用事件
next.click();
},3000)
})