vue中利用iscroll.js解決pc端滾動問題
專案中經常遇到區域超出部分會出現滾動條,滾動條在pc端可以通過滑鼠滾輪控制上下,在移動端可以通過滑鼠拖動頁面進行滾動,這兩種場景都是符合使用者習慣,然而這種滾動條一般都是豎【vertical】項滾動條,如果pc端出現橫向滾動條【horizontal】,在不做處理的情況下,你只能用滑鼠拖動橫向滾動條按鈕【scrollerbar】展示滾動區域,而且為了美觀,一般滾動條會進行樣式編寫或者隱藏,那麼橫向區域預設情況下就沒法滾動。
二、描述
現為了解決pc端滾動區域能像移動端一樣,能夠通過滑鼠拖動滾動區域直接進行滾動,如圖所示
pc端滾動示例圖
滾動例項用到知識點如下:
- 採用vue-cli3+iscroll.js組合的方式;
- 使用 vue 自定義指令實現 iscroll 例項化和引數配置;
- 實現橫向滾動區域和豎向滾動區域之間的聯動;
- 實現橫向滾動條居中顯示和使用scrollIntoView()方法的差別
三、自定義指令 v-iscroll
1、新建指令檔案
這裡使用 vue 自定義指令初始化 iscroll 例項,在 vue-cli3 專案目錄下新建vIscroll.js,檔案程式碼如下:
const IScroll = require('iscroll') const VIScroll = { install: function (Vue,options) { Vue.directive('iscroll',{ inserted: function (el,binding,vnode) { let callBack let iscrollOptions = options <!--vue元件中繫結的兩個引數 option、instance--> const option = binding.value && binding.value.option const func = binding.value && binding.value.instance // 判斷輸入引數 const optionType = option ? [].toString.call(option) : undefined const funcType = func ? [].toString.call(func) : undefined // 相容 google 瀏覽器拖動 el.addEventListener('touchmove',function (e) { e.preventDefault() }) // 將引數配置到new IScroll(el,iscrollOptions)中 if (optionType === '[object Object]') { iscrollOptions = option } if (funcType === '[object Function]') { callBack = func } // 使用vnode繫結iscroll是為了讓iscroll物件能夠誇狀態傳遞,避免iscroll重複建立 // 這裡面跟官方網站 const myScroll = new IScroll('#wrapper',option) 初始化一樣 vnode.scroll = new IScroll(el,iscrollOptions) // 如果指令傳遞函式進來,把iscroll例項傳遞出去 if (callBack) callBack(vnode.scroll) },componentUpdated: function (el,vnode,oldVnode) { // 將scroll繫結到新的vnode上,避免多次繫結 vnode.scroll = oldVnode.scroll // 使用 settimeout 讓refresh跳到事件流結尾,保證refresh時資料已經更新完畢 setTimeout(() => { vnode.scroll.refresh() },0) },unbind: function (el,oldVnode) { // 解除繫結時要把iscroll銷燬 vnode.scroll = oldVnode.scroll vnode.scroll.destroy() vnode.scroll = null } }) } } module.exports = VIScroll
這裡附上 iscroll.js 5 官方文件地址, iscroll npm 包地址,相關屬性和方法自行檢視。
2、載入引用指令
首先在 main.js 中載入指令:
import Vue from 'vue' import App from './App.vue' import "./assets/reset.css" // 載入scroll指令 import VIscroll from './directive/vIscroll' Vue.use(VIscroll) Vue.config.productionTip = false new Vue({ render: h => h(App),}).$mount('#app')
使用指令,摘自 tabList.vue 元件部分程式碼如下:
<template> <div class="tab-container"> <div class="scroll-container" v-iscroll="{ option: iscrollConf,instance: getIscroll }" ref="scrollContainer" > <ul class="tab-li-container" ref="tabLiContainer" > <li class="tab-li-item" v-for="(item,index) in list" :key="item.id" :id="item.id" ref="tabItem" @click="tabEvent(item,index)" > <div class="item" :class="{ 'item-active': currentId == item.id }" >{{item.num}}</div> </li> </ul> </div> <div class="tab-left" @click="tabBtnEvent('left')" ><</div> <div class="tab-right" @click="tabBtnEvent('right')" >></div> </div> </template> <script> export default { props: ['list'],data () { return { iscrollConf: { bounce: true,mouseWheel: true,click: true,scrollX: true,scrollY: false },currentId: null,currentIndex: 0,myScroll: null } },mounted () { this.$refs.tabLiContainer.style.width = this.$refs.tabItem[0].offsetWidth * this.list.length + 'px' this.$nextTick(() => { this.myScroll.refresh() }) },methods: { tabEvent (item,currentIndex) { <!--點選某個li 按鈕事件處理邏輯--> },tabBtnEvent (direction) { <!--左右切換邏輯事件--> },getIscroll (iscroll) { this.myScroll = iscroll } },watch: { list: { handler (l) { this.currentId = l[0].id },immediate: true,deep: true } } } </script> <style scoped> // 樣式 </style>
上述程式碼中 v-iscroll 指令傳入兩個欄位引數:
option:配置iscroll引數,這裡面注意scrollX,scrollY兩個屬性,代表的是橫向還是豎向滾動;
instance:回撥方法的呼叫, vIscroll.js 中執行回撥方法,通過該元件方法 getIscroll() 獲取到 iscroll 的例項。
3、上下滾動區域聯動
上面的程式碼可以解決開篇場景中的問題,現在實現上下區域聯動,通過點選橫向滾動條某個按鈕,使其變成選中狀態,然後豎向滾動條對應的項跳到首位,如圖所以:
聯動示例圖
3-1、聯動實現方法
點選按鈕的方法:
tabEvent (item,currentIndex) { this.currentId = item.id this.currentIndex = currentIndex <!--這裡實現按鈕始終居中顯示,暫時省略,下面補充--> ... <!--傳給豎向滾動元件--> this.$emit("switchTab",this.currentId,this.currentIndex) },
豎向滾動區域元件【App.vue】程式碼部分如下,並對 switchTab() 方法進行詳細註釋:
<template> <div id="app"> <TabList :list="list" @switchTab="switchTab" ></TabList> <!-- v-iscroll="defalutOption" --> <div v-iscroll="{ option: defalutOption,instance: getIscroll }" class="tab-content-container" ref="detailItemContainer" > <ul class="tab-list-container"> <li v-for="item in list" :key="item.id" class="list-item" ref="detailItem" > <div>{{item.value}}</div> </li> </ul> </div> </div> </template> <script> import TabList from './components/tabList.vue' export default { name: 'App',components: { TabList,},data () { return { list: [ { id: 1,value: '這是第1題',num: 1 },<!--...省略資料展示--> { id: 16,value: '這是第16題',num: 16 } ],defalutOption: { bounce: true,scrollX: false,scrollY: true },methods: { switchTab (currentId,currentIndex) { <!--對選中的當前項,這裡就是“3”按鈕對應的“這是第3題”,求出它距離父元素的上邊距offsetTop值--> const offsetTop = this.$refs.detailItem[currentIndex].offsetTop <!--滾動的範圍不能超過這個滾動體的底部,這裡面用到iscroll的屬性maxScrollY--> const y = offsetTop >= Math.abs(this.myScroll.maxScrollY) ? this.myScroll.maxScrollY : -offsetTop <!--呼叫iscroll的方法進行滾動到相應的位置--> this.myScroll.scrollTo(0,y) },<!--獲取例項--> getIscroll (iscroll) { this.myScroll = iscroll } } } </script> <style scoped> <!--樣式--> ... </style>
這裡面用到的都是 iscroll 外掛自帶的屬性和方法進行滾動邊界的判斷和滾動,比用 JavaScript 方法方便的多,而且用了iscroll作為滾動容器,已經在vIscroll.js禁用了相關瀏覽器預設事件。
3-2、居中顯示
這裡 JavaScript 有個 scrollIntoView() 方法, 官方文件連結 ,這個方法讓當前的元素滾動到瀏覽器視窗的可視區域內。關鍵缺點是,如果橫向滾動和豎向滾動都同時用到這個方法,只能保證一個滾動區域有效,另一個會不滾動。
使用 scrollIntoView() 方法配置如下:
this.$refs.tabItem[this.currentIndex].scrollIntoView({ behavior: "smooth",inline: "center",block: 'nearest' })
這裡在橫向滾動區域添加了一對左右按鈕,實現切換功能,如圖所示:
切換按鈕示例圖
切換按鈕事件方法就是通過改變上一個、下一個按鈕下標,呼叫方法,實現切換功能,切換事件方法邏輯如下:
tabBtnEvent (direction) { const max = this.$refs.tabItem.length if (direction === 'left' && this.currentIndex > 0) { this.currentIndex-- } if (direction === 'right' && this.currentIndex < max - 1) { this.currentIndex++ } <!--呼叫單擊按鈕事件--> this.tabEvent(this.$refs.tabItem[this.currentIndex],
下面對 單擊按鈕事件 新增居中邏輯,詳細程式碼和解析圖如下,可以對比檢視:
居中計算圖
tabEvent (item,currentIndex) { this.currentId = item.id this.currentIndex = currentIndex // 獲取滾動容器的長度的一半,即中間點 const scrollContainerHalfWidth = this.$refs.scrollContainer.offsetWidth / 2 // 獲取單個item的一半長度 const tabItemHalfWidth = this.$refs.tabItem[currentIndex].offsetWidth / 2 // 求取插值,就是開始到中間開始位置的距離 const halfDistance = scrollContainerHalfWidth - tabItemHalfWidth // 求取當前item的相對總長度的偏移量 const currentItemOffsetLeft = this.$refs.tabItem[currentIndex].offsetLeft // scroll 移動到中間的值 const x = halfDistance - currentItemOffsetLeft this.myScroll.scrollTo(x,0) this.$emit("switchTab",
4、總結
1、整個例項用的都是iscroll外掛相關屬性實現的滾動,避免同時使用JavaScript方法造成的程式碼混亂;
2、利用自定義指令的方式有效的避免了傳統例項化iscroll帶來的程式碼冗餘,使其方便簡潔;
3、本例項滾動選項都是字串,如果出現圖片的情況,合理使用iscroll.refresh() 方法,在正確的時期重新計算滾動區域,避免滾動邊界受限;
總結
以上所述是小編給大家介紹的vue中利用iscroll.js解決pc端滾動問題,希望對大家有所幫助!