Vue + better-scroll 實(shí)現(xiàn)移動(dòng)端字母索引導(dǎo)航功能
vue+ better-scroll 實(shí)現(xiàn)移動(dòng)端歌手列表字母索引導(dǎo)航。算是一個(gè)學(xué)習(xí)筆記吧,寫個(gè)筆記讓自己了解的更加深入一點(diǎn)。
Demo:list-view,使用 chrome 手機(jī)模式查看。換成手機(jī)模式之后,不能滑動(dòng)的話,刷新一下就 OK 了。
Github: 移動(dòng)端字母索引導(dǎo)航
效果圖
配置環(huán)境
因?yàn)橛玫降氖?vue-cli 和 better-scroll,所以首先要安裝 vue-cli,然后再 npm 安裝better-scroll。
簡單介紹一下 better-scroll:
better-scroll 是一款重點(diǎn)解決移動(dòng)端(已支持 PC)各種滾動(dòng)場景需求的插件。它的核心是借鑒的 iscroll 的實(shí)現(xiàn),它的 API 設(shè)計(jì)基本兼容 iscroll,在 iscroll 的基礎(chǔ)上又?jǐn)U展了一些 feature 以及做了一些性能優(yōu)化。
better-scroll 是基于原生 JS 實(shí)現(xiàn)的,不依賴任何框架。它編譯后的代碼大小是 63kb,壓縮后是 35kb,gzip 后僅有 9kb,是一款非常輕量的 JS lib。
除了這兩,還使用 scss、vue-lazyload。scss 預(yù)處理器,大家都懂,用別的也一樣。lazyload 實(shí)現(xiàn)懶加載,不用也可以,主要是優(yōu)化一下體驗(yàn)。
數(shù)據(jù)直接使用了網(wǎng)易云的歌手榜單,偷懶就直接放在 data 里面了。
CSS 樣式我就不貼了,直接看源碼就可以了。
實(shí)現(xiàn)基本樣式
直接使用 v-for 和 雙側(cè)嵌套實(shí)現(xiàn)歌手列表、以及右側(cè)索引欄。
HTML 結(jié)構(gòu):
<ul> <li v-for="group in singers" class="list-group" :key="group.id" ref="listGroup"> <h2 class="list-group-title">{{ group.title }}</h2> <ul> <li v-for="item in group.items" class="list-group-item" :key="item.id"> <img v-lazy="item.avatar" class="avatar"> <span class="name">{{ item.name }}</span> </li> </ul> </li> </ul> <div class="list-shortcut"> <ul> <li v-for="(item, index) in shortcutList" class="item" :data-index="index" :key="item.id" > {{ item }} </li> </ul> </div>
shortcutList 是通過計(jì)算屬性得到的,取 title 的第一個(gè)字符即可。
shortcutList () { return this.singers.map((group) => { return group.title.substr(0, 1) }) }
使用 better-scroll
使用 better-scroll 實(shí)現(xiàn)滾動(dòng)。對了,使用的時(shí)候別忘了用 import 引入。
created () { // 初始化 better-scroll 必須要等 dom 加載完畢 setTimeout(() => { this._initSrcoll() }, 20) }, methods: { _initSrcoll () { console.log('didi') this.scroll = new BScroll(this.$refs.listView, { // 獲取 scroll 事件,用來監(jiān)聽。 probeType: 3 }) } }
使用 created 方法進(jìn)行 better-scroll 初始化,使用 setTimeout 是因?yàn)樾枰鹊?DOM 加載完畢。不然 better-scroll 獲取不到 dom 就會初始化失敗。
這里把方法寫在兩 methods 里面,這樣就不會看起來很亂,直接調(diào)用就可以了。
初始化的時(shí)候傳入兩 probeType: 3,解釋一下:當(dāng) probeType 為 3 的時(shí)候,不僅在屏幕滑動(dòng)的過程中,而且在 momentum 滾動(dòng)動(dòng)畫運(yùn)行過程中實(shí)時(shí)派發(fā) scroll 事件。如果沒有設(shè)置該值,其默認(rèn)值為 0,即不派發(fā) scroll 事件。
給索引添加點(diǎn)擊事件和移動(dòng)事件實(shí)現(xiàn)跳轉(zhuǎn)
首先需要給索引綁定一個(gè) touchstart 事件(當(dāng)在屏幕上按下手指時(shí)觸發(fā)),直接使用 v-on 就可以了。然后還需要給索引添加一個(gè) data-index 這樣就可以獲取到索引的值,使用 :data-index="index" 。
<div class="list-shortcut"> <ul> <li v-for="(item, index) in shortcutList" class="item" :data-index="index" :key="item.id" @touchstart="onShortcutStart" @touchmove.stop.prevent="onShortcutMove" > {{ item }} </li> </ul> </div>
綁定一個(gè) onShortcutStart 方法。實(shí)現(xiàn)點(diǎn)擊索引跳轉(zhuǎn)的功能。再綁定一個(gè) onShortcutMove 方法,實(shí)現(xiàn)滑動(dòng)跳轉(zhuǎn)。
created () { // 添加一個(gè) touch 用于記錄移動(dòng)的屬性 this.touch = {} // 初始化 better-scroll 必須要等 dom 加載完畢 setTimeout(() => { this._initSrcoll() }, 20) }, methods: { _initSrcoll () { this.scroll = new BScroll(this.$refs.listView, { probeType: 3, click: true }) }, onShortcutStart (e) { // 獲取到綁定的 index let index = e.target.getAttribute('data-index') // 使用 better-scroll 的 scrollToElement 方法實(shí)現(xiàn)跳轉(zhuǎn) this.scroll.scrollToElement(this.$refs.listGroup[index]) // 記錄一下點(diǎn)擊時(shí)候的 Y坐標(biāo) 和 index let firstTouch = e.touches[0].pageY this.touch.y1 = firstTouch this.touch.anchorIndex = index }, onShortcutMove (e) { // 再記錄一下移動(dòng)時(shí)候的 Y坐標(biāo),然后計(jì)算出移動(dòng)了幾個(gè)索引 let touchMove = e.touches[0].pageY this.touch.y2 = touchMove // 這里的 16.7 是索引元素的高度 let delta = Math.floor((this.touch.y2 - this.touch.y1) / 18) // 計(jì)算最后的位置 // * 1 是因?yàn)?this.touch.anchorIndex 是字符串,用 * 1 偷懶的轉(zhuǎn)化一下 let index = this.touch.anchorIndex * 1 + delta this.scroll.scrollToElement(this.$refs.listGroup[index]) } }
這樣就可以實(shí)現(xiàn)索引的功能了。
當(dāng)然這樣是不會滿足我們的對不對,我們要加入炫酷的特效呀。比如索引高亮什么的~~
移動(dòng)內(nèi)容索引高亮
emmm,這個(gè)時(shí)候就有點(diǎn)復(fù)雜啦。但是有耐心就可以看懂滴。
我們需要 better-scroll 的 on 方法,返回內(nèi)容滾動(dòng)時(shí)候的 Y軸偏移值。所以在初始化 better-scroll 的時(shí)候需要添加一下代碼。對了,別忘了在 data 中添加一個(gè) scrollY,和 currentIndex (用來記錄高亮索引的位置)因?yàn)槲覀冃枰O(jiān)聽,所以在 data 中添加。
_initSrcoll () { this.scroll = new BScroll(this.$refs.listView, { probeType: 3, click: true }) // 監(jiān)聽Y軸偏移的值 this.scroll.on('scroll', (pos) => { this.scrollY = pos.y }) }
然后需要計(jì)算一下內(nèi)容的高度,添加一個(gè) calculateHeight() 方法,用來計(jì)算索引內(nèi)容的高度。
_calculateHeight () { this.listHeight = [] const list = this.$refs.listGroup let height = 0 this.listHeight.push(height) for (let i = 0; i < list.length; i++) { let item = list[i] height += item.clientHeight this.listHeight.push(height) } } // [0, 760, 1380, 1720, 2340, 2680, 2880, 3220, 3420, 3620, 3960, 4090, 4920, 5190, 5320, 5590, 5790, 5990, 6470, 7090, 7500, 7910, 8110, 8870] // 得到這樣的值
然后在 watch 中監(jiān)聽 scrollY,看代碼:
watch: { scrollY (newVal) { // 向下滑動(dòng)的時(shí)候 newVal 是一個(gè)負(fù)數(shù),所以當(dāng) newVal > 0 時(shí),currentIndex 直接為 0 if (newVal > 0) { this.currentIndex = 0 return } // 計(jì)算 currentIndex 的值 for (let i = 0; i < this.listHeight.length - 1; i++) { let height1 = this.listHeight[i] let height2 = this.listHeight[i + 1] if (-newVal >= height1 && -newVal < height2) { this.currentIndex = i return } } // 當(dāng)超 -newVal > 最后一個(gè)高度的時(shí)候 // 因?yàn)?this.listHeight 有頭尾,所以需要 - 2 this.currentIndex = this.listHeight.length - 2 } }
得到 currentIndex 的之后,在 html 中使用。
給索引綁定 class --> :class="{'current': currentIndex === index}"
最后再處理一下滑動(dòng)索引的時(shí)候改變 currentIndex。
因?yàn)榇a可以重復(fù)利用,且需要處理邊界情況,所以就把
this.scroll.scrollToElement(this.$refs.listGroup[index])
重新寫了個(gè)函數(shù),來減少代碼量。
// 在 scrollToElement 的時(shí)候,改變 scrollY,因?yàn)橛?watch 所以就會計(jì)算出 currentIndex scrollToElement (index) { // 處理邊界情況 // 因?yàn)?index 通過滑動(dòng)距離計(jì)算出來的 // 所以向上滑超過索引框框的時(shí)候就會 < 0,向上就會超過最大值 if (index < 0) { return } else if (index > this.listHeight.length - 2) { index = this.listHeight.length - 2 } // listHeight 是正的, 所以加個(gè) - this.scrollY = -this.listHeight[index] this.scroll.scrollToElement(this.$refs.listGroup[index]) }
lazyload
lazyload 插件也順便說一下哈,增加一下用戶體驗(yàn)。
使用方法
先 npm 安裝
在 main.js 中 import,然后 Vue.use
import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { loading: require('./common/image/default.jpg') })
添加一張 loading 圖片,使用 webpack 的 require 獲取圖片。
然后在需要使用的時(shí)候,把 :src="" 換成 v-lazy="" 就實(shí)現(xiàn)了圖片懶加載的功能。
總結(jié)
移動(dòng)端字母索引導(dǎo)航就這么實(shí)現(xiàn)啦,感覺還是很有難度的哈(對我來說)。
主要就是使用了 better-scroll 的 on 獲取移動(dòng)偏移值(實(shí)現(xiàn)高亮)、scrollToElement 跳轉(zhuǎn)到相應(yīng)的位置(實(shí)現(xiàn)跳轉(zhuǎn))。以及使用 touch 事件監(jiān)聽觸摸,來獲取開始的位置,以及滑動(dòng)距離(計(jì)算最后的位置)。
總結(jié)
以上所述是小編給大家介紹的Vue + better-scroll 實(shí)現(xiàn)移動(dòng)端字母索引導(dǎo)航功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Avue和Element-UI動(dòng)態(tài)三級表頭的實(shí)現(xiàn)
本文主要介紹了Avue和Element-UI動(dòng)態(tài)三級表頭的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07Vue.js實(shí)現(xiàn)一個(gè)SPA登錄頁面的過程【推薦】
本篇文章主要介紹了Vue.js寫一個(gè)SPA登錄頁面過程的相關(guān)知識,具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-04-04vue 使用v-if切換輸入框時(shí)導(dǎo)致輸入框的數(shù)據(jù)內(nèi)容沒有清空的問題解決(兩種解決方法)
這篇文章主要介紹了vue 使用v-if切換輸入框時(shí)導(dǎo)致輸入框的數(shù)據(jù)內(nèi)容沒有清空的問題解決,本文給大家分享兩種解決方法,需要的朋友可以參考下2023-05-05vue下history模式刷新后404錯(cuò)誤解決方法
這篇文章主要介紹了vue下history模式刷新后404錯(cuò)誤解決方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08使用Vue+ElementUI動(dòng)態(tài)生成面包屑導(dǎo)航教程
Vue和ElementUI都是非常流行的前端開發(fā)框架,它們可以讓我們更加便捷地開發(fā)前端應(yīng)用,下面這篇文章主要給大家介紹了關(guān)于使用Vue+ElementUI動(dòng)態(tài)生成面包屑導(dǎo)航的相關(guān)資料,需要的朋友可以參考下2023-05-05vue新vue-cli3環(huán)境配置和模擬json數(shù)據(jù)的實(shí)例
今天小編就為大家分享一篇vue新vue-cli3環(huán)境配置和模擬json數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09