如果想看最优解,请跳到Version 2
Version 1
第一次自己尝试轮播图,思路大概是这样的:
点击按钮切换图片&导航点→点击导航点切换图片→自动播放图片并切换导航点→添加过渡效果
其中,切换图片用的是修改src的方式,导致我在添加过渡时绕了弯路,最后的效果差强人意,这只是过渡轮播图的第一版。
后来又尝试了把图片横着排,改变left的第二版,意识到官网的过渡效果应该同时包括淡入和淡出,即上一张图片淡出的时候下一张图片淡入,我前两次的尝试都是把每张图片分开的,不论是修改src还是修改left,两张图片没有交集,自然做不出好的过渡效果。
HTML
<div class="banner-wrapper">
<div class="banner w">
<ul class="img">
<li><a href="#"><img class="banner-img"src="./img/banner1.jpg"></a></li>
</ul>
<div class="dot-wrapper">
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
</div>
<div>
<a href="#" class="prev"></a>
</div>
<div>
<a href="#" class="next"></a>
</div>
</div>
</div>
CSS
.banner{
position: relative;
height: 460px;
}
.banner .img li{
position: absolute;
}
.banner .banner-img{
width: 100%;
}
.transition{
width: 100%;
animation: Ani 800ms linear 1;
}
@keyframes Ani{
0%{
opacity: 0.4;
}
100%{
opacity: 1;
}
}
.dot-wrapper{
width: 80px;
height: 10px;
bottom: 22px;
right: 35px;
position: absolute;
}
.dot{
width: 6px;
height: 6px;
border-radius: 50%;
background-color: rgba(0, 0, 0, .4);
border: 2px rgba(255, 255, 255, .4) solid;
margin-left: 6px;
float: left;
}
.active{
border-color: rgba(0, 0, 0, .4);
background-color:rgba(255, 255, 255, .4);
}
.dot:hover{
border-color: rgba(0, 0, 0, .4);
background-color:rgba(255, 255, 255, .4);
}
.prev,.next{
display: block;
position: absolute;
width: 41px;
height: 69px;
background-image: url(../img/icon-slides.png);
top: 184px;
}
.prev{
left: 233px;
background-position: -83px;
}
.prev:hover{
background-position: 0px;
}
.next{
right: 0;
background-position: -124px;
}
.next:hover{
background-position: -41px;
}
JS
window.onload = function(){
let prev = document.getElementsByClassName("prev")[0];
let next = document.getElementsByClassName("next")[0];
let img = document.getElementsByClassName("banner-img")[0];
let dots = document.getElementsByClassName("dot");
let imgArr = ["img/banner1.jpg","img/banner2.jpg","img/banner3.jpg","img/banner4.jpg","img/banner5.jpg"];
let index = 0;
//let count = 0; 动画播放次数计时器
dots[index].className = "dot active"//让第一个导航点选中
//点击按钮切换图片,改变导航点,设置过渡
prev.onclick = function(){
index--;
// count++;
if(index < 0){
index = imgArr.length - 1;
}
// if(img.className == "transition2" || img.className == "banner-img"){
// img.className = "transition1";
// }
// if(img.className == "transition1" || img.className == "banner-img"){
// img.className = "transition2";
// }
//尝试双类名交替,让动画多次播放,失败
//img.className = "transition";
//img.style.animationIterationCount = count;
//尝试增加播放次数,让动画多次播放,失败
img.className = "transition";//这一句必需,否则transition函数的第一句无法发挥作用,达不到预期效果
transition();//必须在第一句的下面
img.src = imgArr[index];
dots[index].className = "dot active";//让当前导航点选中
if(index+1 <= dots.length-1){
dots[index+1].className = "dot";
}
else{
dots[0].className = "dot";
}
clearInterval(timer);//关闭之前的定时器(每次点击按钮时要重新开始计时)
timer = autoRun();//设置新的定时器
}
next.onclick = function(){
index++;
if(index > imgArr.length -1){
index = 0;
}
img.className = "transition";
transition();
img.src = imgArr[index];
dots[index].className = "dot active";//让当前导航点选中
if(index-1 >= 0){
dots[index-1].className = "dot"
}
else{
dots[dots.length-1].className = "dot"
}
clearInterval(timer);//关闭之前的定时器
timer = autoRun();//设置新的定时器
}
//点击导航点切换图片
let tmp = 0;
for(let i = 0;i < dots.length; i++){
dots[i].onclick = function(){
dots[index].className = "dot";
//双变量方法:index是点击prev和next对应的图片编号,i是属于每个导航点的编号
//不能在2个for循环内都用index,否则无法修改index,即最后一行的代码无法实现
//先让index对应的导航点未选中
dots[tmp].className = "dot";
//tmp表示之前 通过点击导航点 而选中的导航点,让它未选中
dots[i].className = "dot active";
//让 最新一次点击的导航点 选中
img.src = imgArr[i];
img.className = "transition";
transition();
//改变图片
tmp = i;
//更新tmp
index = i;
//修改index,否则再次点击prev或next时会出问题
clearInterval(timer);//关闭之前的定时器
timer = autoRun();//设置新的定时器
}
}
timer = autoRun();
img.style.transitionDuration = "1s";
//图片的自动轮播函数
function autoRun(){
let timer = setInterval(function(){
index++;
if(index >= imgArr.length){
index = 0;
dots[dots.length-1].className = "dot";
}
else{
dots[index-1].className = "dot";
}
img.src = imgArr[index];
img.className = "transition";
transition();
dots[index].className = "dot active";
},5000);
// let timer_ = transition();
// img.style.opacity = 1;
return timer;
//timer封装在autoRun函数内,外层访问不到,必须返回timer,这样才能关闭定时器
}
//轮播的过渡效果
function transition(){
// img.style.opacity = 1; 默认值
// let timer_ = setTimeout(() => {
// img.style.opacity = 0;
// }, 4000);
// img.style.opacity = 1;
//如果像上边这样写,会先执行最后一行代码,再执行定时器,错误
//setTimeOut定时器不可行,因为在点击按钮和导航点时是等不到4s的,会失去过渡效果,而且定时器的开关问题比较麻烦
document.querySelector(".transition").className = "banner-img";
//注意,需要先在prev,next和导航点的onclick回调函数中,将img的className设为transition
window.requestAnimationFrame(function(time){
window.requestAnimationFrame(function(time){
document.querySelector(".banner-img").className = "transition";
});
});
}
//如果不在上方直接调用transition函数,写成这种绑定的形式也可以:
// document.querySelector(".prev").addEventListener("click", transition, false);
// document.querySelector(".next").addEventListener("click", transition, false);
// for(let j = 0;j < dots.length;j++){
// dots[j].addEventListener("click", transition, false);
// }
}
最后过渡的效果,是反复执行一段动画(半透明→不透明)实现的,至于如何反复执行一段动画,参考了CSS动画之animation再次触发与防止多次触发
Version 2
后来上网查,发现正确做法应该是:把所有图片叠在一起,每次只有一张的opacity值为1,其它为0,切换图片时候就把要隐藏元素的opacity设为0,要显示的设为1,再加上transition过渡,大功告成
HTML
<div class="banner-wrapper">
<div class="banner w">
<ul class="img-list">
<li class="banner-img no-trans"><a href="#"><img src="./img/banner1.jpg"></a></li>
<li class="banner-img"><a href="#"><img src="./img/banner2.jpg"></a></li>
<li class="banner-img"><a href="#"><img src="./img/banner3.jpg"></a></li>
<li class="banner-img"><a href="#"><img src="./img/banner4.jpg"></a></li>
<li class="banner-img"><a href="#"><img src="./img/banner5.jpg"></a></li>
</ul>
<div class="dot-wrapper">
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
<a href="#" class="dot"></a>
</div>
<div>
<a href="#" class="prev"></a>
</div>
<div>
<a href="#" class="next"></a>
</div>
</div>
</div>
CSS
.banner{
position: relative;
height: 460px;
}
.banner li{
position: absolute;
opacity: 0;
transition: opacity 800ms;
}
.banner li.no-trans{
opacity: 1;
/*让第一张图片设为不透明,避免刷新页面时多余的过渡*/
/*需要提高该选择器的优先级*/
}
.banner img{
width: 100%;
}
.dot-wrapper{
width: 80px;
height: 10px;
bottom: 22px;
right: 35px;
position: absolute;
}
.dot{
width: 6px;
height: 6px;
border-radius: 50%;
background-color: rgba(0, 0, 0, .4);
border: 2px rgba(255, 255, 255, .4) solid;
margin-left: 6px;
float: left;
}
.active{
border-color: rgba(0, 0, 0, .4);
background-color:rgba(255, 255, 255, .4);
}
.dot:hover{
border-color: rgba(0, 0, 0, .4);
background-color:rgba(255, 255, 255, .4);
}
.prev,.next{
display: block;
position: absolute;
width: 41px;
height: 69px;
background-image: url(../img/icon-slides.png);
top: 184px;
}
.prev{
left: 233px;
background-position: -83px;
}
.prev:hover{
background-position: 0px;
}
.next{
right: 0;
background-position: -124px;
}
.next:hover{
background-position: -41px;
}
JS
window.onload = function(){
let prev = document.getElementsByClassName("prev")[0];
let next = document.getElementsByClassName("next")[0];
let imgs = document.getElementsByClassName("banner-img");
let dots = document.getElementsByClassName("dot");
let index = 0;
imgs[index].style.opacity = 1;
dots[index].className = "dot active"
prev.onclick = function(){
index--;
if(index >= 0){
imgs[index].style.opacity = 1;
imgs[index + 1].style.opacity = 0;
dots[index].className = "dot active";
dots[index + 1].className = "dot";]
else{
index = imgs.length - 1;
imgs[index].style.opacity = 1;
imgs[0].style.opacity = 0;
dots[0].className = "dot";
dots[dots.length - 1].className = "dot active";
}
clearInterval(timer);
timer = autoRun();
}
next.onclick = function(){
index++;
if(index < imgs.length){
imgs[index - 1].style.opacity = 0;
imgs[index].style.opacity = 1;
dots[index].className = "dot active";
dots[index - 1].className = "dot";
}
else{
index = 0;
imgs[imgs.length - 1].style.opacity = 0;
imgs[index].style.opacity = 1;
dots[dots.length - 1].className = "dot";
dots[0].className = "dot active";
}
clearInterval(timer);
timer = autoRun();
}
for(let i = 0; i < dots.length; i++){
dots[i].onclick = function(){
imgs[index].style.opacity = 0;
dots[index].className = "dot";
imgs[i].style.opacity = 1;
dots[i].className = "dot active";
index = i;
clearInterval(timer);
timer = autoRun();
}
}
function autoRun(){
let timer = setInterval(function(){
index++;
if(index < imgs.length){
imgs[index - 1].style.opacity = 0;
imgs[index].style.opacity = 1;
dots[index].className = "dot active";
dots[index - 1].className = "dot";
}
else{
index = 0;
imgs[imgs.length - 1].style.opacity = 0;
imgs[index].style.opacity = 1;
dots[dots.length - 1].className = "dot";
dots[0].className = "dot active";
}
},5000);
return timer;
}
let timer = autoRun();
}
Version2基本上就能还原小米官网的轮播图了
如果有帮助的话,请帮我Star一下→谢谢大佬动动手指