vue中利用iscroll.js解決pc端滾動問題
項目中經(jīng)常遇到區(qū)域超出部分會出現(xiàn)滾動條,滾動條在pc端可以通過鼠標滾輪控制上下,在移動端可以通過鼠標拖動頁面進行滾動,這兩種場景都是符合用戶習(xí)慣,然而這種滾動條一般都是豎【vertical】項滾動條,如果pc端出現(xiàn)橫向滾動條【horizontal】,在不做處理的情況下,你只能用鼠標拖動橫向滾動條按鈕【scrollerbar】展示滾動區(qū)域,而且為了美觀,一般滾動條會進行樣式編寫或者隱藏,那么橫向區(qū)域默認情況下就沒法滾動。
二、描述
現(xiàn)為了解決pc端滾動區(qū)域能像移動端一樣,能夠通過鼠標拖動滾動區(qū)域直接進行滾動,如圖所示

pc端滾動示例圖
滾動實例用到知識點如下:
- 采用vue-cli3+iscroll.js組合的方式;
- 使用 vue 自定義指令實現(xiàn) iscroll 實例化和參數(shù)配置;
- 實現(xiàn)橫向滾動區(qū)域和豎向滾動區(qū)域之間的聯(lián)動;
- 實現(xiàn)橫向滾動條居中顯示和使用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組件中綁定的兩個參數(shù) option、instance-->
const option = binding.value && binding.value.option
const func = binding.value && binding.value.instance
// 判斷輸入?yún)?shù)
const optionType = option ? [].toString.call(option) : undefined
const funcType = func ? [].toString.call(func) : undefined
// 兼容 google 瀏覽器拖動
el.addEventListener('touchmove', function (e) {
e.preventDefault()
})
// 將參數(shù)配置到new IScroll(el, iscrollOptions)中
if (optionType === '[object Object]') {
iscrollOptions = option
}
if (funcType === '[object Function]') {
callBack = func
}
// 使用vnode綁定iscroll是為了讓iscroll對象能夠夸狀態(tài)傳遞,避免iscroll重復(fù)建立
// 這里面跟官方網(wǎng)站 const myScroll = new IScroll('#wrapper',option) 初始化一樣
vnode.scroll = new IScroll(el, iscrollOptions)
// 如果指令傳遞函數(shù)進來,把iscroll實例傳遞出去
if (callBack) callBack(vnode.scroll)
},
componentUpdated: function (el, binding, vnode, oldVnode) {
// 將scroll綁定到新的vnode上,避免多次綁定
vnode.scroll = oldVnode.scroll
// 使用 settimeout 讓refresh跳到事件流結(jié)尾,保證refresh時數(shù)據(jù)已經(jīng)更新完畢
setTimeout(() => {
vnode.scroll.refresh()
}, 0)
},
unbind: function (el, binding, vnode, oldVnode) {
// 解除綁定時要把iscroll銷毀
vnode.scroll = oldVnode.scroll
vnode.scroll.destroy()
vnode.scroll = null
}
})
}
}
module.exports = VIScroll
這里附上 iscroll.js 5 官方文檔地址, iscroll npm 包地址,相關(guān)屬性和方法自行查看。
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 指令傳入兩個字段參數(shù):
option:配置iscroll參數(shù),這里面注意scrollX,scrollY兩個屬性,代表的是橫向還是豎向滾動;
instance:回調(diào)方法的調(diào)用, vIscroll.js 中執(zhí)行回調(diào)方法,通過該組件方法 getIscroll() 獲取到 iscroll 的實例。
3、上下滾動區(qū)域聯(lián)動
上面的代碼可以解決開篇場景中的問題,現(xiàn)在實現(xiàn)上下區(qū)域聯(lián)動,通過點擊橫向滾動條某個按鈕,使其變成選中狀態(tài),然后豎向滾動條對應(yīng)的項跳到首位,如圖所以:

聯(lián)動示例圖
3-1、聯(lián)動實現(xiàn)方法
點擊按鈕的方法:
tabEvent (item, currentIndex) {
this.currentId = item.id
this.currentIndex = currentIndex
<!--這里實現(xiàn)按鈕始終居中顯示,暫時省略,下面補充-->
...
<!--傳給豎向滾動組件-->
this.$emit("switchTab", this.currentId, this.currentIndex)
},
豎向滾動區(qū)域組件【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 },
<!--...省略數(shù)據(jù)展示-->
{ id: 16, value: '這是第16題', num: 16 }
],
defalutOption: {
bounce: true,
mouseWheel: true,
click: true,
scrollX: false,
scrollY: true
},
myScroll: null
}
},
methods: {
switchTab (currentId, currentIndex) {
<!--對選中的當(dāng)前項,這里就是“3”按鈕對應(yīng)的“這是第3題”,求出它距離父元素的上邊距offsetTop值-->
const offsetTop = this.$refs.detailItem[currentIndex].offsetTop
<!--滾動的范圍不能超過這個滾動體的底部,這里面用到iscroll的屬性maxScrollY-->
const y = offsetTop >= Math.abs(this.myScroll.maxScrollY) ? this.myScroll.maxScrollY : -offsetTop
<!--調(diào)用iscroll的方法進行滾動到相應(yīng)的位置-->
this.myScroll.scrollTo(0, y)
},
<!--獲取實例-->
getIscroll (iscroll) {
this.myScroll = iscroll
}
}
}
</script>
<style scoped>
<!--樣式-->
...
</style>
這里面用到的都是 iscroll 插件自帶的屬性和方法進行滾動邊界的判斷和滾動,比用 JavaScript 方法方便的多,而且用了iscroll作為滾動容器,已經(jīng)在vIscroll.js禁用了相關(guān)瀏覽器默認事件。
3-2、居中顯示
這里 JavaScript 有個 scrollIntoView() 方法, 官方文檔鏈接 ,這個方法讓當(dāng)前的元素滾動到瀏覽器窗口的可視區(qū)域內(nèi)。關(guān)鍵缺點是,如果橫向滾動和豎向滾動都同時用到這個方法,只能保證一個滾動區(qū)域有效,另一個會不滾動。
使用 scrollIntoView() 方法配置如下:
this.$refs.tabItem[this.currentIndex].scrollIntoView({
behavior: "smooth",
inline: "center",
block: 'nearest'
})
這里在橫向滾動區(qū)域添加了一對左右按鈕,實現(xiàn)切換功能,如圖所示:

切換按鈕示例圖
切換按鈕事件方法就是通過改變上一個、下一個按鈕下標,調(diào)用方法,實現(xiàn)切換功能,切換事件方法邏輯如下:
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++
}
<!--調(diào)用單擊按鈕事件-->
this.tabEvent(this.$refs.tabItem[this.currentIndex], 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
// 求取當(dāng)前item的相對總長度的偏移量
const currentItemOffsetLeft = this.$refs.tabItem[currentIndex].offsetLeft
// scroll 移動到中間的值
const x = halfDistance - currentItemOffsetLeft
this.myScroll.scrollTo(x, 0)
this.$emit("switchTab", this.currentId, this.currentIndex)
},
4、總結(jié)
1、整個實例用的都是iscroll插件相關(guān)屬性實現(xiàn)的滾動,避免同時使用JavaScript方法造成的代碼混亂;
2、利用自定義指令的方式有效的避免了傳統(tǒng)實例化iscroll帶來的代碼冗余,使其方便簡潔;
3、本實例滾動選項都是字符串,如果出現(xiàn)圖片的情況,合理使用iscroll.refresh() 方法,在正確的時期重新計算滾動區(qū)域,避免滾動邊界受限;
總結(jié)
以上所述是小編給大家介紹的vue中利用iscroll.js解決pc端滾動問題,希望對大家有所幫助!
相關(guān)文章
vue cli3中eslint報錯no-undef和eslint規(guī)則配置方式
這篇文章主要介紹了vue cli3中eslint報錯no-undef和eslint規(guī)則配置方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08
vue3?hook重構(gòu)DataV的全屏容器組件詳解
這篇文章主要為大家介紹了vue3?hook重構(gòu)DataV的全屏容器組件詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
vue文件上傳Required request part ‘file‘ is&n
這篇文章主要介紹了vue文件上傳Required request part ‘file‘ is not present問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
關(guān)于Ant-Design-Vue快速上手指南+排坑
這篇文章主要介紹了關(guān)于Ant-Design-Vue快速上手指南+排坑,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
Vue配合iView實現(xiàn)省市二級聯(lián)動的示例代碼
本篇文章主要介紹了Vue配合iView實現(xiàn)省市二級聯(lián)動的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-07-07
vue 獲取到數(shù)據(jù)但卻渲染不到頁面上的解決方法
這篇文章主要介紹了vue 獲取到數(shù)據(jù)但卻渲染不到頁面上的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
vue實力踩坑?當(dāng)前頁push當(dāng)前頁無效的解決
這篇文章主要介紹了vue實力踩坑?當(dāng)前頁push當(dāng)前頁無效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-04-04

