基於原生Javascript的無縫輪播
一、前言
- 輪播圖的種類
說起輪播圖,種類那可是五花八門,什麼淡入淡出呀、旋轉木馬呀、3D輪播呀、無縫輪播呀等等,簡直是秀的頭皮發麻~
本文要講的就是其中的無縫輪播,小夥伴們知道是怎麼實現的嗎,如果不知道的話,嘿嘿,你就接著往下看吧。如果你知道的話,就當捧個場,也往下看吧~
- 基本要求
html和css完成靜態佈局、js獲取並且操縱元素,定時器的應用,節流函式,日期物件等等。
二、無縫輪播的原理
首先,我們來看看什麼是無縫輪播,就像下圖所示:
其大概過程就是,上一張圖片從左往右(或從右往左離開),下一張圖片從左往右(或從右往左)進入。哎呀,貌似很簡單的樣子,有些小夥伴立馬就想到了一個大容器裡面有一個ul,ul裡面有若干個li並且存放圖片,把每個li的寬度設定成和容器同寬,然後每次點選的時候改變ul的left值就行了。嗯,很想很有道理的樣子,於是三下五除二就做出來了,但是卻發現有點問題,就像下面這樣:
從第一張到倒數第二張圖片還好,但是從最後一張圖片到第一張圖片就有問題了,並沒有從最後一張圖片直接切換到第一張圖片,而是經過了中間的圖片慢慢過渡到第一張圖片(第一張圖片到最後一張圖片也同理),這顯然不是預期的效果。問題既然已經發現了,就要去解決它,那麼如何才能達到從最後一張圖片直接切換到第一張圖片(或第一張到最後一張),也就是無縫的效果,答案很簡單,我們需要多一張圖片(這張圖片和第一張圖片是相同的)放在圖片列表的最後!為什麼需要這樣一張圖片,請往下看。
我們在上面已經清楚要解決的就是首尾圖片間的切換問題,所以在引入多一張圖片之後,可以讓輪播從倒數第二張圖片(在沒有引入圖片之前的最後一張圖片)切換到最後一張圖片(新引入的圖片)的動畫完成之後,再瞬間跳轉到第一張圖片,請看下圖(我把外面容器的overflow:hidden去掉了以便大家理解):
- 佈局
- 動圖演示輪播過程
- 把overflow:hidden加上,利用視覺差之後
嘿嘿,是不是很神奇,多引入了一張圖片我們就把剛才的問題解決了,至於從第一張到最後一張圖片的切換,把剛才的過程倒過來就行了。
三、無縫輪播演示
- 這裡我用到了節流函式,如果你還不知道什麼是節流函式的話,可以點選下方連結去我另外一篇文章檢視。
- 程式碼部分
/* css程式碼 */
body{
background-color: #333;
}
ul{
position: absolute;
left: 0;
list-style: none ;
padding: 0;
}
.wrap{
overflow: hidden;
position: relative;
width: 700px;
height: 450px;
margin: 100px auto 0;
}
.wrap .btn{
position: absolute;
top: 50%;
z-index: 1;
width: 50px;
height: 80px;
margin-top: -40px;
background-color: rgba(0,0,0,.5);
color: #fff;
text-align: center;
line-height: 80px;
cursor: pointer;
}
.wrap .left{
left: 0;
}
.wrap .right{
right: 0;
}
.img-list{
top: 0;
margin: 0;
width: 500%;
height: 100%;
}
.img-list li{
float: left;
width: 700px;
height: 100%;
}
.img-list li:nth-of-type(1){
background: url("images/01.jpg") no-repeat center/cover;
}
.img-list li:nth-of-type(2){
background: url("images/02.png") no-repeat center/cover;
}
.img-list li:nth-of-type(3){
background: url("images/03.png") no-repeat center/cover;
}
.img-list li:nth-of-type(4){
background: url("images/04.png") no-repeat center/cover;
}
.img-list li:nth-of-type(5){
background: url("images/01.jpg") no-repeat center/cover;
}
.tab-list{
right: 0;
bottom: 10px;
width: 100px;
margin: auto;
}
.tab-list:after{
content: "";
display: block;
clear: both;
}
.tab-list li{
float: left;
transition: 1s;
width: 15px;
height: 15px;
margin-left: 5px;
background-color: #bbb;
border-radius: 50%;
}
.tab-list li:hover{
cursor: pointer;
}
.tab-list li:first-child{
margin-left: 0;
}
.tab-list .on{
width: 40px;
border-radius: 8px;
}
<!-- html程式碼 -->
<div class="wrap">
<div class="btn left"><</div>
<div class="btn right">></div>
<ul class="img-list">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<ul class="tab-list">
<li class="on"></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
//js程式碼
(function(){
var oImgList = document.getElementsByClassName("img-list")[0],
aButton = document.getElementsByClassName("btn"),
aImgLi = document.querySelectorAll(".img-list li"),
oWidth = parseFloat(getComputedStyle(aImgLi[0]).width),
oWrap = document.getElementsByClassName("wrap")[0],
aTab = document.querySelectorAll(".tab-list li");
len = aImgLi.length,
index = 0;
function throttle(fn,time){
var startTime = new Date();
return function(){
var time_ = (new Date() - startTime) >= time;
if(time_){
fn.apply(this);
startTime = new Date();
}
}
}
function btnTab(){
var t = new Date();
for(var i = 0,tabLen = aTab.length;i < tabLen;i++){
(function(i){
aTab[i].onclick = function(){
if(new Date() - t >= 1000){
aTab[index].className = "";
if((i - index) === (tabLen - 1)){
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth*(len-1) + "px";
index = len - 2;
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = -oWidth*(index) + "px";
},1000/60);
}
else if((i - index) === (1 - tabLen)){
oImgList.style.left = -oWidth*(len - 1) + "px";
index = 0;
setTimeout(function(){
oImgList.style.transition = 0 + "s";
oImgList.style.left = index + "px";
},1000);
}
else{
oImgList.style.left = -oWidth*(i) + "px";
oImgList.style.transition = 1 + "s";
}
index = i;
this.className = "on";
t = new Date();
}
}
})(i);
}
}
function btnPre(){
index--;
if(index < 0){
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth*(len-1) + "px";
aTab[0].className = "";
index = len - 2;
aTab[index].className = "on";
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = -oWidth*(index) + "px";
},1000/60);
}
else{
oImgList.style.transition = 1 + "s";
oImgList.style.left = -oWidth*(index) + "px";
aTab[index + 1].className = "";
aTab[index].className = "on";
}
}
function btnNext(){
index++;
oImgList.style.transition = 1 + "s";
if(index === len-1){
oImgList.style.left = -oWidth*index + "px";
aTab[len - 2].className = "";
index = 0;
aTab[index].className = "on";
setTimeout(function(){
oImgList.style.transition = 0 + "s";
oImgList.style.left = index + "px";
},1000);
}
else{
oImgList.style.left = -oWidth*index + "px";
aTab[index - 1].className = "";
aTab[index].className = "on";
}
}
aButton[0].onclick = throttle(btnPre,1000);
aButton[1].onclick = throttle(btnNext,1000);
btnTab();
var timer = setInterval(btnNext,5000);
oWrap.onmouseover = function(){
clearInterval(timer);
}
oWrap.onmouseout = function(){
timer = setInterval(btnNext,5000);
}
})();
- demo演示
四、無縫輪播的改進
在上面的無縫輪播中,可以發現對於不是相鄰的兩張圖片的切換也並不是直接切換的,而是會過渡中間的圖片再切換到目標圖片,如下圖:
解決辦法就是把除了當前顯示的圖片全都設定成display:none,這樣做的好處就是display:none的元素不佔據位置!比如說從第一張切換到第三張,因為第二張是display:none,第三章需要顯示所以display:block,由於第二張圖片不佔據位置的原因第三張圖片會在第一張圖片之後,因此就達到了從第一張直接切換到第三張的效果。
- 動圖演示(沒有overflow:hidden)
(有overflow:hidden)
- 改進後的程式碼
/* css程式碼 */
body{
background-color: #333;
}
ul{
position: absolute;
left: 0;
list-style: none;
padding: 0;
}
.wrap{
overflow: hidden;
position: relative;
width: 700px;
height: 450px;
margin: 100px auto 0;
}
.wrap .btn{
position: absolute;
top: 50%;
z-index: 1;
width: 50px;
height: 80px;
margin-top: -40px;
background-color: rgba(0,0,0,.5);
color: #fff;
text-align: center;
line-height: 80px;
cursor: pointer;
}
.wrap .left{
left: 0;
}
.wrap .right{
right: 0;
}
.img-list{
top: 0;
margin: 0;
width: 500%;
height: 100%;
}
.img-list li{
display: none;
float: left;
width: 700px;
height: 100%;
}
.img-list .active{
display: block;
}
.img-list li:nth-of-type(1){
background: url("images/01.jpg") no-repeat center/cover;
}
.img-list li:nth-of-type(2){
background: url("images/02.png") no-repeat center/cover;
}
.img-list li:nth-of-type(3){
background: url("images/03.png") no-repeat center/cover;
}
.img-list li:nth-of-type(4){
background: url("images/04.png") no-repeat center/cover;
}
.img-list li:nth-of-type(5){
background: url("images/01.jpg") no-repeat center/cover;
}
.tab-list{
right: 0;
bottom: 10px;
width: 100px;
margin: auto;
}
.tab-list:after{
content: "";
display: block;
clear: both;
}
.tab-list li{
float: left;
transition: 1s;
width: 15px;
height: 15px;
margin-left: 5px;
background-color: #bbb;
border-radius: 50%;
}
.tab-list li:hover{
cursor: pointer;
}
.tab-list li:first-child{
margin-left: 0;
}
.tab-list .on{
width: 40px;
border-radius: 8px;
}
<!-- html程式碼 -->
<div class="wrap">
<div class="btn left"><</div>
<div class="btn right">></div>
<ul class="img-list">
<li class="active"></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<ul class="tab-list">
<li class="on"></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
//js程式碼
(function(){
var oImgList = document.getElementsByClassName("img-list")[0],
aButton = document.getElementsByClassName("btn"),
aImgLi = document.querySelectorAll(".img-list li"),
oWidth = parseFloat(getComputedStyle(aImgLi[0]).width),
oWrap = document.getElementsByClassName("wrap")[0],
aTab = document.querySelectorAll(".tab-list li");
len = aImgLi.length,
index = 0,
index_ = 0,
count = 0;;
function throttle(fn,time){
var startTime = new Date();
return function(){
var time_ = (((new Date() - startTime) >= time) && (count === index));
if(time_){
fn.apply(this);
startTime = new Date();
setTimeout(function(){
count = index;
},1000);
}
}
}
function btnTab(){
var t = new Date(),
direction;
for(var i = 0,tabLen = aTab.length;i < tabLen;i++){
(function(i){
aTab[i].onclick = function(){
if((new Date() - t >= 1000) && (count === index)){
index_ = index;
i - index > 0 ? direction = true : direction = false;
if(this.className !== "on"){
aTab[index].className = "";
if((i - index) === (tabLen - 1)){
aImgLi[len - 1].className = "active";
aImgLi[0].className = "";
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth + "px";
aTab[0].className = "";
index = len - 2;
aImgLi[index].className = "active";
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = 0 + "px";
},1000/60);
setTimeout(function(){
aImgLi[len - 1].className = "";
},1000);
}
else if((i - index) === (1 - tabLen)){
oImgList.style.transition = 1 + "s";
oImgList.style.left = -oWidth + "px";
aTab[len - 2].className = "";
aImgLi[len - 1].className = "active";
index = 0;
aTab[index].className = "on";
setTimeout(function(){
oImgList.style.transition = 0 + "s";
oImgList.style.left = index + "px";
aImgLi[index].className = "active";
aImgLi[len-2].className = "";
aImgLi[len-1].className = "";
},1000);
}
else{
if(direction){
oImgList.style.left = -oWidth + "px";
oImgList.style.transition = 1 + "s";
setTimeout(function(){
aImgLi[index_].className = "";
oImgList.style.left = 0 + "px";
oImgList.style.transition = 0 + "s";
},1000);
}
else{
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth + "px";
aImgLi[index].className = "active";
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = 0 + "px";
},1000/60);
setTimeout(function(){
aImgLi[index_].className = "";
},1000);
}
index = i;
aImgLi[index].className = "active";
}
this.className = "on";
t = new Date();
setTimeout(function(){
count = index;
},1000);
}
}
}
})(i);
}
}
function btnPre(){
index--;
if(index < 0){
aImgLi[len - 1].className = "active";
aImgLi[0].className = "";
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth + "px";
aTab[0].className = "";
index = len - 2;
aImgLi[index].className = "active";
aTab[index].className = "on";
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = 0 + "px";
},1000/60);
setTimeout(function(){
aImgLi[len - 1].className = "";
},1000);
}
else{
oImgList.style.transition = 0 + "s";
oImgList.style.left = -oWidth + "px";
aTab[index + 1].className = "";
aTab[index].className = "on";
aImgLi[index].className = "active";
setTimeout(function(){
oImgList.style.transition = 1 + "s";
oImgList.style.left = 0 + "px";
},1000/60);
setTimeout(function(){
aImgLi[index + 1].className = "";
},1000);
}
}
function btnNext(){
index++;
oImgList.style.transition = 1 + "s";
if(index === len-1){
oImgList.style.left = -oWidth + "px";
aTab[len - 2].className = "";
aImgLi[index].className = "active";
index = 0;
aTab[index].className = "on";
setTimeout(function(){
oImgList.style.transition = 0 + "s";
oImgList.style.left = index + "px";
aImgLi[index].className = "active";
aImgLi[len-2].className = "";
aImgLi[len-1].className = "";
},1000);
}
else{
oImgList.style.left = -oWidth + "px";
aTab[index - 1].className = "";
aTab[index].className = "on";
aImgLi[index].className = "active";
setTimeout(function(){
oImgList.style.transition = 0 + "s";
aImgLi[index - 1].className = "";
oImgList.style.left = 0 + "px";
},1000);
}
setTimeout(function(){
count = index;
},1000);
}
aButton[0].onclick = throttle(btnPre,1000);
aButton[1].onclick = throttle(btnNext,1000);
btnTab();
var timer = setInterval(btnNext,5000);
oWrap.onmouseover = function(){
clearInterval(timer);
}
oWrap.onmouseout = function(){
timer = setInterval(btnNext,5000);
}
})();
- 改進後的無縫輪播demo演示
- 分享一個我自己做的全屏無縫輪播(更新於2018.8.27晚21點)
這個無縫輪播我做了一些處理,也就是隻有每個動畫完成了下一個選項卡點選事件或者左右兩邊的按鈕點選事件才會觸發,這樣做也是符合業務邏輯,對了,我在裡面添加了背景音樂,右上角有個播放按鈕,點選可以關閉音樂哈~
GTAV–全屏無縫輪播(有背景音樂)
GTAV–全屏無縫輪播(無背景音樂)
五、結束語
本文僅僅提到了無縫輪播,其實還有很多優秀的輪播效果,但是由於目前能力有限,還駕馭不了~所以就到這裡啦,總之任重而道遠,最後感謝閱讀咯~