相信我,这是你能找到的,除了模板之外,纯手打的最强轮播图源码。
不论B站,还是imooc,除了模板我干不过(他们太专业了)。
我还是很自信吊打其他的轮播图组件的。
首先我的轮播图没有任何BUG,不论你怎么按,怎么玩,都不会有问题。
然后我给轮播图增加了一个进度条控件,然后追加了一些小细节作为彩蛋。
1、连续快速点击左右键,会进入快速切图模式(就是CSS3的动画不一样了)
2、鼠标放在指示器上,读条会停止。
3、没有使用任何定时器或者计时器一类的工具,我用两个回调函数设计了一个死循环。
4、因为第三点的存在,所以不论你切出浏览器,还是tab到其他标签页,过多久回来,我的轮播图依然能够正常运行。
5、点击指示器时,切图效果与快速浏览模式不同(就是CSS3的动画不一样了)。
6、用到了锁的概念,确保轮播图的稳定运行。
7、用到了三个算法,对z-index的排序进行纠正,所以哪怕遇到BUG,也可以自动修复。
8、CSS代码用的是LESS,如果要追加图片可以修改一下循环的参数即可。
然后HTML标签中的ID属性和CLASS属性命名按照规范书写,既可实现添加和删减图片。
是不是心动了呀,那就上代码了哟~
图就不发了,太晚了,搞不动了。
转载需注明,使用无需授权,但因使用遇到BUG造成的损失概不负责。(尽管我很自信没有BUG,但是保不齐)
结构:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>轮播图</title>
<script type="text/javascript" src="./js/jquery.js"></script>
<link rel="stylesheet" href="./css/carousel-figure.css">
<link rel="stylesheet" href="./font/font.css">
</head>
<body>
<div class="carousel-figure-container">
<ul class="carousel-content-list" id="carousel-content-list">
<li><a><img src="images/01.jpg" alt=""></a></li>
<li><a><img src="images/02.jpg" alt=""></a></li>
<li><a><img src="images/03.jpg" alt=""></a></li>
<li><a><img src="images/04.jpg" alt=""></a></li>
<li><a><img src="images/05.jpg" alt=""></a></li>
<li><a><img src="images/06.jpg" alt=""></a></li>
</ul>
<div class="carousel-previous-button" id="carousel-previous-button">
<i class="iconfont"></i>
</div>
<div class="carousel-next-button" id="carousel-next-button">
<i class="iconfont"></i>
</div>
<ul class="carousel-pointer-list" id="carousel-pointer-list">
<li id="carousel-pointer-0"><div class="carousel-progress" id="carousel-progress-0"></div></li>
<li id="carousel-pointer-1"><div class="carousel-progress" id="carousel-progress-1"></div></li>
<li id="carousel-pointer-2"><div class="carousel-progress" id="carousel-progress-2"></div></li>
<li id="carousel-pointer-3"><div class="carousel-progress" id="carousel-progress-3"></div></li>
<li id="carousel-pointer-4"><div class="carousel-progress" id="carousel-progress-4"></div></li>
<li id="carousel-pointer-5"><div class="carousel-progress" id="carousel-progress-5"></div></li>
</ul>
</div>
<script src="js/carousel_figure.js"></script>
</body>
</html>
样式(LESS):
@import "./mixin/carousel-figure.less";
* {
font-size: 12px;
//margin: 0;
//border: none;
//padding: 0;
}
body {
margin: 0;
height: 100vh;
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
/* 轮播图盒子 */
.carousel-figure-container {
@box_width: 1226px;
width: @box_width;
height: 460px;
position: relative;
//border: 1px red solid;
//行内元素居中 主要针对 ul2
text-align: center;
//margin-top: 100px;
margin-left: auto;
margin-right: auto;
/* 轮播图内容组 */
.carousel-content-list {
width: 100%;
height: 100%;
position: relative;
z-index: 1;
//overflow: visible !important;
&>li {
position: absolute;
top: 0;
left: 0;
/* 层级 */
&:first-child {
z-index: 6;
}
&:nth-child(2) {
z-index: 5;
}
&:nth-child(3) {
z-index: 4;
}
&:nth-child(4) {
z-index: 3;
}
&:nth-child(5) {
z-index: 2;
}
&:nth-child(6) {
z-index: 1;
}
// 图片上浮
&.float-upward {
animation: float-upward 1s;
}
// 快速浮现
&.float-upward-quick {
animation: float-upward-quick 500ms;
}
// 点击浮现
&.float-for-click {
animation: float-for-click 100ms;
}
/* a标签 */
a {
/* 图组 */
img {
width: 100%;
height: 100%;
}
}
}
}
/* 后退按钮 */
.carousel-previous-button {
.center(35px, 70px, #00000050);
left: 0;
-webkit-border-radius: 0 5px 5px 0;
-moz-border-radius: 0 5px 5px 0;
border-radius: 0 5px 5px 0;
}
/* 前进按钮 */
.carousel-next-button {
.center(35px, 70px, #00000050);
right: 0;
-webkit-border-radius: 5px 0 0 5px;
-moz-border-radius: 5px 0 0 5px;
border-radius: 5px 0 0 5px;
}
/* 指示器组 */
.carousel-pointer-list {
position: relative;
bottom: 60px;
display: inline-block;
z-index: 999;
&>li {
width: 60px;
height: 6px;
background: rgba(255,255,255,0.6);
//opacity:.6;
text-align: center;
line-height: 20px;
float: left;
cursor: pointer;
// 指示器 click 样式
&.selected {
background-color: white;
}
// 指示器悬停背景
&:hover {
background-color: white;
}
// 进度条停止
.carousel-progress:hover {
animation-play-state: paused;
}
// 进度条播放样式
div.progress-play {
height: 100%;
background: #e74c3c;
-webkit-animation: countdown 3s linear;
-o-animation: countdown 3s linear;
animation: countdown 3s linear;
pointer-events: none;
}
}
&>li:not(:last-child) {
// 间距
margin-right: 10px;
}
}
}
// 关键帧
// 主图浮现
@keyframes float-upward {
100% {
transform: scale(1);
opacity: 0;
}
}
// 主图快速浮现
@keyframes float-upward-quick {
100% {
transform: scale(1.05);
opacity: 0;
}
}
// 主图点击浮现
@keyframes float-for-click {
100% {
transform: scale(.9);
opacity: 0;
}
}
// 进度条倒计时
@-webkit-keyframes countdown{
0% {
width: 0;
}
90% {
opacity: .8;
}
100% {
width: 100%;
opacity: .6;
}
}
@pointer-columns: 6;
.center(@w, @h, @c) {
position: absolute;
top: 0;
//right: 0;
bottom: 0;
//left: 0;
margin: auto;
width: @w;
height: @h;
background-color: @c;
color: #fff;
font-size: 27px;
text-align: center;
line-height: 70px;
z-index: 9999;
-moz-user-select: none; /*火狐*/
-webkit-user-select: none; /*webkit浏览器*/
-ms-user-select: none; /*IE10*/
user-select: none;
cursor: pointer;
}
.make-carousel-pointer() {
.pointer(@index) { // initial 初始化
@item: ~".carousel-pointer-@{index}:hover";
.pointer((@index + 1), @item);
}
.pointer(@index, @list) when (@index =< @pointer-columns) {
@item: ~".carousel-pointer-@{index}:hover";
.pointer((@index + 1), ~"@{list}, @{item}");
}
.pointer(@index, @list) when (@index > @pointer-columns) {
@{list} {
background: white;
}
}
.pointer(0); // 根据你的需要修改
}
const images = $("#carousel-content-list").children();
const pointer_dock_list = $("#carousel-pointer-list").children();
const progress_list = pointer_dock_list.children();
// 当前图片和进度条下标(数值统一)
let current_index = 0;
// 图片数组长度
let length = images.length;
// 是否在播放
let isPlay = false;
// 上一次按键时间
let older = 0;
// 是否快速模式
let isQuickModel = false;
// 模式 1 下一张 2 上一张 3 跳跃 默认下一张
let model = 1;
// 上一张图片
let jumpBeforeIndex;
// 页面打开执行
setTimeout(function(){
addProgressStyle(0);
}, 0);
// 进度条样式
let addProgressStyle = (index) => {
// 进度条动画
$(getPointerDock(index)).addClass("selected");
$(getPointerProgress(index)).addClass("progress-play");
};
// 获取指示器底座
let getPointerDock = (index) => {
return "#carousel-pointer-" + index;
};
// 获取指示器读条
let getPointerProgress = (index) => {
return "#carousel-progress-" + index;
};
// 移除进度条样式
let removeProgressStyle = (index) => {
$(getPointerDock(index)).removeClass("selected");
$(getPointerProgress(index)).removeClass("progress-play");
};
// 播放下一个进度条
let playProgressOfNext = () => {
addProgressStyle(current_index);
};
// 中断当前进度条 更安全
let breakProgressOfAll = () => {
// 撤销所有的正在播放的动画
for (let i=0; i<length; i++){
removeProgressStyle(i);
}
};
// 中断当前进度条 不推荐
let breakProgressOfCurrent = (index) => {
if (index === undefined) {
index = current_index;
}
removeProgressStyle(index);
};
// 点击 播放下一个
let clickNext = (quick) => {
// 当前元素执行动画
$(images[current_index]).addClass(quick ? 'float-upward-quick' : 'float-upward');
};
// 指定更新下标
let updateIndex = (value) => {
if (value === undefined || value < 0 || value > length-1) return;
current_index = value;
};
// 更新下标 递增
let updateIncreaseIndex = () => {
if (current_index + 1 === images.length) {
current_index = 0;
} else {
++current_index;
}
};
// 更新下标 递增
let updatePreviousIndex = () => {
if (current_index - 1 < 0) {
current_index = images.length - 1;
} else {
--current_index;
}
};
// 获取上一张index 不影响 current
let getPreviousIndex = () => {
let index;
index = current_index - 1;
if (index < 0){
index = length - 1;
}
return index;
};
// 获取下一张index 不影响 current
let getNextIndex = (temp_index) => {
let index;
if (temp_index !== undefined) {
index = temp_index + 1;
} else {
index = current_index + 1;
}
return index >= images.length ? 0 : index;
};
// 立刻提升图片层级
// 参数:当前下标,目标下标
// old的下个索引 与 点击的进行交换
let immediatelyUpLevel = (old_index, target_index) => {
// 获取下一个元素索引
let nextIndex = getNextIndex(old_index);
// 如果下一个元素与目标元素一致不用交换
if(nextIndex === target_index) return;
// 下个元素的 z-index
let next_z_index = $(images[nextIndex]).css('z-index');
images[nextIndex].style.cssText = "z-index:" + parseInt($(images[target_index]).css('z-index')) + ";";
images[target_index].style.cssText = "z-index:" + next_z_index + ";";
};
// 点击 播放上一个
let clickPrevious = (quick) => {
// 获取前一个元素索引
let pIndex = getPreviousIndex();
// 获取下一个元素索引
let nIndex = getNextIndex();
// 交换前后元素的层级
let temp = $(images[nIndex]).css('z-index');
images[nIndex].style.cssText = "z-index:" + parseInt($(images[pIndex]).css('z-index')) + ";";
images[pIndex].style.cssText = "z-index:" + temp + ";";
// 当前元素执行动画
$(images[current_index]).addClass(quick ? 'float-upward-quick' : 'float-upward');
};
// 检查快速点击
let check_quickly = () => {
const current = new Date();
// 记录第一次点击
if(!older) {
older = current;
return;
}
// 是否在高速模式下维持600点击
let speed = current.getTime() - older.getTime();
let flag = true;
// 是否低于 600
if (speed < 800){
// 维持高速模式
older = current;
return true;
}
if (isQuickModel && flag) {
// 结束快速模式
isQuickModel = false;
older = current;
return false;
}
// 时间在1100毫秒内且非快速模式
if (speed < 1200 && !isQuickModel){
// 进入快速模式
isQuickModel = true;
// 更新时间
older = current;
return true;
} else {
older = current;
return false;
}
};
// 获取目标的下标
let getIndexForTarget = (target)=> {
return $("#carousel-content-list li").index(target);
};
// 函数声明
// 图片切换动画回调函数
function pictureAnimationCallback(e) {
console.log("10 回调函数");
// 获取当前元素下标
let index = getIndexForTarget(e.target);
// 下标备份
let cursor = index;
// 模式判断
switch(model) {
case 1:
console.log("模式1");
// 当前元素移除动画
$(e.target).removeClass('float-upward-quick float-upward');
// 当前元素置底 立刻隐藏
e.target.style.cssText = "z-index: 0;";
// 元素重排
for (let i=0; i<images.length; i++){
if (cursor >= 0){
images[i].style.cssText = "z-index:" + cursor + ";";
cursor--;
} else {
images[i].style.cssText = "z-index:" + (images.length - i + index) + ";";
}
}
console.log("之后层级");
for (let i=0; i<images.length; i++) {
let z = $(images[i]).css('z-index');
console.log("层级列表", z);
}
// 更新标记位
updateIncreaseIndex();
break;
case 2:
console.log("模式2");
// 当前元素移除动画
$(e.target).removeClass('float-upward-quick float-upward');
// 当前元素置底 立刻隐藏
e.target.style.cssText = "z-index: 0;";
// 设置起点
let breakpoint = (index === 0 ? images.length : index);
// 设置递减
let temp = images.length;
for (let i=breakpoint; i<=images.length; i++){ // 4
images[i-1].style.cssText = "z-index:" + temp + ";";
temp--;
}
for (let i=0; i<breakpoint-1; i++){
images[i].style.cssText = "z-index:" + temp + ";";
temp--;
}
model = 1;
// 更新标记位
updatePreviousIndex();
break;
case 3:
cursor = current_index;
console.log("11 模式3回调函数");
// 当前元素移除动画
$(images[jumpBeforeIndex]).removeClass("float-for-click");
// 当前元素置底 立刻隐藏
images[jumpBeforeIndex].style.cssText = "z-index: 0;";
// 后半部分
let level = length - 1;
for (let i=cursor; i<length; i++) {
images[i].style.cssText = "z-index:" + level + ";";
level--;
}
for (let i=0; i<cursor; i++){
images[i].style.cssText = "z-index:" + level + ";";
level--;
}
model = 1;
break;
}
// 重置图片播放标记位
isPlay = false;
// 开始播放下一个进度条
playProgressOfNext();
}
(function(){
// 注册动画结束监听事件
for (let j=0; j<length; j++){
// 进度条点击函数
pointer_dock_list[j].addEventListener("click", function(e){
console.log("1 点击");
// 拒绝播放
if (isPlay) {
console.log("1.1 正在播放图片,拒绝切换");
return;
}
// 获取目标下标
let target_index = $(pointer_dock_list).index(e.target);
console.log("2 获取目标下标", target_index);
// 拒绝跳转
if (target_index === current_index){
console.log("2.1 就在本页");
return;
}
// 正在播放
isPlay = true;
console.log("3 正在播放锁");
// 暂停进度条
$("#carousel-progress-" + j).css("animation-play-state","paused");
// 中断进度条 需要 current_index
breakProgressOfAll();
console.log("4 中断所有进度条");
// 切换播放模式
model = 3;
console.log("5 设置模式");
// 备份需要切换的图片的下标
jumpBeforeIndex = current_index;
console.log("6 备份索引");
// 立刻提升目标图片层级 需要 current_index
immediatelyUpLevel(current_index, target_index);
console.log("7 提升图片层级");
// 更新下标
updateIndex(target_index);
console.log("8 更新当前下标");
// current_index 图片立刻动画
$(images[jumpBeforeIndex]).addClass("float-for-click");
console.log("8.1 jumpBeforeIndex", jumpBeforeIndex);
console.log("9 执行动画");
});
// 进度条动画回调函数
progress_list[j].addEventListener("webkitAnimationEnd", function() {
// 正在播放
isPlay = true;
// 当前图片执行动画
$(images[current_index]).addClass('float-upward');
// 移除进度条
removeProgressStyle(current_index);
});
// 主图动画回调函数
images[j].addEventListener("webkitAnimationEnd", pictureAnimationCallback);
// 进度条悬停事件
$("#carousel-pointer-" + j).hover(() => {
$("#carousel-progress-" + j).css("animation-play-state","paused");
},() => {
$("#carousel-progress-" + j).css("animation-play-state","running");
});
}
})();
$(document).ready(function(){
// 点击事件
$("#carousel-next-button").on("click", function(){
// 拒绝播放
if (isPlay) return;
// 正在播放
isPlay = true;
// 中断进度条
breakProgressOfCurrent();
// 进度条选择器
$(getPointerDock(getNextIndex())).addClass("selected");
// 检查是否是快速点击
const isQuick = check_quickly();
// 切图
clickNext(isQuick);
});
// 点击事件
$("#carousel-previous-button").on("click",function(){
// 拒绝播放
if (isPlay) return;
// 正在播放
isPlay = true;
// 修改播放顺序标记位
model = 2;
// 中断进度条
breakProgressOfCurrent();
// 进度条选择器
$(getPointerDock(getPreviousIndex())).addClass("selected");
// 检查是否是快速点击
const isQuick = check_quickly();
// 执行动画
clickPrevious(isQuick);
});
});
// ready();
// 进度条部分
// duration: 3000 只是一个测试
// $({property: 0}).animate({property: 100}, {
// duration: 3000,
// step: function() {
// let percentage = Math.round(this.property);
// $("#progress").css("width", percentage + "%");
// if (percentage == 100) {
// $("#progress").addClass("done");
// }
// }
// });
代码完整,正确配置即可顺利食用,晚安~