JavaScript实现小米风格轮播图:透明度渐变与无缝切换详解
本文详细解析基于JavaScript的小米风格轮播图实现方案。核心采用透明度渐变(opacity)替代位移,通过首尾图片冗余复制实现视觉上的无缝切换。文章涵盖自动播放、手动点击切换(上一张/下一张)及指示器联动逻辑,并附带完整HTML、CSS及JS代码示例,适合前端初学者参考学习。
项目概述与代码结构
本文分享一个仿照小米官网风格编写的轮播图案例,主要利用元素透明度的变化来实现图片切换效果。作为新手练习作品,代码中可能存在不够优化的地方,欢迎指正。所有相关代码已整理在文章末尾供参考。此外,文中存在部分重复逻辑,后续计划将这些重复代码封装为独立函数以提高复用性。
透明度渐变动画机制
以函数调用 delayOpacity(OliArray[pre],0,-0.1); 为例,该操作旨在将 OliArray[pre] 对象的透明度值逐步调整为 0,其中 -0.1 代表每次变化的步长(速度)。
透明度渐变动画实现
function delayOpacity(obj,target,speed){
clearInterval(timer);
timer = setInterval(function(){
var old = getComputedStyle(obj,null)['opacity'];
var newVal = +old + +speed;//+是将字符串转变成number类型
if(parseInt(speed)>0 && newVal>=target){
newVal = 1;
}
if(parseInt(speed)<0 && newVal<=target){
newVal = 0;
}
obj.style.opacity = newVal;//每次将新的值赋值给该对象的透明度,产生渐变效果
if(newVal==target){
clearInterval(timer);//当透明度的值和目标中的透明度的值一样的时候,将定时器关闭
}
},100);
}
自动轮播功能实现
为了实现视觉上的无缝循环,虽然实际展示的图片只有四张,但在DOM结构中实际上放置了五张图片,其中第五张图片与第一张图片完全相同。这种冗余设计确保了从最后一张切换到第一张时,透明度渐变过程自然流畅,避免生硬的跳变。全局变量 next 用于记录即将切换到的图片索引,pre 用于记录当前正在显示的图片索引。
自动轮播与无缝切换逻辑
function autoPlay(){
autoTimer = setInterval(function(){
next++;
pre++;
next %= OliArray.length;
pre %= OliArray.length;
if(pre==OliArray.length-1){
pre = 0;
}
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
/*解决点击前几张图片时,之后自动轮播没有切换效果的问题,
由于后面的图片显示的效果在前面的图片之上,所以如果在播放到第三张图片时,
点击第一张图片后,自动轮播时,因为第三张图片的显示在第二张图片之上
所以从第一张到第二张,是没有动画效果,且图片一直停在第三张
所以要把将要显示的图片的层级设置为1,其他图片的层级设置为0*/
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = ""; //这个是点击时,除点击的那个原点外,其他的激活的样式清空
}
}
OliArray[next].style.zIndex=1;
delayOpacity(OliArray[pre],0,-0.1);//将上一张的透明度由1变成0
delayOpacity(OliArray[next],1,0.1);//将要显示的图片的透明度由0变成1
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;//到最后一张图片的时候,立即切换到第一张,相对于假装轮播到第一张了,因为用户的眼睛没有那么亮
}
PointerArray[next].className = "active";
},3000);
}
点击“上一张”按钮逻辑
手动切换交互处理
prevBanner.onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
pre = next;//此时的pre就是原来切换到的那张图片
next = next-1>=0? next-1:OliArray.length-2;//next就是上一张,要切换的图片
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = "";
}
}
OliArray[next].style.zIndex = 1;
delayOpacity(OliArray[pre],0,-0.1);
delayOpacity(OliArray[next],1,0.1);
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;
}
PointerArray[next].className = "active";
pre = next - 1;
//开启自动轮播
autoPlay();
}
点击“下一张”按钮逻辑
手动切换逻辑与自动播放类似,但区别在于不使用定时器。此处逻辑较为独立,作者认为未来可以将其封装为通用函数以便后续修改和维护。
nextBanner.onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
next++;
pre++;
next %= OliArray.length;
pre %= OliArray.length;
if(pre==OliArray.length-1){
pre = 0;
}
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = "";
}
}
OliArray[next].style.zIndex = 1;
delayOpacity(OliArray[pre],0,-0.1);
delayOpacity(OliArray[next],1,0.1);
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;
}
PointerArray[next].className = "active";
//开启自动轮播
autoPlay();
}
点击圆点指示器切换图片
指示器点击与联动
for(let i=0;i<PointerArray.length;i++){
PointerArray[i].onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
for(let j=0;j<OliArray.length;j++){
if(j!=i){
OliArray[j].style.zIndex = 0;
}
if(j!=next)
OliArray[j].style.opacity = 0;
if(j!=OliArray.length-1){
PointerArray[j].className = "";
}
}
OliArray[i].style.zIndex=1;
delayOpacity(OliArray[next],0,-0.1);
delayOpacity(OliArray[i],1,0.1);
PointerArray[i].className = "active";
pre = i - 1 == 0? OliArray.length-1:i-1;
next = i;
//开启自动轮播
autoPlay();
}
}
HTML结构部分
完整代码示例
<div class="banner-wapper">
<div class="container">
<div class="banner">
<ul class="img-list">
<li>
<a href="#">
<img src="./img/1.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="./img/2.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="./img/3.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="./img/4.jpg" alt="">
</a>
</li>
<li>
<a href="#">
<img src="./img/1.jpg" alt="">
</a>
</li>
</ul>
<div class="pointer">
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
</div>
<div class="prev-next">
<a class="prev" href="javascript:;"></a>
<a class="next" href="javascript:;"></a>
</div>
</div>
</div>
</div>
CSS样式部分
.banner{
position: relative;
height: 460px;
}
.banner .img-list li{
position: absolute;
opacity: 0;
}
.banner-wapper .banner a img{
width: 1226px;
height: 460px;
vertical-align: top;
}
.banner .img-list li:nth-child(1){
opacity: 1;
}
.pointer{
z-index: 2;
position: absolute;
right: 30px;
bottom: 20px;
}
.pointer a{
float: left;
width: 6px;
height: 6px;
border: 2px rgba(255, 255, 255, 0.4) solid;
box-sizing: content-box;
margin: 0 4px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.4);
}
.pointer a:hover,
.pointer .active{
border-color:rgba(0, 0, 0, 0.4);
background-color: rgba(255, 255, 255, 0.4);
}
.prev-next a{
width: 41px;
height: 69px;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
background-image: url(../img/icon-slides.png);
}
.prev-next .prev{
z-index: 2;
left: 234px;
background-position: -84px 50%;
}
.prev-next .prev:hover{
background-position: 0 0;
}
.prev-next .next{
z-index: 2;
right: 0;
background-position: -125px 50%;
}
.prev-next .next:hover{
background-position: -42px 50%;
}
JavaScript逻辑部分
window.onload = function(){
var Oul = document.getElementsByClassName("img-list")[0];
var OliArray = Oul.getElementsByTagName("li");
var pointer = document.getElementsByClassName("pointer")[0];
var PointerArray = pointer.getElementsByTagName("a");
var nextBanner = document.getElementsByClassName("next")[0];
var prevBanner = document.getElementsByClassName("prev")[0];
var timer,autoTimer, next = 0,pre = OliArray.length-1;
PointerArray[0].className = "active";
autoPlay();
// 点击轮播
for(let i=0;i<PointerArray.length;i++){
PointerArray[i].onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
for(let j=0;j<OliArray.length;j++){
if(j!=i){
OliArray[j].style.zIndex = 0;
}
if(j!=next)
OliArray[j].style.opacity = 0;
if(j!=OliArray.length-1){
PointerArray[j].className = "";
}
}
// console.log(pre,next,i)
OliArray[i].style.zIndex=1;
delayOpacity(OliArray[next],0,-0.1);
delayOpacity(OliArray[i],1,0.1);
PointerArray[i].className = "active";
pre = i - 1 == 0? OliArray.length-1:i-1;
next = i;
//开启自动轮播
autoPlay();
}
}
// 点击下一张
nextBanner.onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
next++;
pre++;
next %= OliArray.length;
pre %= OliArray.length;
if(pre==OliArray.length-1){
pre = 0;
}
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = "";
}
}
OliArray[next].style.zIndex = 1;
delayOpacity(OliArray[pre],0,-0.1);
delayOpacity(OliArray[next],1,0.1);
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;
}
PointerArray[next].className = "active";
//开启自动轮播
autoPlay();
}
//点击上一张
prevBanner.onclick = function(){
//将自动轮播的定时器清空
clearInterval(autoTimer);
pre = next;
next = next-1>=0? next-1:OliArray.length-2;
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = "";
}
}
OliArray[next].style.zIndex = 1;
delayOpacity(OliArray[pre],0,-0.1);
delayOpacity(OliArray[next],1,0.1);
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;
}
PointerArray[next].className = "active";
pre = next - 1;
//开启自动轮播
autoPlay();
}
// 自动轮播
function autoPlay(){
autoTimer = setInterval(function(){
next++;
pre++;
next %= OliArray.length;
pre %= OliArray.length;
if(pre==OliArray.length-1){
pre = 0;
}
for(let i=0;i<OliArray.length;i++){
if(i!=next){
OliArray[i].style.zIndex = 0;
}
if(i!=pre)
OliArray[i].style.opacity = 0;
if(i!=OliArray.length-1){
PointerArray[i].className = "";
}
}
OliArray[next].style.zIndex=1;
delayOpacity(OliArray[pre],0,-0.1);
delayOpacity(OliArray[next],1,0.1);
if(next==OliArray.length-1) {
next = 0;
OliArray[next].style.opacity = 1;
}
PointerArray[next].className = "active";
},3000);
}
function delayOpacity(obj,target,speed){
clearInterval(timer);
timer = setInterval(function(){
var old = getComputedStyle(obj,null)['opacity'];
// console.log(getComputedStyle(obj,null)['opacity'])
var newVal = +old + +speed;
// console.log(obj,newVal)
if(parseInt(speed)>0 && newVal>=target){
newVal = 1;
}
if(parseInt(speed)<0 && newVal<=target){
newVal = 0;
}
obj.style.opacity = newVal;
// console.log(getComputedStyle(obj,null)['opacity'])
if(newVal==target){
clearInterval(timer);
}
},100);
}
}
以上内容即为本文的全部代码与解析,希望能为读者的学习提供一定帮助。欢迎大家支持本站,如有问题欢迎交流。
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
