全局自動下拉變色解決方案
雀語文章地址
📖 項目簡介
這是一個基于 Vue.js 和 uni-app 的全局自動下拉變色解決方案,通過全局 mixin 實現頁面滾動時導航欄的自動顏色變化效果。
? 核心特性
● 🎯 全局自動生效:無需在每個頁面手動導入,自動為所有頁面添加滾動監聽
● 🎨 智能顏色變化:根據滾動位置自動調整導航欄背景色和文字顏色
● 📱 跨平臺兼容:支持微信小程序、H5、App 等多端
● ? 性能優化:使用節流函數優化滾動事件處理
● 🔧 易于配置:支持自定義顏色配置和觸發閾值
🏗? 項目結構
buddhism/
├── mixins/
│ └── page-scroll-mixin.js # 全局滾動監聽 mixin
├── components/
│ └── custom-navbar/
│ └── custom-navbar.vue # 自定義導航欄組件
├── main.js # 全局 mixin 注冊
└── pages/
└── basePage/
└── basePage.vue # 示例頁面
🚀 快速開始
-
安裝依賴
-
全局配置
在 main.js 中已經配置了全局 mixin:
import PageScrollMixin from './mixins/page-scroll-mixin.js'// 注冊全局 mixin
Vue.mixin(PageScrollMixin)
- 使用導航欄組件
在任何頁面中直接使用 custom-navbar 組件:
<template><view class="page"><!-- 自定義導航欄 --><custom-navbar title="頁面標題":show-back="true"@back="goBack"ref="customNavbar"//必寫/><!-- 頁面內容 --><view class="content"><!-- 你的頁面內容 --></view></view>
</template>
<script>
export default {name: 'YourPage',methods: {goBack() {uni.navigateBack()}}
}
</script>
📋 核心文件說明
- mixins/page-scroll-mixin.js
全局滾動監聽 mixin,為所有頁面提供滾動事件處理:
export default {data() {return {scrollTop: 0,navbarOpacity: 0,navbarTextColor: '#000000',navbarBgColor: 'rgba(255, 255, 255, 0)'}},onPageScroll(e) {this.handlePageScroll(e)},methods: {handlePageScroll(e) {// 節流處理滾動事件if (this.scrollTimer) returnthis.scrollTimer = setTimeout(() => {this.scrollTop = e.scrollTopthis.updateNavbarStyle()this.scrollTimer = null}, 16) // 約60fps},updateNavbarStyle() {// 根據滾動位置更新導航欄樣式const opacity = Math.min(this.scrollTop / 100, 1)this.navbarOpacity = opacityif (opacity > 0.5) {this.navbarTextColor = '#000000'this.navbarBgColor = `rgba(255, 255, 255, ${opacity})`} else {this.navbarTextColor = '#ffffff'this.navbarBgColor = `rgba(255, 255, 255, ${opacity})`}}}
}
- components/custom-navbar/custom-navbar.vue
自定義導航欄組件,支持動態樣式變化:
<template><view class="custom-navbar":style="navbarStyle"><view class="navbar-content"><view v-if="showBack" class="back-btn"@click="handleBack"><text class="back-icon">?</text></view><text class="navbar-title":style="{ color: navbarTextColor }">{{ title }}</text></view></view>
</template>
<script>
export default {name: 'CustomNavbar',props: {title: {type: String,default: ''},showBack: {type: Boolean,default: false}},computed: {navbarStyle() {return {backgroundColor: this.navbarBgColor,color: this.navbarTextColor}}},methods: {handleBack() {this.$emit('back')}}
}
</script>
🎨 自定義配置
修改顏色配置
在 mixins/page-scroll-mixin.js 中可以自定義顏色:
updateNavbarStyle() {const opacity = Math.min(this.scrollTop / 100, 1)this.navbarOpacity = opacity// 自定義顏色邏輯if (opacity > 0.5) {this.navbarTextColor = '#333333' // 深色文字this.navbarBgColor = `rgba(255, 255, 255, ${opacity})`} else {this.navbarTextColor = '#ffffff' // 白色文字this.navbarBgColor = `rgba(0, 0, 0, ${opacity * 0.3})`}
}
修改觸發閾值
調整滾動距離閾值:
// 將 100 改為你想要的閾值
const opacity = Math.min(this.scrollTop / 50, 1) // 50px 開始變化
📱 使用示例
基礎頁面
<template><view class="page"><custom-navbar title="首頁":show-back="false"ref="customNavbar"//必寫/><view class="content"><view class="banner"><image src="/static/banner.jpg" mode="aspectFill" /></view><view class="list"><view v-for="item in 20" :key="item"class="list-item">列表項 {{ item }}</view></view></view></view>
</template>
<script>
export default {name: 'HomePage'
}
</script>
<style scoped>
.page {min-height: 100vh;background: #f5f5f5;
}.content {padding-top: 44px; /* 導航欄高度 */
}.banner {height: 200px;background: linear-gradient(45deg, #667eea, #764ba2);
}.list-item {padding: 15px;margin: 10px;background: white;border-radius: 8px;box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
</style>詳情頁面
<template><view class="page"><custom-navbar title="詳情頁":show-back="true"@back="goBack"/><view class="content"><view class="hero-image"><image src="/static/detail.jpg" mode="aspectFill" /></view><view class="detail-content"><text class="title">詳情標題</text><text class="description">詳情描述內容...</text></view></view></view>
</template>
<script>
export default {name: 'DetailPage',methods: {goBack() {uni.navigateBack()}}
}
</script>
🔧 技術實現
核心原理
- 全局 Mixin:通過 Vue 的全局 mixin 機制,為所有頁面自動注入滾動監聽
- 節流優化:使用 setTimeout 實現 60fps 的滾動事件節流
- 動態樣式:根據滾動位置計算透明度,實現平滑的顏色過渡
- 響應式數據:通過 Vue 的響應式系統,自動更新導航欄樣式
性能優化
● ? 滾動事件節流(16ms 間隔)
● ? 使用 computed 屬性緩存樣式計算
● ? 避免頻繁的 DOM 操作
● ? 合理的內存管理
🐛 常見問題
Q: 導航欄不顯示?
A: 確保頁面內容有足夠的高度可以滾動,并且設置了正確的 padding-top
Q: 顏色變化不明顯?
A: 檢查背景圖片的對比度,可以調整顏色配置或透明度
Q: 在某些頁面不需要效果?
A: 可以在特定頁面中覆蓋 mixin 的方法:
export default {
onPageScroll() {
// 覆蓋全局 mixin,不執行滾動處理
}
}
🤝 貢獻
歡迎提交 Issue 和 Pull Request!
注意:此解決方案專為 uni-app 項目設計,確保在目標平臺上測試兼容性。
全局導航欄組件,自動實現下拉透明到純色
import PageScrollMixin from './mixins/page-scroll-mixin.js'// 注冊全局 mixin
Vue.mixin(PageScrollMixin)
/*** 頁面滾動監聽 Mixin* 用于自動處理 custom-navbar 組件的滾動事件傳遞* * 使用方法:* 1. 在頁面中引入此 mixin* 2. 確保 custom-navbar 組件有 ref="customNavbar"(默認)或自定義 ref* 3. 自動處理滾動事件傳遞* * 配置選項:* - scrollRefs: 需要傳遞滾動事件的組件 ref 數組,默認為 ['customNavbar']* * 使用示例:* * // 基礎用法(使用默認 ref="customNavbar")* export default {* mixins: [PageScrollMixin],* // ... 其他配置* }* * // 自定義 ref 名稱* export default {* mixins: [PageScrollMixin],* scrollRefs: ['myNavbar'], // 自定義 ref 名稱* // ... 其他配置* }* * // 多個組件* export default {* mixins: [PageScrollMixin],* scrollRefs: ['customNavbar', 'floatingButton'], // 多個組件* // ... 其他配置* }*/export default {data() {return {// 默認的滾動組件 ref 列表scrollRefs: this.$options.scrollRefs || ['customNavbar']};},// 頁面生命周期onPageScroll(e) {// 自動將頁面滾動事件傳遞給所有配置的組件this.scrollRefs.forEach(refName => {if (this.$refs[refName] && typeof this.$refs[refName].handlePageScroll === 'function') {this.$refs[refName].handlePageScroll(e);}});}
};
<template><view><!-- 填充區,避免內容被導航欄遮擋 --><view class="navbar-placeholder" :style="{ height: navBarHeight + 'px' }"></view><!-- 自定義導航欄 --><view class="custom-navbar" :class="{ 'navbar-scrolled': isScrolled }":style="{ paddingTop: statusBarHeight + 'px', height: navBarHeight + 'px',backgroundColor: isScrolled ? backgroundColor : 'transparent'}"><view class="navbar-left" @click="handleBack" :style="{ height: capsuleHeight + 'px', lineHeight: capsuleHeight + 'px' }"><image :src="backIcon" mode="aspectFit" class="back-icon"></image></view><view class="navbar-title" :style="{ height: capsuleHeight + 'px', lineHeight: capsuleHeight + 'px' }">{{ title }}</view><view class="navbar-right" :style="{ height: capsuleHeight + 'px', lineHeight: capsuleHeight + 'px' }"><slot name="right"></slot></view></view></view>
</template><script>
import CommonEnum from "../../enum/common";export default {name: 'CustomNavbar',props: {title: {type: String,default: ''},backIcon: {type: String,default: CommonEnum.BACK_BUTTON},showBack: {type: Boolean,default: true},// 新增:滾動相關配置backgroundColor: {type: String,default: CommonEnum.BASE_COLOR // 滾動時的背景色,使用基調顏色},scrollThreshold: {type: Number,default: 20 // 滾動閾值,超過此值開始變色},enableScrollEffect: {type: Boolean,default: true // 是否啟用滾動效果}},data() {return {statusBarHeight: 0,navBarHeight: 0,menuButtonInfo: null,capsuleHeight: 0,// 新增:滾動狀態isScrolled: false,scrollTop: 0,lastScrollTop: 0}},mounted() {this.getSystemInfo();},methods: {getSystemInfo() {// 獲取系統信息const systemInfo = uni.getSystemInfoSync();// 獲取狀態欄高度this.statusBarHeight = systemInfo.statusBarHeight;// 獲取膠囊按鈕信息this.menuButtonInfo = uni.getMenuButtonBoundingClientRect();// 計算膠囊高度this.capsuleHeight = this.menuButtonInfo.height;// 計算導航欄高度(膠囊底部到狀態欄頂部的距離)this.navBarHeight = this.menuButtonInfo.bottom + 8;},handleBack() {if (this.showBack) {// 先觸發自定義事件,讓父組件有機會處理this.$emit('back');// 自動執行返回邏輯uni.navigateBack({delta: 1});}},// 新增:處理頁面滾動(供父組件調用)handlePageScroll(e) {if (!this.enableScrollEffect) return;this.scrollTop = e.scrollTop;// 判斷是否超過滾動閾值if (this.scrollTop > this.scrollThreshold) {if (!this.isScrolled) {this.isScrolled = true;this.$emit('scroll-change', { isScrolled: true, scrollTop: this.scrollTop });}} else {if (this.isScrolled) {this.isScrolled = false;this.$emit('scroll-change', { isScrolled: false, scrollTop: this.scrollTop });}}// 觸發滾動事件,讓父組件可以獲取滾動信息this.$emit('scroll', {scrollTop: this.scrollTop,isScrolled: this.isScrolled});},// 新增:手動設置滾動狀態(供父組件調用)setScrollState(scrollTop) {if (!this.enableScrollEffect) return;this.scrollTop = scrollTop;this.isScrolled = scrollTop > this.scrollThreshold;}}
}
</script><style lang="scss" scoped>
/* 填充區樣式 */
.navbar-placeholder {width: 100%;background-color: transparent;
}/* 自定義導航欄樣式 */
.custom-navbar {position: fixed;top: 0;left: 0;right: 0;z-index: 999;padding-top: 0;background-color: transparent;display: flex;align-items: center;justify-content: space-between;padding-left: 30rpx;padding-right: 30rpx;box-sizing: border-box;transition: background-color 0.3s ease; /* 添加過渡動畫 */
}/* 滾動狀態樣式 */
.navbar-scrolled {box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1); /* 滾動時添加陰影 */
}.navbar-left {display: flex;align-items: center;justify-content: center;width: 60rpx;.back-icon {width: 56rpx;height: 56rpx;}
}.navbar-title {font-size: 36rpx;font-weight: bold;color: #695347;flex: 1;text-align: center;display: flex;align-items: center;justify-content: center;
}.navbar-right {width: 60rpx;display: flex;align-items: center;justify-content: center;
}
</style>
<template><view class="page"><custom-navbarref="customNavbar"title="基礎頁面"/><tile-grid/><view class="header" :style="{ backgroundColor: CommonEnum.BASE_COLOR }"><!-- 狀態展示 --><status-displayv-if="loading"type="loading"loading-text="加載中..."/><!-- 頁面內容將在這里添加 --></view></view>
</template><script>
import CommonEnum from "../../enum/common";
import StatusDisplay from "../../components/status-display/status-display.vue";export default {components: {StatusDisplay},data() {return {CommonEnum,loading: false}},onLoad() {// 頁面加載時獲取數據this.loadPageData()},methods: {// 加載頁面數據async loadPageData() {this.loading = truetry {// TODO: 調用頁面數據API// const response = await getPageData()// 模擬加載setTimeout(() => {this.loading = false}, 1000)} catch (error) {console.error('獲取頁面數據失敗:', error)this.loading = false}}}
}
</script><style lang="scss" scoped>
.page {background: #F5F0E7;
}
.header {width: 100%;min-height: 100vh;display: flex;align-items: flex-start;flex-direction: column;padding: 0 15rpx;padding-bottom: 40rpx;
}
</style>
顏色是在枚舉中是 #FFFFFF
圖片都是 網絡地址
雀語文章地址