HarmonyOS ArkUI之自定義組件側滑菜單(JS)
前言
鴻蒙這次API7更新除了新增TS聲明式開發之外,還有JS開發也增加了很多API,JS開發自定義組件越來越方便了。
本項目基于ArkUI中JS擴展的類Web開發范式,關于語法和概念直接看官網官方文檔地址:
基于JS擴展的類Web開發范式1 基于JS擴展的類Web開發范式2
本文介紹最新出的插槽用法,實現側滑菜單、支持兩種風格、支持快速滑動打開關閉。
ArKUI系列文章
- 【HarmonyOS ArkUI之仿微信朋友圈圖片預覽】
- 【HarmonyOS ArkUI之仿微信圖片選擇】
- 【HarmonyOS ArkUI之自定義組件側滑菜單(JS)】
效果演示
風格一:內容頁在菜單上面風格二:內容頁在菜單下面


主要知識點
觸摸事件、自定義組件父子組件傳遞參數、api=7新出的slot插槽
實現思路
自定義組件的邏輯都在此目錄下:entry/js/default/pages/drawer
主要使用onTouch相關事件,滑動改變菜單布局或內容布局的left偏移量,手指抬起使用動畫完成偏移量
1、onTouch相關事件
只貼出了關鍵代碼
- /**
- * 觸摸按下
- */
- onTouchStart(event) {
- // 記錄首次按下的x坐標
- this.pressX = event.touches[0].localX
- // 記錄上次的x坐標
- this.lastX = this.pressX
- .....
- },
- /**
- * 觸摸移動
- */
- onTouchMove(event) {
- // 當前x坐標
- let localX = event.touches[0].localX
- // 計算與上次的x坐標的偏移量
- let offsetX = this.lastX - localX;
- // 記錄上次的x坐標
- this.lastX = localX
- // 累計偏移量
- this.offsetLeft -= offsetX
- // 設置偏移量的范圍
- .....
- }
- **
- * 觸摸抬起
- */
- onTouchEnd(event) {
- ......
- // 手指抬起,根據情況,判斷toX的值,也就是判斷關閉或開啟菜單的情況
- // 當移動偏移量大于菜單一半寬度,完全打開菜單,否則反之
- if (this.offsetLeft > this.menuWidth / 2) {
- toX = this.menuWidth
- } else {
- toX = 0
- }
- ......
- // 開啟動畫
- this.startAnimator(toX)
- }
- /**
- * 開啟動畫
- */
- startAnimator(toX) {
- // 設置動畫參數
- let options = {
- duration: ANIMATOR_DURATION, // 持續時長
- fill: 'forwards', // 啟停模式:保留在動畫結束狀態
- begin: this.offsetLeft, // 起始值
- end: toX // 結束值
- };
- // 更新動畫參數
- this.animator.update(options)
- // 監聽動畫值變化事件
- this.animator.onframe = (value) => {
- this.offsetLeft = value
- this.changeMenuOffsetLeft()
- }
- // 開啟動畫
- this.animator.play()
- },
2、showStyle
0 第一種樣式下,解決設置z-index之后菜單界面在內容下面,但點擊事件卻還在內容上面的問題。
初始化設置left偏移量
動畫結束,判斷菜單是否關閉,關閉直接設置菜單偏移量為負的菜單寬度
注意:目前使用插槽之后,預覽器不走生命周期方法:onShow。
- export default {
- // 使用外部傳入
- props: ['showStyle'],// 顯示樣式:0菜單在下面,1菜單在上面
- ......
- }
- **
- * 界面顯示
- */
- onShow() {
- .....
- // 設置菜單偏移量為負的菜單寬度,為了解決z-index設置后,菜單界面到內容下面,
- // 事件還停留到內容上面,導致點擊菜單區域,響應的還是菜單點擊事件
- this.menuOffsetLeft = -this.menuWidth
- }
3、使用具名插槽封裝
- <div id="drawer-container" class="drawer-container" on:touchstart="onTouchStart"
- on:touchmove="onTouchMove" on:touchend="onTouchEnd">
- <div class="drawer-content" style="left : {{ showStyle == 0 ? offsetLeft : 0 }} px;
- z-index : {{ zIndexContent }};" on:click="closeMenu">
- <!--具名插槽,根據名稱加入對應的插槽中-->
- <slot name="content"></slot>
- </div>
- <stack class="drawer-menu" style="z-index : {{ zIndexMenu }};">
- <div class="drawer-menu-background" style="opacity : {{ menuBgOpacity }};"></div>
- <div style="width : {{ menuWidth }} px; height : 100%;
- left : {{ menuOffsetLeft }} px;" on:click="clickMenu">
- <!--具名插槽,根據名稱加入對應的插槽中-->
- <slot name="menu"></slot>
- </div>
- </stack>
- </div>
4、index頁面使用
- <!--引入自定義組件-->
- <element name='drawer' src='../drawer/drawer.hml'></element>
- <div class="container">
- <!--通過傳值設置樣式-->
- <drawer show-style="0">
- <!--根據名稱加入對應的插槽中-->
- <div slot='content' class="content-layout">
- <div class="title-bar">
- <text>主頁</text>
- </div>
- <div class="mainpage-content">
- <text class="content1">我是內容頁面</text>
- <text class="content2">V1.0.0</text>
- <text class="content2">梁迪迪</text>
- </div>
- </div>
- <div slot='menu' class="menu-layout">
- <div class="my-info">
- <image src="common/images/head_photo.png"></image>
- <text>登錄 | 注冊</text>
- </div>
- <div class="menu-content">
- <div for="{{ listMenu }}" tid="{{ $item.id }}" on:click="menuSkip({{ $item.name }})">
- <image src="{{ $item.icon }}"></image>
- <text>{{ $item.name }}</text>
- </div>
- </div>
- </div>
- </drawer>
- </div>
結尾
每天進步一點點、需要付出努力億點點。