实现思路
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>左右版轮播图</title>
<!-- IMPORT CSS -->
<link rel="stylesheet" href="css/reset.min.css">
<link rel="stylesheet" href="css/index.css">
</head>
<body>
<div class="container" id="bannerBox">
<!-- 轮播图 -->
<div class="wrapper">
<!-- <div class="slide"><img src="images/banner01.png" alt=""></div>
<div class="slide"><img src="images/banner02.jpg" alt=""></div>
<div class="slide"><img src="images/banner03.png" alt=""></div>
<div class="slide"><img src="images/banner01.png" alt=""></div> -->
</div>
<!-- 分页器 -->
<div class="pagination">
<!-- <span index="0" class="active"></span>
<span index="1"></span>
<span index="2"></span> -->
</div>
<!-- 导航按钮 -->
<div class="navigation prev"></div>
<div class="navigation next"></div>
</div>
<!-- IMPORT JS -->
<script src="js/index.js"></script>
</body>
</html>
CSS
.container{
box-sizing: border-box;
position: relative;
margin: 20px auto;
width: 1226px;
height: 460px;
overflow: hidden;
}
.container .wrapper{
display: flex;
/* justify-content: flex-start;
align-items: flex-start; */
position: absolute;
top: 0;
/* 默认展示第一张 */
left: 0;
/*width是根据slide数量决定的{不要忘记克隆的这一张}*/
width: 400%;
height: 100%;
/* 每一次wrapper左右运动,都是改变lefe值,我们让其具备过渡动画效果 */
transition: left .3s;
}
.container .wrapper .slide{
width: 1226px;
height: 100%;
}
.container .wrapper .slide img{
display: block;
width: 100%;
height: 100%;
}
/* 分页器 */
.container .pagination {
display: flex;
position: absolute;
bottom: 20px;
left: 50%;
/* 居中 */
transform: translateX(-50%);
padding: 5px;
/* 椭圆 */
border-radius: 10px;
background: rgba(0, 0, 0, .2);
}
.container .pagination span{
margin: 0 8px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #fff;
cursor: pointer;
transition: background .5s;
}
.container .pagination span.active{
background:#aaa;
}
.container .navigation{
position: absolute;
top:50%;
transform: translateY(-50%);
width: 41px;
height: 69px;
/* 垂直居中 */
background: url('../images/icon-slides.png') no-repeat;
cursor: pointer;
opacity: .5;
}
.container .navigation.prev {
left: 0;
background-position: 0 50%;
cursor: pointer;
}
.container .navigation.next {
right: 0;
background-position: -42px 50%;
}
js
-1)基础实现版本
(function () {
let bannerBox = document.querySelector('#bannerBox'), //最外层轮播图容器
wrapper = bannerBox.querySelector('.wrapper'),//包裹所有图片的容器
pagination = bannerBox.querySelector('.pagination'),//分页器容器
paginationList = pagination.querySelectorAll('span');//分页器span标签集合
// step记录当前展示这个slide的索引
// autoTimer记录自动轮播的定时器
// distance记录容器的宽度,也是每一次wrapper运动的距离 1226
// count记录slide的总数量(包含克隆这一张)
let step = 0,
autoTimer = null,
distance = bannerBox.offsetWidth,//容器的宽度
count = 4;//包含克隆的哪一张
// 实现自动轮播
const autoMove = function autoMove() {
step++;
if (step >= count) {
// 如果累加后的索引比最大索引都大,说明当前已经是末尾这一张(克隆):我们让其立即运动到真实第一张 & 再让其有动画效果的运动到第二张
wrapper.style.transitionDuration = '0s';
wrapper.style.left = '0px';
step = 1;
wrapper.offsetWidth;
}
wrapper.style.transitionDuration = '0.3s';
wrapper.style.left = `${-step * distance}px`;
// 每次切换完成,同时控制焦点对齐
paginationFocus();
};
// 分页器对齐
const paginationFocus = function paginationFocus() {
let temp = step;
if (temp === count - 1) temp = 0;
paginationList.forEach((item, index) => {
if (index === temp) {
item.className = 'active';
return;
}
item.className = '';
});
};
// 基于事件委托实现容器中分页器点击切换&左右导航按钮左右切换
bannerBox.addEventListener('click', function (ev) {
let target = ev.target,
tarTag = target.tagName;
tarClass = target.className;
// 点击分页器的焦点
if (tarTag === "SPAN") {
let index = +target.getAttribute('index');
if ((index === step) || (step === count - 1 && index === 0)) return; //点击的这一项就是展示的这一项,则无需处理[不要忘记克隆的那个]{展示的克隆的那项 但是焦点处于0 所以也不用做处理}
step = index;
wrapper.style.left = `${-step * distance}px`;
paginationFocus();//分页器焦点对齐
return;
}
// 点击左右按钮
if (tarTag === 'DIV' && tarClass.includes('navigation')) {
// 左按钮
if (tarClass.includes('prev')) {
step--;
if (step < 0) {
// 到达左边界:立即蹦到最后一张(克隆),然后运动到倒数第二张
wrapper.style.transitionDuration = '0s';
wrapper.style.left = `${-(count - 1) * distance}px`;
step = count - 2;
wrapper.offsetWidth;
}
wrapper.style.transitionDuration = '0.3s';
wrapper.style.left = `${-step * distance}px`;
paginationFocus();//分页器焦点对齐
return;
}
// 右按钮 和自动轮播一样
autoMove();
}
});
// 开启自动轮播 & 控制自动轮播的暂停/继续
autoTimer = setInterval(autoMove, 1000);//轮循定时器
//当鼠标点上去要清空轮播图
bannerBox.onmouseenter = () => clearInterval(autoTimer);
//当鼠标离开的时候要 把轮播定时器加上
bannerBox.onmouseleave = () => autoTimer = setInterval(autoMove, 1000);
})();
-2)最终实现版本
动态数据绑定&封装左右切换点击分页器切换方法
(function () {
let bannerBox = document.querySelector('#bannerBox'),
wrapper = bannerBox.querySelector('.wrapper'),
pagination = bannerBox.querySelector('.pagination'),
paginationList =null;
// step记录当前展示这个slide的索引
// autoTimer记录自动轮播的定时器
// distance记录容器的宽度,也是每一次wrapper运动的距离 1226
// count记录slide的总数量(包含克隆这一张)
let step = 0,
autoTimer = null,
distance = bannerBox.offsetWidth,//容器的宽度
count = 0;//包含克隆的哪一张
// 获取数据&数据绑定
const binding=function binding(){
// 获取数据
let data=[],
str1=``;
str2=``;
let xhr=new XMLHttpRequest();
xhr.open('GET','./data.json',false);
xhr.onreadystatechange=function(){
if(xhr.readyState===4&&xhr.status===200){
data=JSON.parse(xhr.responseText);
}
};
xhr.send();
//数据绑定
data.push(data[0]);//从数据层克隆一份到末尾
data.forEach((item,index)=>{
str1+=`<div class="slide"><img src="${item.pic}" alt=""></div>`;
if(index<data.length-1){
str2+=`<span index="${index}" class="${index===0?'active':''}" ></span>`;
}
});
wrapper.innerHTML=str1;
pagination.innerHTML=str2;
count=data.length;
wrapper.style.width=`${count*100}%`;
paginationList=pagination.querySelectorAll('span');
};
//封装公共切换的方法
const change = function change(dir) {
if (dir != null) {
if (dir === 'left') {
//点击左按钮 切换到上一张
step--;
if (step < 0) {
// 到达左边界:立即蹦到最后一张(克隆),然后运动到倒数第二张
wrapper.style.transitionDuration = '0s';
wrapper.style.left = `${-(count - 1) * distance}px`;
step = count - 2;
wrapper.offsetWidth;
}
} else {
//右按钮 切换到下一张
step++;
if (step >= count) {
// 如果累加后的索引比最大索引都大,说明当前已经是末尾这一张(克隆):我们让其立即运动到真实第一张 & 再让其有动画效果的运动到第二张
wrapper.style.transitionDuration = '0s';
wrapper.style.left = '0px';
step = 1;
wrapper.offsetWidth;
}
}
}
wrapper.style.transitionDuration = '0.3s';
wrapper.style.left = `${-step * distance}px`;
// 每次切换完成,同时控制焦点对齐
paginationFocus();
};
// 分页器对齐
const paginationFocus = function paginationFocus() {
let temp = step;
if (temp === count - 1) temp = 0;
paginationList.forEach((item, index) => {
if (index === temp) {
item.className = 'active';
return;
}
item.className = '';
});
};
// 基于事件委托实现容器中分页器点击切换&左右导航按钮左右切换
bannerBox.addEventListener('click', function (ev) {
let target = ev.target,
tarTag = target.tagName;
tarClass = target.className;
// 点击分页器的焦点
if (tarTag === "SPAN") {
let index = +target.getAttribute('index');
if ((index === step) || (step === count - 1 && index === 0)) return; //点击的这一项就是展示的这一项,则无需处理[不要忘记克隆的那个]{展示的克隆的那项 但是焦点处于0 所以也不用做处理}
step = index;
change();
return;
}
// 点击左右按钮
if (tarTag === 'DIV' && tarClass.includes('navigation')) {
// 左按钮
if (tarClass.includes('prev')) {
change('left');
return;
}
// 右按钮 和自动轮播一样
change('right');
}
});
binding();
// 开启自动轮播 & 控制自动轮播的暂停/继续
autoTimer = setInterval(change.bind(null,'right'), 1000);//轮循定时器
//当鼠标点上去要清空轮播图
bannerBox.onmouseenter = () => clearInterval(autoTimer);
//当鼠标离开的时候要 把轮播定时器加上
bannerBox.onmouseleave = () => autoTimer = setInterval(change.bind(null,'right'), 1000);
})();