实现轮播图是前端学习的一门必修课,也是用来训练js能力。今天我用原生js写了个轮播图,包含无缝衔接、指示器、左右控制器等功能,跟大家分享。先看看效果图:
1. 设计html+css部分
结构很简单,图片部分用ul
作容器(本人习惯ul,你们也可以div),flex
布局让里面的li
排成一排,因为我们要做的是滑动效果而不是简单显隐。注意,要在最后另外多一个li
存放第一张图,这是为了后面实现无缝轮播。 其他的就是小圆点指示器、左右控制器了。
<body>
<div class="content">
<ul class="wrapper"> //图片容器
<li><img src="./static/banner1.jpg" ></li>
<li><img src="./static/banner2.jpg" ></li>
<li><img src="./static/banner3.jpg" ></li>
<li><img src="./static/banner1.jpg" ></li> //重复第一张
</ul>
<ul class="radius"> //小圆点指示器,后面js动态添加li
</ul>
<div class="prev"> //左控制器
<span><</span>
</div>
<div class="next">
<span>></span> //右控制器
</div>
</div>
</body>
<style>
* {
margin: 0;
padding: 0;
}
.content {
width: 300px;
height: 300px;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.wrapper {
width: 400%;
height: 100%;
margin: 0;
padding: 0;
position: absolute;
top: 0;
left: 0;
display: flex;
transition: none;
}
.wrapper li {
flex: 1;
list-style: none;
margin: 0;
padding: 0;
}
.wrapper li img {
width: 100%;
height: 100%;
}
.radius {
height: 12px;
margin: 0;
padding: 0;
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
align-items: center;
}
.radius li {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: white;
opacity: 0.6;
margin: 0 3px;
padding: 0;
list-style: none;
}
.radius-active {
opacity: 1 !important;
border: 2px solid rgb(255, 255, 255, 0.5);
background-clip: padding-box;
}
.prev {
width: 23px;
line-height: 34px;
text-align: center;
position: absolute;
left: 0;
top: 50%;
margin-top: -17px;
background-color: darkgray;
opacity: 0;
}
.next {
width: 23px;
line-height: 34px;
text-align: center;
position: absolute;
right: 0;
top: 50%;
margin-top: -17px;
background-color: darkgray;
opacity: 0;
}
.prev span, .next span {
font-weight: bold;
color: white;
font-size: 18px;
}
</style>
2. Js获取需要用到的Dom节点
var content = document.getElementsByClassName('content')[0] //整个容器
var wrapper = document.getElementsByClassName('wrapper')[0] //轮播图容器
var radius = document.getElementsByClassName('radius')[0] //小圆点容器
var prev = document.getElementsByClassName('prev')[0] //左控制器
var next = document.getElementsByClassName('next')[0] //右控制器
var imgWidth = wrapper.children[0].offsetWidth //一个轮播li的宽度
var wrapIndex = 0 //用来判断位置的标识
3. 动态创建小圆点指示器
有人说“轮播几张图就给几个小圆点就好了嘛”,确实,不过站在便于以后修改、维护的角度,我习惯让小圆点根据图片数量动态创建,这也是开发好习惯。
function createLi() {
for(let i=0; i<wrapper.children.length-1; i++) { //轮播图x张,实际只是轮播x-1张,小圆点也是x-1个
let li = document.createElement("li") //创建li标签
radius.appendChild(li) //添加进radius
}
radius.children[0].className='radius-active' //初始化,为第一个小圆点添加active
}
createLi()
4. 动画函数
以16毫秒一帧地改变wrapper
的left
值。
function animate(el, target) {
clearInterval(el.timer) //停止上一个功能的el.timer
el.timer = setInterval(function(){ //16毫秒地循环执行
let move = 8;
let present = wrapper.offsetLeft; //当前位置left值
move = present > target ? -move : move;
present += move; //改变当前位置left值
if(Math.abs(present-target) > Math.abs(move)) {
wrapper.style.left = present + 'px' //如果位置差大于move,则保留位置继续循环移动
}
else { //如果小于,则让当前位置等于目标位置,退出计时器循环
clearInterval(el.timer);
wrapper.style.left = target + 'px'
}
},16)
}
5. 小圆点指示器响应
清除所有active
,跟随当前标识响应相应的小圆点添加active
。
function cirAction(wrapIndex) {
for(let i=0; i<radius.children.length; i++) {
radius.children[i].classList.remove("radius-active")
}
if(wrapIndex === wrapper.children.length-1) {
radius.children[0].className='radius-active'
}
else {
radius.children[wrapIndex].className='radius-active'
}
}
6. next单击控制
next元素的单击控制是轮播图核心,提前封装好了可在其他功能上直接调用,减少代码量。注意当轮播到最后一张图时,要让位置标识变为"0",其实也就是闪现到第一张图(第一张和最后一张相同,不漏痕迹),这就是前面为什么要在最后加一张首图的原因。
next.onclick = function() {
if(wrapIndex === wrapper.children.length-1){ //如果到了最后一张
wrapIndex = 0; //调整标识
wrapper.style.left = 0 + 'px'; //闪现回第一张
}
wrapIndex++;
animate(wrapper,-wrapIndex*imgWidth); //滑动动画
cirAction(wrapIndex); //小圆点跟随响应
}
7. 自动轮播
var timer = setInterval(function(){
next.onclick()
},3500)
8. prev单击控制
prev.onclick = function() {
if(wrapIndex === 0){
wrapIndex = wrapper.children.length-1;
wrapper.style.left = -wrapIndex*imgWidth + 'px';
}
wrapIndex--;
animate(wrapper,-wrapIndex*imgWidth);
cirAction(wrapIndex)
}
9. 小圆点指示器移入控制
切记要先清除timer定时器。
function cirMouse() {
for(let i=0; i<radius.children.length; i++) {
radius.children[i].onmouseover = function() {
clearInterval(timer);
animate(wrapper, -i*imgWidth);
wrapIndex = i;
cirAction(wrapIndex)
}
}
}
cirMouse()
10. 悬停和移出content容器响应
悬停时停止timer定时器,显示左右控制器;移出时再设置回timer,左右控制器消失。
content.onmouseover = function() {
clearInterval(timer)
next.style.opacity = "0.6";
prev.style.opacity = "0.6";
}
content.onmouseout = function() {
timer = setInterval(function(){
next.onclick()
},3500)
next.style.opacity = "0";
prev.style.opacity = "0";
}
这样一个满足日常开发功能的轮播图就实现了。完整代码如下,可参考交流:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swiper Test</title>
</head>
<body>
<div class="content">
<ul class="wrapper">
<li><img src="./static/banner1.jpg" ></li>
<li><img src="./static/banner2.jpg" ></li>
<li><img src="./static/banner3.jpg" ></li>
<li><img src="./static/banner1.jpg" ></li>
</ul>
<ul class="radius">
</ul>
<div class="prev">
<span><</span>
</div>
<div class="next">
<span>></span>
</div>
</div>
</body>
<script type="text/javascript">
window.onload= function() {
var content = document.getElementsByClassName('content')[0]
var wrapper = document.getElementsByClassName('wrapper')[0]
var radius = document.getElementsByClassName('radius')[0]
var prev = document.getElementsByClassName('prev')[0]
var next = document.getElementsByClassName('next')[0]
var imgWidth = wrapper.children[0].offsetWidth
var wrapIndex = 0
//悬停显隐
content.onmouseover = function() {
clearInterval(timer)
next.style.opacity = "0.6";
prev.style.opacity = "0.6";
}
content.onmouseout = function() {
timer = setInterval(function(){
next.onclick()
},3500)
next.style.opacity = "0";
prev.style.opacity = "0";
}
//动态创建圆点指示器
function createLi() {
for(let i=0; i<wrapper.children.length-1; i++) {
let li = document.createElement("li")
radius.appendChild(li)
}
radius.children[0].className='radius-active'
}
createLi()
//指示器响应
function cirAction(wrapIndex) {
for(let i=0; i<radius.children.length; i++) {
radius.children[i].classList.remove("radius-active")
}
if(wrapIndex === wrapper.children.length-1) {
radius.children[0].className='radius-active'
}
else {
radius.children[wrapIndex].className='radius-active'
}
}
//指示器控制
function cirMouse() {
for(let i=0; i<radius.children.length; i++) {
radius.children[i].onmouseover = function() {
clearInterval(timer);
animate(wrapper, -i*imgWidth);
wrapIndex = i;
cirAction(wrapIndex)
}
}
}
cirMouse()
//滑动动画
function animate(el, target) {
clearInterval(el.timer)
el.timer = setInterval(function(){
let move = 8;
let present = wrapper.offsetLeft;
move = present > target ? -move : move;
present += move;
if(Math.abs(present-target) > Math.abs(move)) {
wrapper.style.left = present + 'px'
}
else {
clearInterval(el.timer);
wrapper.style.left = target + 'px'
}
},16)
}
//next控制
next.onclick = function() {
if(wrapIndex === wrapper.children.length-1){
wrapIndex = 0;
wrapper.style.left = 0 + 'px';
}
wrapIndex++;
animate(wrapper,-wrapIndex*imgWidth);
cirAction(wrapIndex);
}
//prev控制
prev.onclick = function() {
if(wrapIndex === 0){
wrapIndex = wrapper.children.length-1;
wrapper.style.left = -wrapIndex*imgWidth + 'px';
}
wrapIndex--;
animate(wrapper,-wrapIndex*imgWidth);
cirAction(wrapIndex)
}
//自动滑动
var timer = setInterval(function(){
next.onclick()
},3500)
}
</script>
<style>
* {
margin: 0;
padding: 0;
}
.content {
width: 300px;
height: 300px;
position: relative;
overflow: hidden;
margin: 0 auto;
}
.wrapper {
width: 400%;
height: 100%;
margin: 0;
padding: 0;
position: absolute;
top: 0;
left: 0;
display: flex;
transition: none;
}
.wrapper li {
flex: 1;
list-style: none;
margin: 0;
padding: 0;
}
.wrapper li img {
width: 100%;
height: 100%;
}
.radius {
height: 12px;
margin: 0;
padding: 0;
position: absolute;
bottom: 10px;
left: 10px;
display: flex;
align-items: center;
}
.radius li {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: white;
opacity: 0.6;
margin: 0 3px;
padding: 0;
list-style: none;
}
.radius-active {
opacity: 1 !important;
border: 2px solid rgb(255, 255, 255, 0.5);
background-clip: padding-box;
}
.prev {
width: 23px;
line-height: 34px;
text-align: center;
position: absolute;
left: 0;
top: 50%;
margin-top: -17px;
background-color: darkgray;
opacity: 0;
}
.next {
width: 23px;
line-height: 34px;
text-align: center;
position: absolute;
right: 0;
top: 50%;
margin-top: -17px;
background-color: darkgray;
opacity: 0;
}
.prev span, .next span {
font-weight: bold;
color: white;
font-size: 18px;
}
</style>
</html>