知识储备
- jQuery 方法
- 根据选择器获取元素:$(selector)
- ajax 方法及配置参数 $.ajax({})
- 绑定事件 on方法
- html() 方法
- css() 方法
- addClass()、removeClass() 方法
- animate() 动画方法 finish() 结束动画方法
- 定时器基础:
- js 动画的原理就是利用定时器操作某个样式值缓慢增加,轮播图操作的是 wrapper 元素的 left 属性。
- 停止动画就是清除定时器,再次开启动画即重新开启定时器;
- 定时器 setTimeout 和 setInterval 的返回值是一个定时器 id;
- clearTimeout(定时器 id)和 clearInterval(定时器 id);
轮播图需求:
- 制作一个轮播图(又称焦点图),自动轮播,并且轮播图下焦点跟随切换;
- 当鼠标移入轮播图时,轮播图停止自动轮播,并且展示手动切换的箭头图标,当鼠标移出轮播图时,继续自动轮播,隐藏手动切换箭头图标;
- 当点击手动控制轮播箭头图标的时候,可以手动切换图片。如果点击左侧箭头,则切换至上一张,如果切换到第一张,再次点击则切换至最后一张,依此类推;如果点击右侧箭头,则切换至下一张,如果已经是最后一张,再次点击则切换至第一张,实现循环播放;
- 点击焦点时,对应索引的图片也要跟随切换;
轮播图原理:
- 有一个外面的盒子 container,container 的宽度只能展示一张图片,其余的溢出隐藏。
- 在 container 里面有一个 wrapper 的盒子,里面装着很多张图片,这些图片在一行排列,而 wrapper 相对于 container 绝对定位,初始值 left = 0,所以默认展示第一张图片(第一张图片索引为 0)。
- 轮播一次其实就是让 wrapper 盒子向左移动一张图片的距离(操作 wrapper 的 left 值),这样第二张(索引为 1)图片就露出来了(此时 wrapper 的 left 的值是 负的 1 张图片的宽度),里面的盒子再向左移动一张,第三张(索引为 2)就露出来了(此时 wrapper 的 left 值是 负的 2 张图片的宽度),依次类推,我们发现如果想让轮播图轮到第几张,就将 wrapper 的 left 值设置成:负的图片宽度 * 索引。
展示图片 |
图片索引 |
wrapper 的 left 值 |
1 |
0 |
0 * 图片宽度 |
2 |
1 |
-1 * 图片宽度 |
3 |
2 |
-2 * 图片宽度 |
4 |
3 |
-3 * 图片宽度 |
- 综上,我们需要有个变量 stepIndex,记录着要轮播的是第几张,然后轮播时就是让 wrapper 的 left 值就是负的 stepIndex 乘以图片宽度,即 left = -1 stepIndex 1000 (1000 是图片宽度)
无缝轮播实现
- 我们首先复制一份第一张图片并且把复制的这个放到末尾,相当于真实图片的最后一张后面还有一个第一张的副本。
- 然后当轮播到最后一张的时候,这个时候展示的是我们第一步复制过来的第一张图片,下一次再轮播应该展示第二张图了。
- 这个时候我们将 wrapper 的 left 设置成 0,然后 left 为 0 时展示的是真正的第一张,下一张要轮播的是第二张所以我们将 stepIndex 的值置为 1(1 就是第二张图的索引),然后再轮播就是第二张图了。
- 又因为浏览器设置 wrapper 的值基本不需要时间,所以给人的感觉就是无缝轮播的。
html代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>珠峰培训-轮播图</title>
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/banner.css">
</head>
<body>
<section class="container" id="container">
<div class="wrapper">
<!--<div class="slide"><img src="img/banner1.jpg" alt=""></div>
<div class="slide">
<img src="img/banner2.jpg" alt="">
</div>
<div class="slide"><img src="img/banner3.jpg" alt=""></div>
<div class="slide"><img src="img/banner4.jpg" alt=""></div>-->
</div>
<ul class="focus">
<!-- <li class="active"></li>
<li></li>
<li></li>
<li></li>-->
</ul>
<a href="javascript: void 0;" class="arrow arrowLeft"></a>
<a href="javascript: void 0;" class="arrow arrowRight"></a>
</section>
<!--<section class="container" id="container2">
<div class="wrapper">
<!–<div class="slide"><img src="img/banner1.jpg" alt=""></div>
<div class="slide">
<img src="img/banner2.jpg" alt="">
</div>
<div class="slide"><img src="img/banner3.jpg" alt=""></div>
<div class="slide"><img src="img/banner4.jpg" alt=""></div>–>
</div>
<ul class="focus">
<!– <li class="active"></li>
<li></li>
<li></li>
<li></li>–>
</ul>
<a href="javascript:;" class="arrow arrowLeft"></a>
<a href="javascript:;" class="arrow arrowRight"></a>
</section>-->
<script src="js/jquery.js"></script>
<script src="js/banner.js"></script>
<!--<script>-->
<!--let b1 = new Banner({-->
<!--ele: '#container',-->
<!--url: 'json/banner.json',-->
<!--speed: 100-->
<!--});-->
<!--let b2 = new Banner({-->
<!--ele: '#container2',-->
<!--url: 'json/banner2.json',-->
<!--interval: 1000,-->
<!--speed: 500-->
<!--});-->
<!--</script>-->
</body>
</html>
css 代码
.container {
position: relative;
margin: 20px auto;
width: 1000px;
height: 300px;
overflow: hidden;
}
.container .wrapper {
position: absolute;
top: 0;
left: 0;
width: 4000px; /*根据 JS 中获取的数据动态控制宽度*/
height: 100%;
}
.container .wrapper .slide {
float: left;
width: 1000px;
height: 100%;
overflow: hidden;
}
.container .wrapper .slide img {
display: block;
width: 100%;
height: 100%;
}
.container .focus {
position: absolute;
z-index: 999;
bottom: 10px;
left: 50%;
transform: translateX(-50%); /*基于CSS3中的 transform 变形属性,在不固定的宽度的情况下实现水品居中 translateX:让当前元素在水平方向发生位移(此处-50%向左移动当前盒子的一半)*/
padding: 4px;
height: 12px;
background: rgba(0, 0, 0, .5);
border-radius: 10px; /*让其为盒子高度的一半,这样把长方型的盒子修改为椭圆状*/
font-size: 0;
}
.container .focus li {
display: inline-block;
margin: 0 4px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #FFF;
cursor: pointer;
}
.container .focus li.active {
background: #DB192A;
}
.container .arrow {
display: none;
position: absolute;
top: 50%;
margin-top: -22.5px;
width: 28px;
height: 45px;
background: url("../img/pre.png") no-repeat;
opacity: 0.3;
}
.container .arrow:hover {
opacity: 1;
}
.container .arrow.arrowLeft {
left: 0;
background-position: 0 0;
}
.container .arrow.arrowRight {
right: 0;
background-position: -50px 0;
}
js 代码
let bannerRender = (function () {
// -> 获取后续需要操作的对象或者元素
let container = $('#container'),
wrapper = $('.wrapper'),
focus = $('.focus'),
arrowLeft = $('.arrowLeft'),
arrowRight = $('.arrowRight'),
slideList = null,
focusList = null;
// => 轮播图运动的基础参数
let stepIndex = 0; // stepIndex 记录当前展示块的索引
let autoTimer = null; // autoTimer 自动轮播的定时器
let interval = 3000; // interval 间隔多长时间自动切换
// autoMove:控制轮播图的运动和切换
/*
* 索引为1,展示第二张,warpper 的 left - 1000;
* 索引为2,展示第三章,wrapper 的 left - 2000;
* ....
* wrapper 的 left 值其实就是当前要展示的图片索引对应的结果 :-索引 * 1000
* */
function autoMove() {
stepIndex++;
if (stepIndex >= slideList.length) {
// 说明再往后切换没有了,现在展示的是克隆的第一张,此时我们将 wrapper 设置到回到真实的第一张的位置,即 left = 0,然后 stepIndex = 1,这样就可以切换到第二张了
wrapper.css('left', 0);
stepIndex = 1;
}
wrapper.animate({
left: -stepIndex * 1000
}, 200); // 200 是从当前切换到下一张的动画时间 interval 是间隔多久切换一次
// -> 每一次运动完成需要让焦点跟着切换
changeFocus();
}
// changeFocus: 让焦点跟着轮播图切换而切换(运动到克隆的这一张的时候,也需要让第一个 li 有选中的样式
function changeFocus() {
// 当轮播图运动到最后一张(克隆的第一张,我们需要让第一个 li 有选中样式,之所以使用 tempIndex 是因为 stepIndex 对轮播图切换起着很大作用,不能轻易修改)
let tempIndex = stepIndex;
tempIndex === slideList.length - 1
? tempIndex = 0
: null;
focusList.each((index, item) => {
if (index === tempIndex) {
$(item).addClass('active')
} else {
$(item).removeClass('active')
}
})
}
// queryDate:获取数据
function queryData(onSuccess, onError) {
$.ajax({
url: 'json/banner.json',
method: 'GET',
dataType: 'json',
async: false,
success: onSuccess,
error: onError
})
}
// bindHTML:数据绑定
function bindHTML(data) {
let strSlide = ``;
let strFocus = ``;
data.forEach((item, index) => {
let {
img = 'img/banner1.jpg',
desc = '珠峰培训'
} = item;
strSlide += `<div class="slide">
<img src="${img}" alt="${desc}">
</div>`;
strFocus += `<li class="${index === 0 ? 'active' : '' }"></li>`
});
// 将第一张克隆一份放到最后
strSlide += `<div class="slide">
<img src="${data[0].img}" alt="${data[0].desc}">
</div>`
// 插入到容器中
wrapper.html(strSlide);
focus.html(strFocus);
// -> 获取所有的 slide 和 li
slideList = $('.slide');
focusList = $('.focus > li');
// -> 根据 slide 的个数动态计算 wrapper 的宽度
wrapper.css({
width: slideList.length * 1000
})
}
// handleContainer 鼠标进入后停止自动轮播,离开后开始自动轮播;
function handleContainer() {
container.on('mouseenter', function () {
clearInterval(autoTimer);
arrowLeft.css({
display: 'block'
});
arrowRight.css({
display: 'block'
})
}).on('mouseleave', function () {
clearInterval(autoTimer);
autoTimer = setInterval(autoMove, interval);
arrowLeft.css({
display: 'none'
});
arrowRight.css({
display: 'none'
})
});
}
// 点击箭头切换轮播
function handleArrow() {
arrowRight.click(autoMove);
arrowLeft.click(function () {
stepIndex--;
if (stepIndex < 0) {
// 如果索引减减后小于 0,说明当前已经是第一张,不能向右运动了,如果再减就该展示最后一张图片了(这里的最后一张应该是真实的最后一张图,而不是我们复制出来的那个),最后一张在 slide 中的位置是倒数第二,即索引位置是 length - 2
wrapper.css({
left: -(slideList.length - 1) * 1000
});
stepIndex = slideList.length - 2;
}
wrapper.finish().animate({
left: -stepIndex * 1000
}, 200);
changeFocus();
})
}
// 点击焦点切换
function handleFocus() {
focusList.on('click', function () {
// 给每个焦点绑定点击事件,点击的是谁,就让 stepIndex 的值等于谁的索引,然后运动到这里
let focusIndex = $(this).index();
stepIndex = index;
wrapper.finish().animate({
left: -focusIndex * 1000
}, 200);
changeFocus();
})
}
// queryData(bindHTML);
// handleContainer();
// handleArrow();
// handleFocus();
// autoTimer = setInterval(autoMove, interval)
return {
init() {
queryData(bindHTML);
handleContainer();
handleArrow();
handleFocus();
autoTimer = setInterval(autoMove, interval)
}
}
})();
bannerRender.init();