Vue+better-scroll 實(shí)現(xiàn)通訊錄字母索引的示例代碼
如??圖,實(shí)現(xiàn)一個(gè)滾動(dòng)內(nèi)容區(qū)域,右側(cè)字母滾動(dòng)索引定位;選擇和拖動(dòng)字母索引,對(duì)應(yīng)內(nèi)容滾動(dòng)到視窗
環(huán)境準(zhǔn)備:
- 安裝better-scroll npm包
- 安裝 mouseWheel 擴(kuò)展 BetterScroll 鼠標(biāo)滾輪的能力,開(kāi)啟鼠標(biāo)滾動(dòng)(移動(dòng)端非必須)
npm install @better-scroll/core @better-scroll/mouse-wheel --save
實(shí)現(xiàn)步驟:
數(shù)據(jù)結(jié)構(gòu)
- 內(nèi)容區(qū)和索引按下圖數(shù)據(jù)結(jié)構(gòu)處理
export default { data() { return { entityList: [ { key: 'A', list: ['氨基酸代謝病', '廣泛性發(fā)育障礙'] }, { key: 'B', list: ['巴特綜合征', '包涵體性結(jié)膜炎', '膀胱外翻', '鼻腔結(jié)外型NK/T細(xì)胞淋巴瘤'] }, { key: 'C', list: ['C5功能不全綜合征', '腸道蛔蟲(chóng)癥', '喘息樣支氣管炎'] }, { key: 'D', list: ['低氯性氮質(zhì)血癥綜合征', '石棉狀糠疹', 'Dravet綜合征'] } ] }; } };
基本HTML
<!-- 內(nèi)容區(qū)域 --> <!-- 最外層父容器wrapper,固定高度并且overflow:hidden--> <div class="h-534px flex-1 wrapper overflow-hidden" ref="wrapper"> <!-- content 注意滾動(dòng)區(qū)域一定是父容器的第一個(gè)子元素,當(dāng)高度超出父容器即可滾動(dòng) --> <ul class="content"> <!-- v-for 循環(huán)出列表 --> <li v-for="(item, index) in entityList" :key="index" class="flex flex-col" ref="listGroup" > <div class="h-42px leading-42px text-sm font-bold pl-15px w-244px" > {{ item.key }} </div> <div class="flex flex-col"> <span class="h-42px leading-42px text-sm pl-15px g-clamp1 w-244px" v-for="(it, i) in item.list" :key="i" > {{ it }} </span> </div> </li> </ul> </div> <!-- 索引 --> <ul class="entityList w-15px bg-white"> <!-- v-for 循環(huán)出索引 --> <li v-for="(item, index) in entityList" :key="index" :data-index="index" class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6" > {{ item.key }} </li> </ul>
使用better-scroll實(shí)現(xiàn)內(nèi)容區(qū)列表的滾動(dòng)
<script> //import 引入BScroll import BScroll from '@better-scroll/core'; import MouseWheel from '@better-scroll/mouse-wheel'; BScroll.use(MouseWheel); export default { mounted() { //dom渲染完畢,初始化better-scroll this.$nextTick(() => { this.initBanner(); }); }, methods: { initBanner() { if (this.scroll && this.scroll.destroy){ this.scroll.refresh();//當(dāng) DOM 結(jié)構(gòu)發(fā)生變化的時(shí)候需重新計(jì)算 BetterScroll this.scroll.destroy();//銷(xiāo)毀 BetterScroll,解綁事件 } this.scroll = new BScroll('.wrapper', { scrollY: true,//縱向滾動(dòng) click: true, mouseWheel: true, disableMouse: false, //啟用鼠標(biāo)拖動(dòng) disableTouch: false, //啟用手指觸摸 probeType: 3 //設(shè)置為3,BetterScroll實(shí)時(shí)派發(fā) scroll 事件 }); } } }; </script>
??注意:這里我們?cè)趍ounted時(shí)期,在this.$nextTick 的回調(diào)函數(shù)中初始化 better-scroll 。這時(shí)wrapper 的 DOM 已經(jīng)渲染了,我們可以正確計(jì)算它以及它內(nèi)層 content 的高度,以確保滾動(dòng)正常。
給索引添加點(diǎn)擊事件和移動(dòng)事件實(shí)現(xiàn)跳轉(zhuǎn)
<ul class="entityList w-15px bg-white"> <li v-for="(item, index) in entityList" :key="index" :data-index="index" class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6" @touchstart="onShortcutStart" //點(diǎn)擊事件 @touchmove.stop.prevent="onShortcutMove" //移動(dòng)事件 > {{ item.key }} </li> </ul>
created() { // 添加一個(gè) touch 用于記錄移動(dòng)的屬性 this.touch = {}; this.$nextTick(() => { this.initBanner(); }); }, methods: { 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ì)算最后的位置 let index = this.touch.anchorIndex * 1 + delta; this.scroll.scrollToElement(this.$refs.listGroup[index]); } }
給索引添加高亮
- 在data中定義currentIndex用于索引高亮的判斷,并在html中綁定class
data() { return { currentIndex: 0, entityList: [ { key: 'A', list: ['氨基酸代謝病', '廣泛性發(fā)育障礙'] }] } }
<ul class="entityList w-15px bg-white"> <li v-for="(item, index) in entityList" :key="index" :data-index="index" class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6" @touchstart="onShortcutStart" @touchmove.stop.prevent="onShortcutMove" :class="{ current: currentIndex === index }" > {{ item.key }} </li> </ul>
接下來(lái)求currentIndex:
- 先通過(guò)better-scroll 的on(type, fn, context)方法,監(jiān)聽(tīng)當(dāng)前實(shí)例上的scroll,得到內(nèi)容區(qū)y軸的偏移量
initBanner() { if (this.scroll && this.scroll.destroy) { this.scroll.refresh(); this.scroll.destroy(); } this.scroll = new BScroll('.wrapper', { scrollY: true, click: true, mouseWheel: true, disableMouse: false, //啟用鼠標(biāo)拖動(dòng) disableTouch: false, //啟用手指觸摸 probeType: 3 }); // 監(jiān)聽(tīng)Y軸偏移的值 this.scroll.on('scroll', pos => { this.scrollY = pos.y; }); },
- data中初始化 listHeight ,添加calculateHeight() 方法計(jì)算內(nèi)容區(qū)高度
data() { return { listHeight: [], currentIndex: 0, entityList: [ { key: 'A', list: ['氨基酸代謝病', '廣泛性發(fā)育障礙'] } ] } }
//計(jì)算內(nèi)容區(qū)高度 _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); } }
- data中初始化scrollY為-1,在 watch 中監(jiān)聽(tīng) scrollY
data() { return { scrollY: -1 currentIndex: 0, listHeight: [], entityList: [ { key: 'A', list: ['氨基酸代謝病', '廣泛性發(fā)育障礙'] } ] } }
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ì)算內(nèi)容區(qū)高度判斷 對(duì)應(yīng)索引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 實(shí)現(xiàn)索引高亮的特效
全部代碼
<template> <div> <!-- 內(nèi)容區(qū)域 --> <div class="h-534px flex-1 wrapper overflow-hidden" ref="listview"> <ul class="content"> <li v-for="(item, index) in entityList" :key="index" class="flex flex-col" ref="listGroup" > <div class="h-42px leading-42px text-sm font-bold pl-15px w-244px"> {{ item.key }} </div> <div class="flex flex-col"> <Link class="h-42px leading-42px text-sm pl-15px g-clamp1 w-244px" v-for="(it, i) in item.list" :key="i" :to="{ name: 'Yidian', query: { title: it } }" > {{ it }} </Link> </div> </li> </ul> </div> <!-- 索引 --> <ul class="entityList w-15px bg-white"> <li v-for="(item, index) in entityList" :key="index" :data-index="index" class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6" @touchstart="onShortcutStart" @touchmove.stop.prevent="onShortcutMove" :class="{ current: currentIndex === index }" > {{ item.key }} </li> </ul> </div> </template> <script> import BScroll from '@better-scroll/core'; import MouseWheel from '@better-scroll/mouse-wheel'; BScroll.use(MouseWheel); export default { data() { return { currentIndex: 0, listHeight: [], entityList: [ { key: 'A', list: ['氨基酸代謝病', '廣泛性發(fā)育障礙'] }, { key: 'B', list: ['巴特綜合征', '包涵體性結(jié)膜炎', '膀胱外翻', '鼻腔結(jié)外型NK/T細(xì)胞淋巴瘤'] }, { key: 'C', list: ['C5功能不全綜合征', '腸道蛔蟲(chóng)癥', '喘息樣支氣管炎'] }, { key: 'D', list: ['低氯性氮質(zhì)血癥綜合征', '石棉狀糠疹', 'Dravet綜合征'] }, { key: 'E', list: ['耳聾', '兒童癲癇', '兒童頭痛', '兒童急性中耳炎'] }, { key: 'F', list: ['腹肌缺如綜合征', '肥大性神經(jīng)病', '肺缺如', '樊尚咽峽炎', '腹壁疝'] } ], scrollY: -1 }; }, mounted() { this.touch = {}; this.$nextTick(() => { this.initBanner(); }); }, methods: { //初始化scroll initBanner() { if (this.scroll && this.scroll.destroy) { this.scroll.refresh(); this.scroll.destroy(); } this.scroll = new BScroll('.wrapper', { scrollY: true, click: true, mouseWheel: true, disableMouse: false, //啟用鼠標(biāo)拖動(dòng) disableTouch: false, //啟用手指觸摸 probeType: 3 }); this._calculateHeight(); this.scroll.on('scroll', pos => { console.log(pos.y); this.scrollY = pos.y; }); }, 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ì)算最后的位置 let index = this.touch.anchorIndex * 1 + delta; //注意這里需要判斷邊界,不然拖動(dòng)到頂部和底部會(huì)報(bào)錯(cuò) if (index >= 0 && index <= this.entityList.length - 2) { this.scroll.scrollToElement(this.$refs.listGroup[index]); } }, //計(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); } } }, 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; } } }; </script> <style scoped lang="postcss"> .tabActive { @apply font-bold; } .tabActive::after { content: ''; display: block; width: 18px; height: 3px; background: #00c2b0; border-radius: 2px; position: absolute; bottom: 0; } .sortActive { color: #00c2b0; } .select-left { @apply w-110px; } .select-left-item { @apply pl-15px h-42px text-sm; } .entityList { position: fixed; right: 8px; top: 156px; } .current { border-radius: 50%; background: #00beb0; color: #fff; } .typeAct { @apply bg-white text-primary; } </style>
總結(jié)
- 參考了很多網(wǎng)上的資料,相對(duì)于原生實(shí)現(xiàn),better-scroll帶來(lái)了更大的便利,但是同時(shí)也需要我們對(duì)better-scroll有一定的了解。
參考文獻(xiàn)
到此這篇關(guān)于Vue+better-scroll 實(shí)現(xiàn)通訊錄字母索引的文章就介紹到這了,更多相關(guān)Vue+better-scroll 實(shí)現(xiàn)通訊錄字母索引內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue數(shù)據(jù)代理的實(shí)現(xiàn)流程逐步講解
通過(guò)一個(gè)對(duì)象代理對(duì)另一個(gè)對(duì)象中的屬性的操作(讀/寫(xiě)),就是數(shù)據(jù)代理。要搞懂Vue數(shù)據(jù)代理這個(gè)概念,那我們就要從Object.defineProperty()入手,Object.defineProperty()是Vue中比較底層的一個(gè)方法,在數(shù)據(jù)劫持,數(shù)據(jù)代理以及計(jì)算屬性等地方都或多或少的用到了本函數(shù)2023-01-01詳解Vue項(xiàng)目引入CreateJS的方法(親測(cè)可用)
CreateJS是基于HTML5開(kāi)發(fā)的一套模塊化的庫(kù)和工具。這篇文章主要介紹了Vue項(xiàng)目引入CreateJS的方法(親測(cè)),需要的朋友可以參考下2019-05-05vue emit之Property or method “$$v“ i
這篇文章主要介紹了vue emit之Property or method “$$v“ is not defined的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06vue雙向數(shù)據(jù)綁定原理分析、vue2和vue3原理的不同點(diǎn)
這篇文章主要介紹了vue雙向數(shù)據(jù)綁定原理分析、vue2和vue3原理的不同點(diǎn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12vue單頁(yè)面如何通過(guò)prerender-spa-plugin插件進(jìn)行SEO優(yōu)化
這篇文章主要介紹了vue單頁(yè)面如何通過(guò)prerender-spa-plugin插件進(jìn)行SEO優(yōu)化,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05解決找不到模塊“xxx.vue”或其相應(yīng)的類(lèi)型聲明問(wèn)題
這篇文章主要介紹了解決找不到模塊“xxx.vue”或其相應(yīng)的類(lèi)型聲明問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2022-10-10解決vue-router在同一個(gè)路由下切換,取不到變化的路由參數(shù)問(wèn)題
今天小編就為大家分享一篇解決vue-router在同一個(gè)路由下切換,取不到變化的路由參數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue路由傳參方式的方式總結(jié)及獲取參數(shù)詳解
vue 路由傳參的使用場(chǎng)景一般都是應(yīng)用在父路由跳轉(zhuǎn)到子路由時(shí),攜帶參數(shù)跳轉(zhuǎn),下面這篇文章主要給大家介紹了關(guān)于vue路由傳參方式的方式總結(jié)及獲取參數(shù)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07