設(shè)計(jì)初衷
在開(kāi)發(fā)頁(yè)面時(shí),往往需要實(shí)現(xiàn),點(diǎn)擊頁(yè)面的導(dǎo)航菜單頁(yè)面滾動(dòng)到相應(yīng)位置,滾動(dòng)頁(yè)面實(shí)現(xiàn)菜單選項(xiàng)的高亮。在html開(kāi)發(fā)中,我們可以用到a標(biāo)簽錨點(diǎn)實(shí)現(xiàn),jq的動(dòng)畫(huà)相結(jié)合實(shí)現(xiàn)類似效果。在框架中vant UI框架也為我們實(shí)現(xiàn)了這一效果。
效果展示
當(dāng)菜單導(dǎo)航滾動(dòng)到頁(yè)面頂部時(shí),菜單吸頂
當(dāng)點(diǎn)擊菜單按鈕時(shí),切換到對(duì)應(yīng)區(qū)域(過(guò)渡到該區(qū)域,有動(dòng)畫(huà)效果)
當(dāng)內(nèi)容區(qū)滾動(dòng)到某類區(qū)域時(shí),對(duì)應(yīng)區(qū)域的菜單按鈕高亮
設(shè)計(jì)思路
1、吸頂效果的實(shí)現(xiàn)
獲取菜單導(dǎo)航距離頁(yè)面頂部距離
wx.createSelectorQuery()
頁(yè)面滾動(dòng)監(jiān)聽(tīng)
滾動(dòng)距離與菜單初始位置值比較
1) 距離
const query = wx.createSelectorQuery() query.select('.menu_nav').boundingClientRect(function(res) { let obj = {} if (res && res.top) { obj[item.attr] = parseInt(res.top) } }).exec()
① wx.createSelectorQuery()
返回一個(gè) SelectorQuery 對(duì)象實(shí)例。在自定義組件或包含自定義組件的頁(yè)面中,應(yīng)使用 this.createSelectorQuery() 來(lái)代替。
② SelectorQuery.select(string selector)
在當(dāng)前頁(yè)面下選擇第一個(gè)匹配選擇器 selector 的節(jié)點(diǎn)。返回一個(gè) NodesRef 對(duì)象實(shí)例,可以用于獲取節(jié)點(diǎn)信息。
selector 語(yǔ)法
selector類似于 CSS 的選擇器,但僅支持下列語(yǔ)法。
屬性 | 類型 | 說(shuō)明 |
---|---|---|
id | string | 節(jié)點(diǎn)的 ID |
dataset | Object | 節(jié)點(diǎn)的 dataset |
left | number | 節(jié)點(diǎn)的左邊界坐標(biāo) |
right | number | 節(jié)點(diǎn)的右邊界坐標(biāo) |
top | number | 節(jié)點(diǎn)的上邊界坐標(biāo) |
bottom | number | 節(jié)點(diǎn)的下邊界坐標(biāo) |
width | number | 節(jié)點(diǎn)的寬度 |
height | number | 節(jié)點(diǎn)的高度 |
③ NodesRef.boundingClientRect(function callback)
添加節(jié)點(diǎn)的布局位置的查詢請(qǐng)求。相對(duì)于顯示區(qū)域,以像素為單位。其功能類似于 DOM 的 getBoundingClientRect。返回 NodesRef 對(duì)應(yīng)的 SelectorQuery。
屬性類型說(shuō)明idstring節(jié)點(diǎn)的 IDdatasetObject節(jié)點(diǎn)的 datasetleftnumber節(jié)點(diǎn)的左邊界坐標(biāo)rightnumber節(jié)點(diǎn)的右邊界坐標(biāo)topnumber節(jié)點(diǎn)的上邊界坐標(biāo)bottomnumber節(jié)點(diǎn)的下邊界坐標(biāo)widthnumber節(jié)點(diǎn)的寬度heightnumber節(jié)點(diǎn)的高度
④ SelectorQuery.exec(function callback)
執(zhí)行所有的請(qǐng)求。請(qǐng)求結(jié)果按請(qǐng)求次序構(gòu)成數(shù)組,在callback的第一個(gè)參數(shù)中返回。
2) 頁(yè)面滾動(dòng)監(jiān)聽(tīng)
data中初始化--tabFixed=false
(表示是否固定定位)
滾動(dòng)條滾動(dòng)距離超過(guò)了菜單初始距離時(shí),tabFixed=true
開(kāi)啟定位
// 監(jiān)聽(tīng)頁(yè)面滾動(dòng) onPageScroll: function(e) { let hTop = parseInt(e.scrollTop) // 菜單是否需要定位到頂部 if (hTop > this.data.menu_top) { this.setData({ tabFixed: true }) } else { this.setData({ tabFixed: false }) } }
onPageScroll(Object object))
監(jiān)聽(tīng)用戶滑動(dòng)頁(yè)面事件。
參數(shù) Object object:
屬性 | 類型 | 說(shuō)明 |
---|---|---|
scrollTop | Number | 頁(yè)面在垂直方向已滾動(dòng)的距離(單位px) |
注意:請(qǐng)只在需要的時(shí)候才在 page 中定義此方法,不要定義空方法。以減少不必要的事件派發(fā)對(duì)渲染層-邏輯層通信的影響。 注意:請(qǐng)避免在 onPageScroll 中過(guò)于頻繁的執(zhí)行 setData 等引起邏輯層-渲染層通信的操作。尤其是每次傳輸大量數(shù)據(jù),會(huì)影響通信耗時(shí)。
2、切換到對(duì)應(yīng)區(qū)域
記錄當(dāng)前點(diǎn)擊的菜單并高亮
獲取每個(gè)區(qū)域初始距離頁(yè)面頂部距離
設(shè)置當(dāng)前頁(yè)面滾動(dòng)條滾動(dòng)到的位置,設(shè)置過(guò)度時(shí)間
// 導(dǎo)航欄切換設(shè)置 setSelectType(event) { let index = event.currentTarget.dataset.type this.setData({ tabIndex: index, }) let arr = ['panel1_top', 'panel2_top', 'panel3_top', 'panel4_top'] let _this = this wx.pageScrollTo({ scrollTop: _this.data[arr[index]], duration: 500 }) },
wx.pageScrollTo(Object object)
將頁(yè)面滾動(dòng)到目標(biāo)位置,支持選擇器和滾動(dòng)距離兩種方式定位
屬性 | 類型 | 默認(rèn)值 | 必填 | 說(shuō)明 |
---|---|---|---|---|
scrollTop | number | 無(wú) | 否 | 滾動(dòng)到頁(yè)面的目標(biāo)位置,單位 px |
duration | number | 300 | 否 | 滾動(dòng)動(dòng)畫(huà)的時(shí)長(zhǎng),單位 ms |
selector | string | 無(wú) | 否 | 選擇器 2.7.3 |
success | function | 無(wú) | 否 | 接口調(diào)用成功的回調(diào)函數(shù) |
fail | function | 無(wú) | 否 | 接口調(diào)用失敗的回調(diào)函數(shù) |
complete | unction | 無(wú) | 否 | 接口調(diào)用結(jié)束的回調(diào)函數(shù)(調(diào)用成功、失敗都會(huì)執(zhí)行) |
3) 滾動(dòng)到某類區(qū)域時(shí),對(duì)應(yīng)區(qū)域的菜單按鈕高亮
獲取初始時(shí)區(qū)域距離頂端距離
let arr = [ { name: '.menu-nav', attr: 'menu_top', addNum: 0 }, { name: '.panel1', attr: 'panel1_top', addNum: 0 }, { name: '.panel2', attr: 'panel2_top', addNum: 0 }, { name: '.panel3', attr: 'panel3_top', addNum: 0 }, { name: '.panel4', attr: 'panel4_top', addNum: 0 }, ] arr.forEach((item, i) => { wx.createSelectorQuery().select(item.name).boundingClientRect(function(res) { let obj = {} if (res && res.top) { obj[item.attr] = parseInt(res.top) if (item.addNum) { obj[item.attr] += item.addNum } that.setData({ ...obj }) } }).exec() })
滾動(dòng)監(jiān)聽(tīng)是否超過(guò)了該區(qū)域
// 監(jiān)聽(tīng)頁(yè)面滾動(dòng) onPageScroll: function(e) { let hTop = parseInt(e.scrollTop) // 自動(dòng)切換菜單 let tab=0 if (hTop >= (this.data['panel4_top'] - this.data.menu_top)) { tab=3 } else if (hTop >= (this.data['panel3_top'] - this.data.menu_top)){ tab=2 } else if (hTop >= (this.data['panel2_top'] - this.data.menu_top)){ tab=1 } this.setData({ tabIndex: tab, }) },
完整代碼
index.js
// pages/index/index.js Page({ /** * 頁(yè)面的初始數(shù)據(jù) */ data: { tabIndex: 0, //當(dāng)前處于那個(gè)菜單 menuList: ['菜單1', '菜單2', '菜單3', '菜單4'], //導(dǎo)航菜單 tabFixed: false, //是否定位 // 初始頁(yè)面距離頂部距離 menu_top: 0, panel1_top: 0, panel2_top: 0, panel3_top: 0, panel4_top: 0, }, /** * 生命周期函數(shù)--監(jiān)聽(tīng)頁(yè)面加載 */ onLoad: function (options) { }, onShow:function (options){ this.getTopDistance() }, // 獲取距離頁(yè)面頂部高度 getTopDistance() { let that = this let arr = [{ name: '.menu-nav', attr: 'menu_top', addNum: 0 }, { name: '.panel1', attr: 'panel1_top', addNum: 0 }, { name: '.panel2', attr: 'panel2_top', addNum: 0 }, { name: '.panel3', attr: 'panel3_top', addNum: 0 }, { name: '.panel4', attr: 'panel4_top', addNum: 0 }, ] arr.forEach((item, i) => { wx.createSelectorQuery().select(item.name).boundingClientRect(function (res) { let obj = {} if (res && res.top) { obj[item.attr] = parseInt(res.top) if (item.addNum) { obj[item.attr] += item.addNum } that.setData({ ...obj }) } }).exec() }) }, // 導(dǎo)航欄切換設(shè)置 setSelectType(event) { let index = event.currentTarget.dataset.type this.setData({ tabIndex: index, }) let arr = ['panel1_top', 'panel2_top', 'panel3_top', 'panel4_top'] let _this = this wx.pageScrollTo({ scrollTop: _this.data[arr[index]], duration: 500 }) }, // 監(jiān)聽(tīng)頁(yè)面滾動(dòng) onPageScroll: function (e) { let hTop = parseInt(e.scrollTop) // 菜單是否需要定位到頂部 if (hTop > this.data.menu_top) { this.setData({ tabFixed: true }) } else { this.setData({ tabFixed: false }) } // 自動(dòng)切換菜單 if (hTop >= (this.data['panel4_top'] - this.data.menu_top)) { this.setData({ tabIndex: 3, }) }else if (hTop >= (this.data['panel3_top'] - this.data.menu_top)){ this.setData({ tabIndex: 2, }) } else if (hTop >= (this.data['panel2_top'] - this.data.menu_top)){ this.setData({ tabIndex: 1, }) }else{ this.setData({ tabIndex: 0, }) } }, })
index.wxml
<view class="Main"> <view class="head"> 我是頭部區(qū)域 </view> <view class="{{tabFixed?'is-fixed':''}} menu-nav"> <text wx:for="{{menuList}}" class="{{tabIndex==index?'is-select':''}}" bind:tap="setSelectType" data-type='{{index}}'>{{item}}</text> </view> <view class="content"> <view class="panel1 panel">頁(yè)面1</view> <view class="panel2 panel">頁(yè)面2</view> <view class="panel3 panel">頁(yè)面3</view> <view class="panel4 panel">頁(yè)面4</view> </view> </view>
index.wxss
.menu-nav { display: flex; align-items: center; justify-content: space-around; color: black; padding: 10px 0; width: 100%; background-color: white; } .is-select { color: red; } .head { display: flex; align-items: center; justify-content: center; font-size: 40px; height: 120px; background-color: greenyellow; } .is-fixed { position: fixed; top: 0; } .panel { display: flex; align-items: center; justify-content: center; font-size: 20px; } .panel1 { height: 800rpx; background-color: rebeccapurple; } .panel2 { height: 700rpx; background-color: blue; } .panel3 { height: 1000rpx; background-color: orange; } .panel4 { height: 1200rpx; background-color: pink; }
到此這篇關(guān)于微信小程序-自定義菜單導(dǎo)航(實(shí)現(xiàn)樓梯效果)的文章就介紹到這了,更多相關(guān)微信小程序自定義菜單導(dǎo)航內(nèi)容請(qǐng)搜索以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持!