抽屜選單是app上常見的選單設計方式,典型的抽屜選單如下圖所示
#下面展示如何基於微信小程式實現抽屜選單,最終效果如下圖所示:
#頁包含一個主頁和抽屜選單頁,為了實現滑動效果,頁面採用absolute佈局,程式碼如下
index.wxml
<view id='id-main-page' class='main-page' animation='{{animationData}}' style='left:{{mainPageLeft}}rpx;' bindtouchstart='onMainPageTouchstart' catchtouchmove='onMainPageTouchmove' bindtouchend='onMainPageTouchend' bindtap='onMainPageTap'> <view class="userinfo"> <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName}}</text> </view> </view> <view class='drawer-menu' animation='{{animationData}}' style='left:{{drawerMenuLeft}}rpx;'> <view class="userinfo"> <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image> <text class="userinfo-nickname">{{userInfo.nickName2}}</text> </view> </view>
index.wxss
.main-page { width:100%; height:2000rpx; position: absolute; top: 0; left: 0; padding: 200rpx 0; } .drawer-menu { width: 800rpx; height:2000rpx; position: absolute; top: 0; left: -800rpx; padding: 200rpx 0; background: rgba(22, 22, 22, 1); z-index: 800; }
程式綁定了主頁的touch事件和tap事件,並且使用catchtouchmove
阻止了move事件的傳遞,因為在真機環境下頁面會自動回應滑動事件,注意不要catch start
和end
事件,這會導致無法觸發tap
事件。
首先定義一些資料來記錄滑動過程和狀態
drawerMenuMoveData: { check: false, //是否触发滑动操作 state:0, //0:初始状态 1:菜单弹出中状态 2:菜单弹入状态中 3:菜单弹出状态 firstTouchX:0, //首次触摸X坐标值 touchCheckX:60, //触发滑动的触摸X moveX:0, // 滑动操作横向的移动距离 maxMoveX: (app.globalData.deviceInfo.windowWidth - 60), //抽屉菜单最大移动距离 lastTranlateX: 0 //上次动画效果的平移距离,用于校准left值 },
之後就是滑動事件的回應處理touchstart
事件,先判斷目前狀態,然後根據觸摸位置判斷是否啟動滑動狀態
onMainPageTouchstart: function(e) { var data = this.drawerMenuMoveData; var clientX = e.touches[0].clientX; //初识状态 if (data.state === 0) { if (clientX <= data.touchCheckX && clientX > 20) { data.check = true; data.state = 1; data.firstTouchX = clientX; } } //菜单弹出状态 else if (data.state === 3) { if (clientX >= data.maxMoveX) { data.check = true; data.state = 2; data.firstTouchX = clientX; } } },
touchmove
事件,首先判斷是否處於滑動狀態
onMainPageTouchmove: function(e) { var data = this.drawerMenuMoveData; var pixelRatio = app.globalData.deviceInfo.pixelRatio; if (data.check) { var mainPageLeft = 0, drawerMenuLeft = 0; var moveX = e.touches[0].clientX - data.firstTouchX; if (data.state === 1) { //处理边界状态 if (moveX < 0) { moveX = 0; } if (moveX > data.maxMoveX) { moveX = data.maxMoveX; } if (moveX >= 0 && moveX <= data.maxMoveX) { data.moveX = moveX; moveX = moveX - data.lastTranlateX; //px转为rpx moveX = moveX * pixelRatio; mainPageLeft = moveX; drawerMenuLeft = -800 + moveX; } } else if (data.state === 2) { //处理边界状态 if (moveX > 0) { moveX = 0; } if (moveX < -data.maxMoveX) { moveX = -data.maxMoveX; } if (moveX <= 0 && moveX >= -data.maxMoveX) { data.moveX = moveX; moveX = moveX - data.lastTranlateX; //px转为rpx moveX = moveX * pixelRatio; var maxMoveX = data.maxMoveX * pixelRatio; mainPageLeft = maxMoveX + moveX; drawerMenuLeft = maxMoveX -800 + moveX; } } this.setData({mainPageLeft: mainPageLeft, drawerMenuLeft: drawerMenuLeft}); } },
touchmove
事件,首先判斷是否處於滑動狀態,之後根據當前模式來計算主頁和選單頁的left值來產生滑動效果
onMainPageTouchend: function(e) { var data = this.drawerMenuMoveData; if (!data.check) { return; } data.check = false; data.firstTouchX = 0; var moveX = data.moveX; data.moveX = 0; var animation = wx.createAnimation({duration: 100}); var translateX = 0; var mainPageLeft = 0; var windowWidth = app.globalData.deviceInfo.windowWidth; if (data.state === 1) { if (moveX === 0 || moveX === data.maxMoveX) { data.state = (moveX === 0) ? 0 : 3; return; } mainPageLeft = moveX; //滑动距离是否超过窗口宽度一半 if (mainPageLeft > (windowWidth / 2)) { translateX = data.maxMoveX - moveX; data.state = 3; } else { translateX = -moveX; data.state = 0; } } else if (data.state === 2) { if (moveX === 0 || moveX === -data.maxMoveX) { data.state = (moveX === 0) ? 3 : 0; return; } //滑动距离是否超过窗口宽度一半 mainPageLeft = data.maxMoveX + moveX if (mainPageLeft > (windowWidth / 2)) { translateX = -moveX; data.state = 3; } else { translateX = -mainPageLeft; data.state = 0; } } translateX += data.lastTranlateX; data.lastTranlateX = translateX; animation.translateX(translateX).step(); this.setData({ animationData:animation.export() }); },
touchend
事件根據滑動的距離來判斷選單是否彈出,並建立滑動動畫
onMainPageTap: function(e) { var data = this.drawerMenuMoveData; if (data.state !== 3) { return; } data.state = 0; var translateX = -data.maxMoveX; translateX += data.lastTranlateX; data.lastTranlateX = translateX; var animation = wx.createAnimation({duration: 100}); animation.translateX(translateX).step(); this.setData({ animationData:animation.export() }); }tap
事件, 如果當前是彈出狀態,則將選單彈回rrreee
整體邏輯並不複雜,主要是做好狀態判斷和座標運算,但有些問題需要注意1: 微信小程式提供了rpx單位用於適配設備,但是各種滑動事件和動畫的單位通常是px,因此需要進行轉換,轉換方法為
rpx = px * pixelRatio
,其中
可以透過wx.getSystemInfoSync ()
取得2: 當元件使用透過
wx.createAnimation
建立的動畫時,小程式框架會為元件新增transform
屬性,其中translateX
值會和left
屬性交互作用,因此計算
值時需要處理
translateX的值。 3: 由於在真機環境下,頁面左滑(初始觸控點在左側邊界時)預設行為是返回上一頁或退出小程式(取決與是否為第一級頁),抽屜選單會和該行為發生衝突。
以上是微信小程式開發之抽屜選單實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!