1. 程式人生 > 程式設計 >小程式原生實現左滑抽屜選單

小程式原生實現左滑抽屜選單

目錄
  • WXS 響應事件
  • 方案A
    • 頁面結構和樣式
  • WXS 事件回撥函式
    • WXS
      • 遮罩層
        • 方案B
          • 為什麼要使用 WXS
            • 結語 & 參考資料
              • 參考資料:

                在移動端,側滑選單是一個很常用的元件(通常稱作 Drawer,抽屜)。因為現在手機螢幕太大,點選角落的選單按鈕明顯不如在螢幕中間滑動方便。

                相比其他平臺,小程式的元件庫支援明顯還不夠完善,各個框架也還不太成熟。由於之前使用框架的過程中被各種神祕bug搞的頭禿,還是用回了原生環境。

                最近研究了一下如何在原生框架中實現滑動抽屜選單效果,本來以為很麻煩,結果發現其實只需要幾十行程式碼,而且可以類比實現很多靈活的效果。感覺現在網上相關資料較少,因此在此分享一下。除了文中貼出的程式碼塊,也可以點選連結在小程式開發工具中預覽效果、檢視程式碼片段。這裡實現了三種常見效果,先看一下動圖,下面將一一講解程式碼實現。

                A 選單在上層

                小程式原生實現左滑抽屜選單

                A2 選單在上層,下層遮罩

                小程式原生實現左滑抽屜選單

                B 選單在下層

                小程式原生實現左滑抽屜選單

                WXS 響應事件

                手勢控制選單的原理很簡單:小程式提供了一系列觸控手勢觸發的事件,包括觸控開始、移動、結束(touchstart,touchmove,touchend)等等。在這些事件上繫結自定義的事件響應函式,即可實現根據手勢開啟關閉選單的操作。

                出於效能考慮,事件處理函式最好放在 WXS、而不是 檔案中。具體原理與小程式的執行環境有關,感興趣的話可以去文末檢視。WXS 是小程式的專用指令碼語言(WXS 與 JS 的關係相當於 WXSS 與 的關係),語法和 JS 類似,有部分區別,比如:

                • 與 JS 隔離,不能呼叫其他 檔案中定義的函式,也不能呼叫小程式提供的API
                • 只能響應小程式內建元件的事件,不支援自定義元件的事件回撥
                • 變數與函式預設為模組私有,通過 module.exports 對外暴露
                • 使用標籤在 WXML 中引入使用(必須使用相對路徑)

                wxs 檔案和 wxml 檔案中的基本寫法如下:

                // index.wxs
                
                function touchStart(e,ins)MJrZBFz {}
                function touchMove(e,ins) {}
                function touchEnd(e,ins) {}
                
                module.exports = {
                  touchstart: touchStart,touchmove: touchMove,touchend: touchEnd
                }
                <wxs module="drawer" src="./index.wxs"></wxs>
                
                <view bindtouchstart="{{drawer.touchstart}}"
                      bindtouchmove="{{drawer.touchmove}}" 
                      bindtouchend="{{drawer.touchend}}">
                </view>
                
                

                方案A

                頁面結構和樣式

                小程式原生實現左滑抽屜選單

                這是最常見的抽屜選單樣式之一,滑動主體內容不動,選單在上層顯示。首先寫出基本的 HTML 結構和 CSS 樣式(省略了一些美觀方面的樣式表):

                <wxs module="drawer" src="./index.wxs"></wxs>
                
                <view>
                  <view class="main" bindtouchstart="{{drawer.touchstart}}"
                    bindtouchmove="{{drawer.touchmove}}" bindtouchend="{{drawer.touchend}}">
                    <view>
                      右滑顯示側邊選單 方案A
                    </view>
                  </view>
                
                  <view class="drawer" data-drawerwidth="150">
                    <view class="drawer-item">drawerA</view>
                    <view wx:for="{{[1,2,3]}}" class="drawer-item">
                      <text>menu item {{item}}</text>
                    </view>
                  </view>
                </view>
                
                

                WXML 中的幾個重點:

                • 正確引入 wxs 模組(必須用相對路徑)
                • 進行滑動手勢時選單是隱藏的,所以實際上是在主介面上進行滑動,所以三個滑動事件回撥需要繫結在主體內容的 view 上面
                • 進行移動的是 .drawer 元素,需要設定好 class 屬性方便獲取
                • 抽屜元素的 data-drawerwidth 屬性通過 dataset 傳值給 wxs 指令碼,規定了選單的寬度,需要和樣式保持一致

                WXSS 沒啥好說的,寫在註釋裡了:

                .main {
                  height: 100vh;
                  width: 100%;
                  position: absolute;
                }
                
                .drawer {
                  height: 100vh;
                  width: 150px;
                  position: absolute;
                  transition: transform 0.4s ease; /* 位移使用transform實現,加個過渡動畫更順滑 */
                  left: -150px;  /* width、偏移與WXML中的數值保持一致,初始狀態隱藏選單 */
                }

                WXS 事件回撥函式

                wxs 函式有兩個入www.cppcns.com

                • event 是小程式事件物件,並在此基礎上多了觸發事件的元件的例項 event.instance
                • ownerInstance 是觸發事件的元件的父元件(頁面)的例項

                wxs 中元件例項是封裝好的 ComponentDescriptor 物件,能夠操作元件的 dataset、設定 style、class 等,對於互動動畫基本夠用了。更多用法可參考文件。

                var wxsFunction = function(event,ownerInstance) {
                    var instance = ownerInstance.selectComponent('.classSelector') // 返回元件的例項
                    instance.setStyle({
                        "font-size": "14px" // 支援rpx
                    })
                    instance.getDataset()
                    instance.setClass(className)
                
                    return false // 不往上冒泡,相當於呼叫了同時呼叫了stopPropagation和preventDefault
                }
                
                

                WXS 指令碼

                條件判斷為主,邏輯沒啥特別的,結合情景不難理解

                • 不要用 let,const 宣告變數,會報錯
                • 把設定 transform 屬性 X 位移的程式碼簡單封裝一下,看起來更美觀
                • judge point 類似於吸附效果,就是選單劃出來超過某一位置就自動把剩餘部分開啟
                var startmark = 0;
                var status = 0;  // 選單開閉狀態
                var JUDGEPOINT = 0.7;
                
                function touchStart(e,ins) {
                  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
                  startmark = pageX;
                }
                
                function touchMove(e,ins) {
                  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
                  var offset = pageX - startmark;
                  var drawerComp = ins.selectComponent('.drawer');
                  var drawerWidth = drawerComp.getDataset().drawerwidth;
                
                  if (offset > 0 && status == 0) {
                    setCompTransX(drawerComp,Math.min(drawerWidth,offset))
                  } else if (offset < 0 && status == 1) {
                    setCompTransX(drawerComp,Math.max(0,offset))
                  }
                }
                
                function touchEnd(e,ins) {
                  var pMJrZBFzageX = (e.touches[0] || e.changedTouches[0]).pageX;
                  var offset = pageX - startmark;
                  var drawerComp = ins.selectComponent('.drawer');
                  var drawerWidth = drawerComp.getDataset().drawerwidth;
                
                  if (offset > 0 && status == 0) {
                    if (offset < drawerWidth * JUDGEPOINT) {
                      setCompTransX(drawerComp,0);
                    } else {
                      setCompTransX(drawerComp,drawerWidth);
                      status = 1;
                    }
                  } else if (offset < 0) {
                    setCompTransX(drawerComp,0);
                    status = 0;
                  }
                }
                
                function setCompTransX(comp,x) {
                  comp.setStyle({
                    transform: 'translateX(' + x + 'px)',})
                }
                
                module.exports = {
                  touchstart: touchStart,touchend: touchEnd
                }

                遮罩層

                點選文首或文末連結在小程式開發工具中檢視完整程式碼。

                遮罩層只需要在選單和主容http://www.cppcns.com器之間增加一個 view 即可:

                <view class="main"></view>
                <view class="mask" data-maxopacity="0.6"></view>
                <view class="drawer" data-drawerwidth="150"></view>
                

                樣式中很重要的是這個 pointer-events 屬性,設定為 none 之後點選動作會穿透這個 view 達到下層。因為遮罩層不像抽屜是處在畫面以外的,它雖然透明度為0,但實際上一直覆蓋在 .main 上方,如果不加這個屬性,所有對 .main 的點選操作都會點到 .mask 上面,那不管是滑動還是其他按鈕都無效了。

                .mask {
                  height: 100vh;
                  width: 100%;
                  position: fixed;
                  transition: opacity 0.4s ease;
                  opacity: 0;
                  pointer-events: none;
                  background-color: #548CA8;
                }
                

                wxs 指令碼也基本完全一致,只需要以相似的方法獲取到 .mask 的例項以及 dataset 中的透明度引數,並在設定位移屬性的同時設定遮罩層的透明度屬性即可。

                function setDrawer(x) {
                  setCompTransX(drawerComp,x);
                  maskComp.setStyle({
                    opacity: x / drawerWidth * maskOpacity,})
                }
                

                方案B

                點選文首或文末連結在小程式開發工具中檢視完整程式碼。

                方案B 與方案A 的區別主要在於滑動時是主介面向右移動露出下層的選單,其餘各部分實現並無不同。這裡只貼出主要差異的部分。

                因為移動的是 .main 元素,因此把寬度配置資料放到了該元素的標籤中,這樣可以少獲取一個元件例項。

                <view class="drawer"></view>
                
                <view class="main" 
                      data-drawerwidth="150" 
                      bindtouchstart="{{drawer.touchstart}}"
                      bindtouchmove="{{drawer.touchmove}}" 
                      bindtouchend="{{drawer.touchend}}">
                </view>
                
                

                transition 動畫屬性也放在 .main 中,.drawer 的偏移不需要了。

                .main {
                  height: 100vh;
                  width: 100%;
                  position: absolute;
                  transition: transform 0.4s ease;
                }
                
                .drawer {
                  height: 100vh;
                  width: 150px;
                  position: absolute;
                }
                
                

                wxs 指令碼中除了獲取的元件不同外,連設定位移都不需要改。

                function touchMove(e,ins) {
                  var pageX = (e.touches[0] || e.changedTouches[0]).pageX;
                  var offset = pageX - startmark;
                  var mainComp = ins.selectComponent('.main');
                  var drawerWidth = mainComp.getDataset().drawerwidth;
                
                  if (offset > 0 && status == 0) {
                    setCompTransX(mainComp,offset))
                  } else if (offset < 0 && status == 1) {
                    setCompTransX(mainComp,offset))
                  }
                }
                
                

                為什麼要使用 WXS

                小程式在很多地方與 web 開發很像,但底層存在一些區別。中,渲染和指令碼執行在同一個執行緒中執行(因此執行指令碼可能會導致頁面整個卡死);小程式在不同的執行緒中分別執行邏輯層(JS指令碼)和渲染層(WXML和WXSS),執行緒間經由客戶端(Native)進行通訊。

                小程式原生實現左滑抽屜選單

                因此,如果使用 JS 指令碼響應事件,每次觸發 touchmove 都會產生兩次程序間通訊(下圖左所示),通訊開銷較大;同時“setData 渲染也會阻塞其它指令碼執行”(文件這麼說的,我也不知道為什麼)。由於一次手勢會觸發巨量的 touchmove 事件,上述原因會造成動畫的卡頓。

                而 WXS 函式執行在檢視層,不存在上述問題(下圖右所示)。

                小程式原生實現左滑抽屜選單

                結語 & 參考資料

                以上就是原生小程式的幾種抽屜選單實現方法,希望對你有所幫助;對於文中存在的疏漏歡迎討論指正。

                點選連結可以在小程式開發工具中檢視完整程式碼(使用小程式開發工具的程式碼片段分享,對開發工具版本有一定要求)。他這個分享程式碼片段有點玄學,如果直接開啟失敗,可以在登入後嘗試在“專案-匯入程式碼片段”中直接輸入連結或連結最後一段ID。

                參考資料:

                小程式框架/檢視層/事件系統/WXS 響應事件

                官方 demo

                小程式宿主環境

                到此這篇關於小程式原生實現左滑抽屜選單的文章就介紹到這了,更多相關小程式 左滑抽屜選單內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!