剛剛的輪播用具體思維做,因為不知道它有哪幾種狀態,就一步步來做,等效果做出來後,哪幾種狀態,一目瞭然。下面就用抽象思維做一遍.
用抽象思維做
初始化CSS樣式
*{
margin:0;
padding:0;
box-sizing:border-box;
}
.window{
width:400px;
height:300px;
overflow:hidden;
margin:20px auto
}
.images{
position:relative;
}
.images > img{
position:absolute;
transition:all 0.5s;
width:100%;
top:0;
}
輪播狀態
先來看下這個輪播有那幾種狀態
- 圖片出現在視窗狀態,我用 current 表示
- 圖片離開視窗狀態,我用 leave 表示
- 圖片準備進入視窗狀態,我用 enter 表示
現在就是要寫三個類,通過JS 啟用class 來實現輪播
.images > img.current{
transform:translateX(0);
z-index:1;
}
.images > img.leave{
transform:translateX(-100%);
z-index:1;
}
.images > img.enter{
transform:translateX(100%);
}
梳理下每張圖片的狀態
- 初始化每張圖片的位置,圖片1 出現在當前視窗
current
,圖片2、圖片3 應該在視窗右邊待命,隨時準備進入視窗enter
- 當圖片1 離開視窗時
leave
,圖片2 進入視窗current
- 當上一步全部完成後,圖片1 應該進入右邊待命,等待著進入視窗
- 這裡主要絕對定位後,會觸發BFC
$(`#images > img:nth-child(1)`).addClass(`current`);
$(`#images > img:nth-child(2)`).addClass(`enter`);
$(`#images > img:nth-child(3)`).addClass(`enter`);
setTimeout(function(){
$(`#images > img:nth-child(1)`).removeClass(`current`).addClass(`leave`).one(`transitionend`,function(e){
$(e.currentTarget).addClass(`enter`).removeClass(`leave`)
});
$(`#images > img:nth-child(2)`).removeClass(`enter`).addClass(`current`)
},3000);
setTimeout(function(){
$(`#images > img:nth-child(2)`).removeClass(`current`).addClass(`leave`).one(`transitionend`,function(e){
$(e.currentTarget).addClass(`enter`).removeClass(`leave`)
});
$(`#images > img:nth-child(3)`).removeClass(`enter`).addClass(`current`)
},6000);
setTimeout(function(){
$(`#images > img:nth-child(3)`).removeClass(`current`).addClass(`leave`).one(`transitionend`,function(e){
$(e.currentTarget).addClass(`enter`).removeClass(`leave`)
});
$(`#images > img:nth-child(1)`).removeClass(`enter`).addClass(`current`)
},9000);
這樣一輪迴圈就結束了,可以在往後新增setTimeout
方法。
無限迴圈下去
大量重複的程式碼就需要尋找合適的的API 代替,一直播下去我們可以使用DOM APIsetInterval()
$(`#images > img:nth-child(1)`).addClass(`current`);
$(`#images > img:nth-child(2)`).addClass(`enter`);
$(`#images > img:nth-child(3)`).addClass(`enter`);
let n = 1;
setInterval(function(){
$(`#images > img:nth-child(${x(n)})`).removeClass(`current`).addClass(`leave`).one(`transitionend`,function(e){
$(e.currentTarget).addClass(`enter`).removeClass(`leave`)
});
$(`#images > img:nth-child(${x(n+1)})`).removeClass(`enter`).addClass(`current`)
n++; //這裡n 是自然增長,讓它一直玄幻下去
},3000)
//n取值應該是[1,2,3,4,5,...,size]
let allImages = $(`#images > img`);
let size = allImages.length;
function x(n){
if(n > size){ //如果n 大於節點size,n就取餘
n = n%size;
if(n === 0){ //如果n 取餘為0,則讓n等於size
n = size;
}
}
return n;
}
這樣就是實現了無縫輪播,上面用到了ES6的插值法。
在CSS中img:nth-child(n)
是沒有這種寫法的,它不能像JS一樣可以用變數,這邊就用到了ES6 的插值法
`img:nth-child(${n})`
最後優化下剛剛寫的程式碼
<style>
*{
margin:0;
padding:0;
box-sizing:border-box;
}
.window{
width:400px;
height:300px;
overflow:hidden;
margin:20px auto
}
.images{
position:relative;
}
.images > img{
position:absolute;
transition:all 0.5s;
width:100%;
top:0;
}
<style>
<div class="window">
<div id="images" class="images">
<img class=`png1` src="./images/1.png" width=400 alt="">
<img class=`png2` src="./images/2.png" width=400 alt="">
<img class=`png3` src="./images/3.png" width=400 alt="">
<img class=`png4` src="./images/4.png" width=400 alt="">
<img class=`png5` src="./images/5.png" width=400 alt="">
</div>
</div>
<script>
let n = 1;
int();
setInterval(function(){
makeLeave(getImage(n)).one(`transitionend`,function(e){
makeEnter($(e.currentTarget))
});
makeCurrent(getImage(n+1));
n++; //這裡n 是自然增長,讓它一直迴圈下去
},1000);
//n取值應該是[1,2,3,4,5,...,size]
let allImages = $(`#images > img`);
let size = allImages.length;
function x(n){
if(n > size){ //如果n 大於節點size,n就取餘
n = n%size;
if(n === 0){ //如果n 取餘為0,則讓n等於size
n = size;
}
}
return n;
}
function getImage(n){
return $(`#images > img:nth-child(${x(n)})`)
}
function int(){
$(`#images > img:nth-child(${n})`).addClass(`current`).siblings().addClass(`enter`);
}
function makeLeave($node){
return $node.removeClass(`current enter`).addClass(`leave`)
}
function makeCurrent($node){
return $node.removeClass(`enter leave`).addClass(`current`)
}
function makeEnter($node){
return $node.addClass(`enter`).removeClass(`leave current`)
}
</script>
優化完了之後,實際程式碼就只有這麼多,這個被稱為狀態機,現在再看輪播後,腦海裡已經自動變成了狀態機了。
let n = 1;
int();
setInterval(function(){
makeLeave(getImage(n)).one(`transitionend`,function(e){
makeEnter($(e.currentTarget))
});
makeCurrent(getImage(n+1));
n++; //這裡n 是自然增長,讓它一直迴圈下去
},1000);
這裡我遇到一個最大的問題之前,是用setTimeout()
方法寫的程式碼,後面做無限迴圈時沒想到用setInterval()
方法,怎麼除錯都不對,這裡看下它們兩個的區別:setTimeout()
方法設定一個定時器,在到時間後執行一段程式碼或者函式setInterval()
方法是重複呼叫一段程式碼或者函式,每次呼叫之間有固定的時間延時
我們上面寫在setInterval()
方法內的函式其實就是一段固定的程式碼,每個一段時間執行一次,就變成我們看到的輪播了
總結
這一篇的核心是狀態機,把動作變成一個個狀態。用具體化寫出的程式碼都是執行的動作,而用抽象化寫出的程式碼都是完成後的狀態,程式碼結構更清晰,更美觀。當然要能寫抽象化的程式碼,肯定少不了具體化的思維。
用這種方法最大的好處是行為樣式分離,如果我要給變輪播的方向,只需要改變CSS中的移動方向即可,還可以根據需要加上一些酷炫的操作。