原生JS实现旋转木马式图片轮播插件开发详解
本文详细解析如何使用原生JavaScript封装一个旋转木马式图片轮播插件。内容涵盖HTML结构配置、CSS绝对定位原理、JS核心参数初始化、多实例封装方法、图片位置动态计算及自定义动画函数的实现,旨在提供无需依赖jQuery的纯原生解决方案。
插件开发背景与概述
作者此前已开发过两款图片轮播组件:一款为无动画效果的纯原生JS实现,另一款为基于jQuery的淡入淡出效果。为了在博客或个人网站中展示更具视觉冲击力的作品,作者参考了慕课网关于旋转木马jQuery插件的课程,并尝试将其逻辑转化为原生JavaScript封装。实际开发过程比预期更为复杂,以下将详细讲解实现步骤。
效果展示
由于服务器环境尚未就绪,无法提供在线演示,此处仅展示静态效果图。
从效果图中可大致了解轮播形态。如需查看完整代码实现,欢迎访问作者的GitHub仓库下载源码,并期待给予Star支持。
HTML结构与CSS定位原理
实现流程
HTML结构
<div class="carrousel-main" data-setting='{"width":1000,"height":400,
"carrouselWidth":750,
"carrouselHeight":400,
"scale":0.9,
"verticalAlign":"middle"}'>
<div class="carrousel-btn carrousel-btn-pre"></div>
<ul class="carrousel-list">
<li class="carrousel-item">
<a href="#"><img src="img/1.jpg"></a>
</li>
<li class="carrousel-item">
<a href="#"><img src="img/2.jpg"></a>
</li>
<li class="carrousel-item">
<a href="#"><img src="img/3.jpg"></a>
</li>
<li class="carrousel-item">
<a href="#"><img src="img/4.jpg"></a>
</li>
<li class="carrousel-item">
<a href="#"><img src="img/5.jpg"></a>
</ul>
<div class="carrousel-btn carrousel-btn-next"></div>
</div>
该组件的HTML结构与常规轮播组件相似,主要区别在于主容器div包含一个data-setting属性,用于存储轮播效果的配置参数,具体参数含义将在后续章节说明。
CSS部分代码此处省略,核心要点在于元素必须采用绝对定位。从效果图可见,每张图片的位置和尺寸均不相同,这些动态变化均由JavaScript控制,因此绝对定位是必要前提。接下来重点介绍JavaScript的实现逻辑。
JavaScript核心实现逻辑
JavaScript实现逻辑
以下梳理JS实现的关键步骤,掌握这些核心环节后,整体逻辑将变得清晰。
默认参数与对象封装
默认参数配置
作为封装插件,必须提供可配置的默认参数。本插件主要包含以下配置项: width:1000, //幻灯片区域宽度 height:400, //幻灯片区域高度 carrouselWidth:700, //首张幻灯片宽度 carrouselHeight:400, //首张幻灯片高度 scale:0.9, //显示比例系数,决定后续图片相对于首图的缩放程度 autoPlay:true, //是否启用自动播放 timeSpan:3000, //自动播放间隔时间(毫秒) verticalAlign:'middle' //图片垂直对齐方式,支持top、middle、bottom,默认为middle
对象封装
考虑到网站中可能多处复用同一轮播组件,封装至关重要。通过定义构造函数并初始化方法,可将所有具有相同类名的DOM节点传入,从而创建多个独立的轮播实例。初始化方法如下:
Carousel.init=function(carrousels){
var _this=this;
//将nodeList转换为数组
var cals= toArray(carrousels); <br> /*因为原生JS获取所有的类,得到的是一个nodeList,是一个类数组,如果想要使用数组的方法则需要转化为真正的数组。这里toArray为转化方法。*/
cals.forEach(function(item,index,array){
new _this(item);
});
}
在window.onload事件中,调用Carrousel.init(document.querySelectorAll('.carrousel-main')),即可批量创建多个轮播组件。
首帧初始化与图片布局
首帧位置参数初始化
后续所有帧的参数均基于首帧计算,因此首帧参数的准确定义至关重要。
setValue:function(){
this.carrousel.style.width=this.Settings.width+'px';
this.carrousel.style.height=this.Settings.height+'px';
/*左右按钮设置,这里要让左右按钮平均地瓜分轮播区域宽减去第一帧宽度之后的区域,z-index要比除第一帧外所有图片都高,而图片刚好左右分放置,因此z-index的值就是图片数量的一半。*/
var btnW=(this.Settings.width-this.Settings.carrouselWidth)/2;
this.preBtn.style.width=btnW+'px';
this.preBtn.style.height=this.Settings.height+'px';
this.preBtn.style.zIndex=Math.ceil(this.carrouselItems.length/2);
this.nextBtn.style.width=btnW+'px';
this.nextBtn.style.height=this.Settings.height+'px';
this.nextBtn.style.zIndex=Math.ceil(this.carrouselItems.length/2);
//第一帧相关设置
this.carrouselFir.style.left=btnW+'px';
this.carrouselFir.style.top=this.setCarrouselAlign(this.Settings.carrouselHeight)+'px';
this.carrouselFir.style.width=this.Settings.carrouselWidth+'px';
this.carrouselFir.style.height=this.Settings.carrouselHeight+'px';
this.carrouselFir.style.zIndex=Math.floor(this.carrouselItems.length/2);
},
在实例化对象时,从DOM节点中读取data-setting属性以覆盖默认配置。需注意,直接赋值可能导致未配置参数丢失,因此作者实现了类似jQuery $.extend的对象扩展方法,用于合并用户配置与默认参数。具体实现可参考源码下载。
其他图片位置设置
除首张外的图片需均匀分布在左右两侧,由于左右两侧图片的位置计算逻辑不同,需分别处理:
//设置右边图片的位置关系
var rightIndex=level;
rightSlice.forEach(function(item,index,array){
rightIndex--;
var i=index;
rw=rw*carrouselSelf.Settings.scale;//右边的图片是按照scale比例逐渐变小的
rh=rh*carrouselSelf.Settings.scale;
item.style.zIndex=rightIndex;//越往右边z-index的值越小,可以用图片数量的一般逐渐递减
item.style.width=rw+'px';
item.style.height=rh+'px';
item.style.opacity=1/(++i);//越往右边透明度越小<br> //这里的gap计算方法为:轮播区域减去第一帧宽度,除2,再除左边或者右边的图片张数
item.style.left=(constOffset+(++index)*gap-rw)+'px';//left的值实际上就是第一帧的left+第一帧的宽度+item的间距减去item的宽度
item.style.top=carrouselSelf.setCarrouselAlign(rh)+'px';
});
左侧图片的设置逻辑与右侧类似但更为简单,此处不再赘述。
旋转逻辑与自定义动画
旋转时图片位置与大小调整
此步骤为核心逻辑。点击右侧按钮执行左旋转(下一张),点击左侧按钮执行右旋转(上一张)。将所有图片视为环形结构,每次点击仅交换图片参数即可实现旋转效果。具体而言,左旋转时,将第二张参数赋给第一张,第三张赋给第二张……最后一张赋给第一张;右旋转时,将第一张参数赋给第二张,第二张赋给第三张……最后一张赋给第一张。
以下以左旋转为例进行说明
if(dir=='left'){
toArray(this.carrouselItems).forEach(function(item,index,array){
var pre;
if(index==0){//判断是否为第一张
pre=_this.carrouselLat;//让第一张的pre等于最后一张
var width=pre.offsetWidth; //获取相应参数
var height=pre.offsetHeight;
var zIndex=pre.style.zIndex;
var opa=pre.style.opacity;
var top=pre.style.top;
var left=pre.style.left;
}else{
var width = tempWidth;
var height = tempHeight;
var zIndex = tempZIndex;
var opa = tempOpacity;
var top = tempTop;
var left = tempLeft;
}
//这里需要注意,因为第二张图片是参照第一张的,而这样改变的时候,第一张是首先被改变的,因此必须先把第一张的相关参数临时保存起来。
tempWidth = item.offsetWidth;
tempHeight = item.offsetHeight;
tempZIndex = item.style.zIndex;
tempOpacity = item.style.opacity;
tempTop = item.style.top;
tempLeft = item.style.left;
item.style.width=width+'px';
item.style.height=height+'px';
item.style.zIndex=zIndex;
item.style.opacity=opa;
item.style.top=top;
// item.style.left=left;
animate(item,'left',left,function(){//自定义的原生js动画函数
_this.rotateFlag=true;
});
});
}
若不使用动画过渡,切换过程会显得生硬。由于原生JS缺乏内置动画函数,作者编写了一个模拟动画函数。其原理是获取DOM当前样式值与目标值的差值,通过差值除以18计算速度,并设定定时器(参考jQuery源码,每13毫秒执行一次),每次累加速度值直至达到目标值后清除定时器。具体实现可参考相关代码片段。
关键逻辑已阐述完毕,理解上述过程后,整体开发将较为顺利。
总结与优化思考
总结与思考
总结:
该插件逻辑相对直观。若结合jQuery实现,开发将更为简便;但纯原生实现时,自定义动画的流畅度略逊于jQuery封装好的动画效果。
此外,由于图片需均匀分布至左右两侧,对于偶数张图片的情况,作者采用克隆首图并追加至末尾的方式,使其变为奇数张,以平衡布局。
思考:
关于潜在Bug,作者定义了rotateFlag标志位以判断当前是否允许旋转,旨在防止快速点击导致的动画冲突。按下按钮时将rotateFlag设为false,动画结束后设为true,但实际防抖效果并不显著,希望有经验的开发者能提供改进建议,共同提升代码质量。
以上就是本文的全部内容,更多内容请参考:javascript图片轮播效果汇总,感谢阅读。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
