使用Vue怎么制作一個圖片輪播組件?針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),薊州企業(yè)網(wǎng)站建設(shè),薊州品牌網(wǎng)站建設(shè),網(wǎng)站定制,薊州網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,薊州網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
一、理清思路,理解需求和原理
1. 要寫一個什么樣的輪播?
在點(diǎn)擊右側(cè)箭頭時,圖片向左滑動到下一張;點(diǎn)擊左側(cè)箭頭時,圖片向右滑到下一張
點(diǎn)擊下面的小圓點(diǎn),滑到對應(yīng)的圖片,相應(yīng)小圓點(diǎn)的樣式也發(fā)生改變
要有過渡效果,要緩緩滑動過去
當(dāng)鼠標(biāo)hover到圖片上時,輪播暫停,當(dāng)鼠標(biāo)leave時,輪播繼續(xù)
自動播放功能
無限滾動,即在滾動到最后一張時,再點(diǎn)擊下一張時會繼續(xù)向左滑動到第一張,而不是整個拉到第一張,這里有點(diǎn)難
2. 理解無限輪播的原理
我們先看下原理圖:
圖中紅線區(qū)域即是我們看到的圖片,這個輪播 只展示5張圖片 ,但是在它的首尾各還有兩張圖片,在圖1前面放置了圖5,在圖5后面放置了圖1,之所以這么做,是為了做無限滾動。 無限滾動的原理在于:當(dāng)整個圖向左側(cè)滾動到右邊的圖5時,會繼續(xù)向前走到圖1,在完全顯示出圖1后,會以肉眼看不到的速度向右側(cè)拉回到最左邊的圖1。 這樣,即使再向左側(cè)滑動看到的就是圖2了。
如下圖:在最后的圖1完成過渡完全顯示出來后,再將整個列表瞬間向右拉到左側(cè)的圖1。另一張邊界圖圖5的滾動也是,不過方向相反。
二、先讓圖片切換起來
1. 布局和準(zhǔn)備
// window上圖中紅線框//注意這里的:style //這是圖片列表,排成一排
- //列表最前面的輔助圖,它和圖5一樣,用于無限滾動
- //通過v-for渲染的需要展示的5張圖
- //列表最后面的輔助圖,它和圖1一樣,用于無限滾動
//兩側(cè)的箭頭
//下面的小圓點(diǎn)
好了,布局大概就是這樣,效果圖如下:
上面的代碼已經(jīng)做了注釋,有幾個點(diǎn)在這里再提一下:
window是紅線框, 寬度為600px ,它不會動,移動的是包裹著圖片的container,它的移動方式用 : ,這是一個計(jì)算屬性,用 transform:translate3d(${this.distance, 0, 0}) 來控制左右移動
data里的 distance 和 currentIndex 是關(guān)鍵, distance 控制著移動的距離,默認(rèn)是-600,顯示7張圖片中的第二張,也就是圖1。 currentIndex 是window顯示的圖片的索引,這里默認(rèn)是1,也是7張圖片中第2張。
需要展示的只有5張圖片,但是在圖1前了一張圖5、在圖5后面放了一張圖1來做無限滾動,原理前面說過了
當(dāng)點(diǎn)擊右側(cè)的箭頭,container向左移動, distance 會越來越??;當(dāng)點(diǎn)擊左側(cè)的箭頭,container向右移動, distance 會越來越大,方向不要弄錯
2. 圖片切換
我們在左側(cè)和右側(cè)的箭頭上添加點(diǎn)擊事件:
解釋下上面的代碼:點(diǎn)擊左側(cè)或者右側(cè)的箭頭,調(diào)用move函數(shù),move接收偏移量offset和方向direction兩個參數(shù)。direction只傳兩個值,1表示container向右移動,-1表示container向左移動;偏移量是600,也就是一張圖片的寬度。如果移動到7張圖片的最后一張,就把container拉到7張圖片里的第二張;如果移動到7張圖片里第一張,就把container拉到7張圖片里的第5張。
效果:
可以看到,圖片切換效果已經(jīng)出來了,但是下面的小圓點(diǎn)沒有跟著變換。接下來我們把這個效果加上。從上面的html代碼可以看到, :class="{dotted: i === (currentIndex - 1)}" ,小圓點(diǎn)的切換效果和data里的currentIndex值相關(guān),我們只要隨著圖片切換變動currentIndex值就可以了。
修改move方法里的代碼:
......
move(offset, direction) { direction === -1 ? this.currentIndex++ : this.currentIndex-- if (this.currentIndex > 5) this.currentIndex = 1 if (this.currentIndex < 1) this.currentIndex = 5 this.distance = this.distance + offset * direction if (this.distance < -3000) this.distance = -600 if (this.distance > -600) this.distance = -3000 }
上面的添加的三行代碼很好理解,如果是點(diǎn)擊右側(cè)箭頭,container就是向左移動, this.currentIndex 就是減1,反之就是加1。
效果:
可以看到,小圓點(diǎn)的切換效果已經(jīng)出來了。
三、過渡動畫
上面的代碼已經(jīng)實(shí)現(xiàn)了切換,但是沒有動畫效果,顯的非常生硬,接下來就是給每個圖片的切換過程添加過渡效果。
這個輪播組件筆者并沒有使用Vue自帶的class鉤子,也沒有直接使用css的transition屬性,而是用慕課網(wǎng)原作者講的setTimeout方法加遞歸來實(shí)現(xiàn)。
其實(shí)我也試過使用Vue的鉤子,但是總有一些小問題解決不掉;比如下面找到的這個例子:例子
這個例子在過渡的邊界上有一些問題,我也遇到了,而且還是時有時無。而如果使用css的transition過渡方法,在處理邊界的無限滾動上總會在chrome瀏覽器上有一下閃動,即使添加了 -webkit-transform-style:preserve-3d; 和 -webkit-backface-visibility:hidden 也還是沒用,而且要配合transition的 transitionend 事件對于IE瀏覽器的支持也不怎么好。
如果大家有看到更好的辦法,請?jiān)谠u論中留言哦~
下面我們來寫這個過渡效果,主要是改寫:
methods:{ move(offset, direction) { direction === -1 ? this.currentIndex++ : this.currentIndex-- if (this.currentIndex > 5) this.currentIndex = 1 if (this.currentIndex < 1) this.currentIndex = 5 const destination = this.distance + offset * direction this.animate(destination, direction) }, animate(des, direc) { if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) { this.distance += 30 * direc window.setTimeout(() => { this.animate(des, direc) }, 20) } else { this.distance = des if (des < -3000) this.distance = -600 if (des > -600) this.distance = -3000 } } }
上面的代碼是這個輪播我覺得最麻煩、也是最難理解的地方。
來理解一下:首先,我們對于move方法進(jìn)行了改寫,因?yàn)橐稽c(diǎn)點(diǎn)的移動,所以要先算出要移動到的目標(biāo)距離。然后,我們寫一個animate函數(shù)來實(shí)現(xiàn)這個過渡。這個animate函數(shù)接收兩個參數(shù),一個是要移動到的距離,另一個是方向。 如果我們點(diǎn)擊了右側(cè)的箭頭,container要向左側(cè)移動,要是沒有移動到目標(biāo)距離,就在 this.distance 減去一定的距離,如果減去后還是沒有到達(dá),在20毫米以后再調(diào)用這個 this.animate ,如此不斷移動,就形成了過渡效果。而如果移動到了目標(biāo)距離,那就將目標(biāo)距離賦值給 this.distance ,然后再進(jìn)行邊界和無限滾動的判斷。
當(dāng)然,使用 window.setInterval()
也可以實(shí)現(xiàn)這個效果,而且會稍微好理解一點(diǎn),因?yàn)闆]有用到遞歸:
methods:{ move(offset, direction) { direction === -1 ? this.currentIndex++ : this.currentIndex-- if (this.currentIndex > 5) this.currentIndex = 1 if (this.currentIndex < 1) this.currentIndex = 5 const destination = this.distance + offset * direction this.animate(destination, direction) }, animate(des, direc) { const temp = window.setInterval(() => { if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) { this.distance += 30 * direc } else { window.clearInterval(temp) this.distance = des if (des < -3000) this.distance = -600 if (des > -600) this.distance = -3000 } }, 20) } }
實(shí)現(xiàn)出來的效果如下:
四、簡單節(jié)流一下
寫到這里,效果是出來了,但是會有一點(diǎn)問題,如果多次快速點(diǎn)擊,就會有可能出現(xiàn)下面這種情況:
出現(xiàn)這種情況的原因很簡單,因?yàn)槭鞘褂枚〞r器過渡,所以連續(xù)快速點(diǎn)擊就會出現(xiàn)錯亂,簡單節(jié)流一下就好了: 在過渡完成之前點(diǎn)擊箭頭無效,其實(shí)就是設(shè)了一個閘,第一次點(diǎn)擊把閘打開,在閘再次打開之前,讓一部分代碼無法執(zhí)行,然后再在恰當(dāng)?shù)臅r機(jī)把閘打開。
我們把這個閘設(shè)在move函數(shù)里:
move(offset, direction) { if (!this.transitionEnd) return //這里是閘 this.transitionEnd = false //開閘以后再把閘關(guān)上 direction === -1 ? this.currentIndex++ : this.currentIndex-- if (this.currentIndex > 5) this.currentIndex = 1 if (this.currentIndex < 1) this.currentIndex = 5 const destination = this.distance + offset * direction this.animate(destination, direction) }
this.transitionEnd 是這個閘的鑰匙,我們把它放到data里:
this.transitionEnd: true
這個閘一開始默認(rèn)的狀態(tài)是開著的,第一次點(diǎn)擊以后,這個閘就關(guān)上了, this.tranisitonEnd = false ,在再次打開之前,后面的代碼都執(zhí)行不了。接下來就是在恰當(dāng)?shù)臅r機(jī)把這個閘打開,而這個恰當(dāng)?shù)臅r機(jī)就是過渡完成時,也就是在 animate函數(shù) 里:
animate(des, direc) { if (this.temp) { window.clearInterval(this.temp) this.temp = null } this.temp = window.setInterval(() => { if ((direc === -1 && des < this.distance) || (direc === 1 && des > this.distance)) { this.distance += 30 * direc } else { this.transitionEnd = true //閘再次打開 window.clearInterval(this.temp) this.distance = des if (des < -3000) this.distance = -600 if (des > -600) this.distance = -3000 } }, 20) }
這下快速點(diǎn)擊就沒有之前的那個問題了:
五、點(diǎn)擊小圓點(diǎn)實(shí)現(xiàn)圖片過渡切換
到目前為止的代碼:
接下來我們要實(shí)現(xiàn)點(diǎn)擊下面的小圓點(diǎn)來實(shí)現(xiàn)過渡和圖片切換。
在點(diǎn)擊小圓點(diǎn)的時候我們調(diào)用 jump 函數(shù),并將索引 i+1 傳給它。 這里需要特別注意,小圓點(diǎn)的索引和圖片對應(yīng)的索引不一致,圖片共7張,而5個小圓點(diǎn)對應(yīng)的是圖片中中間的5張,所以我們才傳 i+1 。
jump(index) { const direction = index - this.currentIndex >= 0 ? -1 : 1 //獲取滑動方向 const offset = Math.abs(index - this.currentIndex) * 600 //獲取滑動距離 this.move(offset, direction) }
上面的代碼有一個問題,在jump函數(shù)里調(diào)用move方法,move里對于currentIndex的都是 +1 ,而點(diǎn)擊小圓點(diǎn)可能是將 currentIndex 加或者減好多個,所以要對move里的代碼修改下:
direction === -1 ? this.currentIndex += offset/600 : this.currentIndex -= offset/600
改一行,根據(jù)offset算出currentIndex就行了。
但是又有一個問題,長距離切換速度太慢,如下:
所以我們需要控制一下速度,讓滑動一張圖片耗費(fèi)的時間和滑動多張圖片耗費(fèi)的時間一樣,給move和animate函數(shù)添加一個speed參數(shù),還要再算一下:
jump(index) { const direction = index - this.currentIndex >= 0 ? -1 : 1 const offset = Math.abs(index - this.currentIndex) * 600 const jumpSpeed = Math.abs(index - this.currentIndex) === 0 ? this.speed : Math.abs(index - this.currentIndex) * this.speed this.move(offset, direction, jumpSpeed) }
六、自動播放與暫停
前面的寫的差不多了,到這里就非常簡單了,寫一個函數(shù)play:
play() { if (this.timer) { window.clearInterval(this.timer) this.timer = null } this.timer = window.setInterval(() => { this.move(600, -1, this.speed) }, 4000) }
除了初始化以后自動播放,還要通過mouseover和mouseleave來控制暫停與播放:
stop() { window.clearInterval(this.timer) this.timer = null }
七、 兩處小坑
1. window.onblur 和 window.onfocus
寫到這里,基本功能都差不多了。但是如果把頁面切換到別的頁面,導(dǎo)致輪播圖所在頁面失焦,過一段時間再切回來會發(fā)現(xiàn)輪播狂轉(zhuǎn)。原因是頁面失焦以后,setInterval停止運(yùn)行,但是如果切回來就會一次性把該走的一次性走完。解決的方法也很簡單,當(dāng)頁面失焦時停止輪播,頁面聚焦時開始輪播。
window.onblur = function() { this.stop() }.bind(this) window.onfocus = function() { this.play() }.bind(this)
2. window.setInterval() 小坑
當(dāng)定時器 window.setInterval() 在多個異步回調(diào)中使用時,就有可能在某種機(jī)率下開啟多個執(zhí)行隊(duì)列, 所以為了保險(xiǎn)起見,不僅應(yīng)該在該清除時清除定時器,還要在每次使用之前也清除一遍 。
八、用props簡單寫兩個對外接口
props: { initialSpeed: { type: Number, default: 30 }, initialInterval: { type: Number, default: 4 } }, data() { ...... speed: this.initialSpeed }, computed:{ interval() { return this.initialInterval * 1000 } }
然后再在相應(yīng)的地方修改下就可以了。
完整的代碼如下:
關(guān)于使用Vue怎么制作一個圖片輪播組件問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。