使用vue封裝一個(gè)自定義樣式的滾動條
話不多說,步入正題,先創(chuàng)建測試文件,測試手寫滾動條是否可用
// test.vue
<template>
<div class="scrollLayout">
<!-- 內(nèi)容 -->
<div class="content" ref="content" @scroll="scroll">
<template v-for="i in 30">
<div style="width: 10rem; text-align: center">{{ i }}</div>
</template>
</div>
<!-- 滾動條 -->
<div class="scrollBar" ref="scrollBar">
<div class="slider" :style="sliderStyle"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const content = ref(null); // ref綁定的內(nèi)容元素
const scrollBar = ref(null); // ref綁定的手寫滾動條
const sliderHeight = ref(0); // 滑塊高度
const position = ref(0); // 手寫滾動條的位置
const sliderStyle = ref(`height:${sliderHeight}%;margin-top:${position}px;`);
const contentCH = ref(0); // content盒子高度
const contentSH = ref(0); // content內(nèi)容高度
const scrollBarCH = ref(0); // 滾動條高度
const activeScrollDistance = ref(0); // 內(nèi)容滾動的距離
const contentScrollDistance = ref(0); // 內(nèi)容滾動的距離
onMounted(() => {
const { clientHeight, scrollHeight } = content.value;
contentCH.value = clientHeight;
contentSH.value = scrollHeight;
scrollBarCH.value = scrollBar.value.clientHeight;
sliderHeight.value = (clientHeight / scrollHeight) * 100;
activeScrollDistance.value = scrollBarCH.value - scrollBarCH.value * (sliderHeight.value / 100);
contentScrollDistance.value = contentSH.value - contentCH.value;
})
// 內(nèi)容滾動時(shí)
const scroll = () => {
const { scrollTop } = content.value;
position.value = (scrollTop * activeScrollDistance.value) / contentScrollDistance.value; // 滑塊需要滑動的距離
};
</script>
<style scoped>
.scrollLayout {
position: relative;
padding: 1rem;
font-size: 1rem;
}
.content {
height: 20rem;
overflow: auto;
background: skyblue;
}
.scrollBar {
position: absolute;
top: 0;
right: 1rem;
width: 5px; /* 滾動條的寬度 */
height: 18rem; /* 滾動條的高度 */
background-color: pink; /* 滾動條的顏色 */
}
.slider {
width: 100%;
background-color: palevioletred; /* 滑塊的顏色 */
border-radius: 0.5rem; /* 滑塊圓角 */
}
</style>
獲取元素的 clientHeight 和 scrollHeight 來計(jì)算滑塊的高度以及可滾動距離,通過scrollTop獲取滾動的距離通過 scroll 事件來監(jiān)聽內(nèi)容的滾動,從而實(shí)現(xiàn)一個(gè)簡單的手搓滾動條,下面開始封裝。
封裝前還要考慮到的問題:可否在同一頁面多次復(fù)用;內(nèi)容容器一般都是調(diào)接口數(shù)據(jù)進(jìn)行遍歷渲染,而v-for在渲染每個(gè)條目時(shí)是逐個(gè)插入到DOM中的,這說明vue會先創(chuàng)建一個(gè)空的父元素,并將每個(gè)條目插入到該父元素中,這意味著 通過用ref綁定父頁面的內(nèi)容容器provide給子組件,子組件inject到dom元素的scrollHeight 這種方法不可行。
我想了一個(gè)辦法,父頁面把內(nèi)容通過slot給子組件把接口數(shù)據(jù)父傳子,在子組件可以拿到接口數(shù)據(jù)和內(nèi)容dom的scrollHeight,然后用watch監(jiān)聽props的接口數(shù)據(jù),若發(fā)生變化,重新獲取scrollHeight即可。
父頁面使用setTimeout模擬接口:
// test.vue
<template>
<div class="scrollLayout">
<scroll :data="arrList">
<template v-for="i in arrList">
<div style="width: 10rem; text-align: center">num: {{ i.num }}</div>
</template>
</scroll>
</div>
</template>
<script setup>
import { ref } from 'vue';
import scroll from '../components/scroll.vue';
const arrList = ref([]);
setTimeout(() => {
for (let i = 1; i <= 30; i++) {
const obj = { num: i < 10 ? '0' + i : i };
arrList.value.push(obj);
}
}, 3000);
</script>
<style lang="scss" scoped>
.scrollLayout {
height: 10rem;
background: pink;
}
</style>
組件部分:
// scroll.vue
<template>
<div class="scrollable">
<div class="content" ref="content" @scroll="scroll">
<slot></slot>
</div>
<div class="scrollBar" ref="scrollBar" :style="scrollBarStyle">
<div class="slider" :style="sliderStyle"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, computed, watch, nextTick } from 'vue';
const props = defineProps({
scrollColor: {
type: String,
default: '',
},
sliderColor: {
type: String,
default: '#000',
},
data: {
type: Array,
default: [],
},
right: {
type: String,
default: '0',
},
});
const content = ref(null); // ref內(nèi)容
const scrollBar = ref(null); // ref滾動條
const contentCH = ref(0); // content盒子高度
const contentSH = ref(0); // content內(nèi)容高度
const scrollBarCH = ref(0); // 滾動條高度
const activeScrollDistance = ref(0); // 內(nèi)容滾動的距離
const contentScrollDistance = ref(0); // 內(nèi)容滾動的距離
const sliderHeight = ref(0); // 滑塊高度
const position = ref(0); // 滾動條滑動距離
const scrollBarStyle = computed(() => `right:${props.right}px;background:${props.scrollColor};`);
const sliderStyle = computed(() => `height:${sliderHeight.value}%;margin-top:${position.value}px;background:${props.sliderColor};`);
onMounted(() => {
watch(
() => props.data,
() => {
// nextTick確保在DOM更新完畢后再執(zhí)行
nextTick(() => {
const { clientHeight, scrollHeight } = content.value;
console.log('容器的高度:', clientHeight, '內(nèi)容高度:', scrollHeight);
contentCH.value = clientHeight;
contentSH.value = scrollHeight;
scrollBarCH.value = scrollBar.value.clientHeight;
sliderHeight.value = (clientHeight / scrollHeight) * 100;
activeScrollDistance.value = scrollBarCH.value - scrollBarCH.value * (sliderHeight.value / 100);
contentScrollDistance.value = contentSH.value - contentCH.value;
});
},
{ immediate: true, deep: true }
);
});
// 監(jiān)聽滾動
const scroll = () => {
const { scrollTop } = content.value;
position.value = (scrollTop * activeScrollDistance.value) / contentScrollDistance.value;
};
</script>
<style lang="scss" scoped>
.scrollable {
position: relative;
display: flex;
height: 100%;
overflow: hidden;
}
.content {
height: 100%;
overflow: auto;
&::-webkit-scrollbar {
display: none;
}
}
.scrollBar {
position: absolute;
top: 0;
width: 5px;
height: 100%;
border-radius: 5px;
.slider {
width: 100%;
border-radius: 3px;
}
}
</style>
這樣就可以解決初始獲取的scrollHeight是內(nèi)容插入前的高度——即容器高度。
以上就是使用vue封裝一個(gè)自定義樣式的滾動條的詳細(xì)內(nèi)容,更多關(guān)于vue封裝滾動條的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式
這篇文章主要介紹了Vuex state中同步數(shù)據(jù)和異步數(shù)據(jù)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Vue 中 reactive創(chuàng)建對象類型響應(yīng)式數(shù)據(jù)的方法
在 Vue 的開發(fā)世界里,響應(yīng)式數(shù)據(jù)是構(gòu)建交互性良好應(yīng)用的基礎(chǔ),之前我們了解了ref用于定義基本類型的數(shù)據(jù),今天就來深入探討一下如何使用reactive定義對象類型的響應(yīng)式數(shù)據(jù),感興趣的朋友一起看看吧2025-02-02
Vue3使用Vue Router實(shí)現(xiàn)前端路由控制
在現(xiàn)代Web應(yīng)用中,前端路由控制是非常重要的一部分,它可以幫助我們將不同的頁面內(nèi)容展示給用戶,同時(shí)保持用戶在瀏覽不同頁面時(shí)的連貫性,本文將介紹如何使用Vue Router來實(shí)現(xiàn)前端路由控制,需要的朋友可以參考下2024-10-10
解決vue父組件調(diào)用子組件只執(zhí)行一次問題
開發(fā)中,需求是將內(nèi)容展示作為一個(gè)組件,輸入為contentId,請求在組件中,只需根據(jù)父組件傳過來的contentId去請求內(nèi)容的詳情即可,但是過程中卻發(fā)現(xiàn)一個(gè)問題,父組件調(diào)用子組件只執(zhí)行一次,所以本文就給大家介紹解決vue父組件調(diào)用子組件只執(zhí)行一次問題2023-09-09
vue elementui 動態(tài)追加下拉框、輸入框功能
這篇文章主要介紹了vue elementui 動態(tài)追加下拉框、輸入框功能,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-04-04
vue?目錄樹的展開與關(guān)閉的實(shí)現(xiàn)
Vue作為一款流行的前端框架,提供了一種數(shù)據(jù)驅(qū)動的方式來實(shí)現(xiàn)目錄樹,本文主要介紹了vue?目錄樹的展開與關(guān)閉的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
Vue項(xiàng)目三級聯(lián)動路由跳轉(zhuǎn)與傳參的思路詳解
這篇文章主要介紹了Vue項(xiàng)目三級聯(lián)動的路由跳轉(zhuǎn)與傳參的思路詳解,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-08-08

