日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

快应用中实现自定义抽屉组件

發(fā)布時間:2023/12/20 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 快应用中实现自定义抽屉组件 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. ?什么是抽屜組件

抽屜組件是一種特殊的彈出面板,可以模擬手機App中推入拉出抽屜的效果。抽屜一般具有如下特點:

  • 抽屜可顯示在左邊,也可以顯示在右邊;
  • 抽屜寬度可定制;
  • 抽屜有遮罩層,點擊遮罩層可收起抽屜;
  • 手勢滑動可呼出抽屜;
  • ?????? 抽屜(Drawer)組件結構分為控制器和抽屜內容兩部分。 一般來說,controls都是按鈕、圖標之類的可點擊的組件,類似真實抽屜的把手,content是抽屜內部的東西,每個抽屜的content都是不一樣的。點擊controls可以觸發(fā)content的顯示和收起。 因此,在使用抽屜組件的頁面布局可以抽象成如下結構:

    <div class=“page"><div class=“controls"><image></image></div><stack class=“drawer_container”><div class=“page_content”>…</div><drawer class="drawer"><div class=“content”>…</div></drawer ></stack> </div>

    2.實現(xiàn)步驟

    ??? 抽屜組件屬于一種擴展能力,當前快應用已有的組件是無法滿足的,需要自定義組件實現(xiàn)。

    2.1自定義子組件

    ????? 抽屜外觀都是通用的,但是抽屜內部格局content不一樣,在設計的時候,不能直接寫死content布局,否則一旦content部分的UI有變化,會導致子組件也要修改,違背了代碼設計中的“開閉”原則。

    ???? 所以,我們子組件drawer.ux中,使用了slot 組件來承載父組件中定義的content,由使用drawer組件的頁面來完成content布局,如下圖所示:

    2.2子組件設計

    支持的屬性如下:

    屬性

    類型

    默認值

    描述

    mode

    String

    left

    設置抽屜的顯示位置,支持left和right

    mask

    boolean

    true

    抽屜展開時是否顯示遮罩層

    maskClick

    Boolean

    true

    點擊遮罩層是否關閉抽屜

    width

    Number

    320px

    抽屜寬度

    支持的事件:

    事件名稱

    參數

    描述

    drawerchange

    {showDrawer:booleanValue}

    抽屜收起、展開的回調事件

    2.3抽屜展開和收起

  • 抽屜默認是關閉不顯示的,通過“display:?none;” 來隱藏。
  • 收起、展開通過X軸的平移動畫控制,收起時,移到屏幕之外,展開時平移到可視區(qū)域。不管是展開還是收起,都是平滑的動畫效果。
  • 抽屜顯示在左側還是右側,是通過div的flex-direction控制的,顯示在左側時,設置row,顯示在右側時,設置為row-reverse。
  • 圖1 左抽屜打開、收起style

    ?? 圖2 有抽屜打開、收起style

    ??圖3 左抽屜動畫

    ?? 圖4? 右抽屜動畫

    2.4遮罩層實現(xiàn)

    遮罩層初始狀態(tài)不顯示,通過“display:?none;” 來隱藏。抽屜展示時,顯示遮罩層,收起時,不顯示,遮罩層使用透明度實現(xiàn)。

    2.5父子組件通信

  • 父組件通過parentVm.$broadcast()向子組件傳遞抽屜打開、收起的事件,子組件通過$on()監(jiān)聽事件和參數。
  • 子組件通過$watch()方法監(jiān)聽抽屜顯示模式mode屬性的變化,從而修改css樣式,讓其在正確的位置顯示抽屜。
  • 子組件通過drawerchange事件及參數通知父組件。
  • 2.6手勢呼出抽屜

    ?在抽屜處手勢滑動,呼出抽屜,需要監(jiān)聽touchstart和touchend事件。注意滑動范圍,只有在抽屜邊緣處呼出抽屜,其其他位置不呼出。

    3.總結

    實現(xiàn)抽屜組件,您可以從中學會如下知識點:

  • 熟悉快應用子組件的設計和屬性定義;
  • 熟悉父子組件通信;
  • 熟悉動畫樣式的實現(xiàn);
  • 學會如何實現(xiàn)一個遮罩層。
  • 欲了解更多詳情,請參見:
    華為官網:
    https://developer.huawei.com/consumer/cn/forum/topic/0202636422958390131?fid=18?ha_source=zzh???????

    最后附上完整的實現(xiàn)代碼:

    抽屜drawer.ux

    <template><div id="drawercontent" style="display:flex;position:absolute;width:100%;height:100%;top:0;left:0;bottom: 0; flex-direction: {{flexdirection}}" onswipe="dealDrawerSwipe"><div class="{{maskstyle}}" onclick="close('mask')"></div><div id="unidrawercontent" class="{{unidrawerstyle}}" style="width:{{drawerWidth}}+'px'}"><slot></slot></div></div> </template><style>.stack {flex-direction: column;height: 100%;width: 100%;}.uni-mask-open {display: flex;height: 100%;width: 100%;position: absolute;background-color: rgb(0, 0, 0);opacity: 0.4;}.uni-mask-closed {height: 100%;width: 100%;position: absolute;background-color: rgb(0, 0, 0);display: none;}.uni-drawer {display: none;height: 100%;}.uni-drawer-open-left {display: flex;height: 100%;animation-name: translateX;animation-timing-function: linear;animation-fill-mode: forwards;animation-duration: 300ms;}.uni-drawer-closed-left {display: flex;height: 100%;animation-name: translateXReverse;animation-timing-function: linear;animation-fill-mode: forwards;animation-duration: 600ms;}.uni-drawer-open-right {display: flex;height: 100%;flex-direction: row-reverse;animation-name: translateXRight;animation-timing-function: linear;animation-fill-mode: forwards;animation-duration: 300ms;}.uni-drawer-closed-right {display: flex;height: 100%;flex-direction: row-reverse;animation-name: translateXRightReverse;animation-timing-function: linear;animation-fill-mode: forwards;animation-duration: 600ms;}@keyframes translateX {from {transform: translateX(-110px);}to {transform: translateX(0px);}}@keyframes translateXReverse {from {transform: translateX(0px);}to {transform: translateX(-750px);}}@keyframes translateXRight {from {transform: translateX(300px);}to {transform: translateX(0px);}}@keyframes translateXRightReverse {from {transform: translateX(0px);}to {transform: translateX(750px);}} </style><script>module.exports = {props: {/*** 顯示模式(左、右),只在初始化生效*/mode: {type: String,default: ''},/*** 蒙層顯示狀態(tài)*/mask: {type: Boolean,default: true},/*** 遮罩是否可點擊關閉*/maskClick: {type: Boolean,default: true},/*** 抽屜寬度*/width: {type: Number,default: 320}},data() {return {visibleSync: false,showDrawer: false,watchTimer: null,drawerWidth: 600,maskstyle: 'uni-mask-closed',unidrawerstyle: 'uni-drawer',flexdirection: 'row'}},onInit() {console.info("drawer oninit");this.$on('broaddrawerstate', this.drawerStateEvt);this.drawerWidth = this.width;if(this.mode=="left"){this.flexdirection="row";}else{this.flexdirection="row-reverse";}this.$watch('mode', 'onDrawerModeChange');},onDrawerModeChange: function (newValue, oldValue) {console.info("onDrawerModeChange newValue= " + newValue + ", oldValue=" + oldValue);if (newValue === 'left') {this.flexdirection = 'row';} else {this.flexdirection = 'row-reverse';}},drawerStateEvt(evt) {this.showDrawer = evt.detail.isOpen;console.info("drawerStateEvt this.showDrawer= " + this.showDrawer);if (this.showDrawer) {this.open();} else {this.close();}},close(type) {// 抽屜尚未完全關閉或遮罩禁止點擊時不觸發(fā)以下邏輯if ((type === 'mask' && !this.maskClick) || !this.visibleSync) {return;}console.info("close");this.maskstyle = 'uni-mask-closed';if (this.mode == "left") {this.unidrawerstyle = 'uni-drawer-closed-left';} else {this.unidrawerstyle = 'uni-drawer-closed-right';}this._change('showDrawer', 'visibleSync', false)},open() {// 處理重復點擊打開的事件if (this.visibleSync) {return;}console.info("open this.mode="+this.mode);this.maskstyle = 'uni-mask-open';if (this.mode == "left") {this.unidrawerstyle = 'uni-drawer-open-left';} else {this.unidrawerstyle = 'uni-drawer-open-right';}this._change('visibleSync', 'showDrawer', true)},_change(param1, param2, status) {this[param1] = status;if (this.watchTimer) {clearTimeout(this.watchTimer);}this.watchTimer = setTimeout(() => {this[param2] = status;this.$emit('drawerchange', {'showDrawer':status});}, status ? 50 : 300)},dealDrawerSwipe: function(e) {console.info("dealDrawerSwipe");let direction=e.direction;if (this.mode == "left") {if(direction=="left"){this.close();}}else{if(direction=="right"){this.close();}}},} </script>頁面hello.ux: <import name="drawer" src="../Drawer/drawer.ux"></import> <template><!-- Only one root node is allowed in template. --><div class="container"><div class="title"><div class="icon" @click="isOpen"><text class="icon-item" for="[1,1,1,1]"></text></div><text class="page-title">模擬drawer組件</text></div><stack style="width: 100%;height:100%;" ontouchstart="touchstart" ontouchend="touchend"><div class="content"><text style="color: #0faeff;">點擊左上角按鈕滑出左側抽屜</text><text class="txt" onclick="switchLocation">切換抽屜滑出位置左或右</text><text style="color: #0faeff;margin-left: 10px;margin-right: 10px">手指在屏幕左側邊緣右滑亦可滑出左側抽屜,手指在屏幕右側邊緣左滑亦可滑出右側抽屜</text><text style="color: #0faeff;margin-top: 20px;margin-left: 10px;margin-right: 10px">滑出抽屜的寬度默認為600px(即最大可設置的寬度,最小可設置寬度為父容器的100px), 如果輸入的值超出500則按最大可設置寬度顯示,小于最小可設置寬時則按最小可設置寬度顯示</text><input id="input" class="input" type="number" placeholder="請輸入寬度值,單位為px" value="{{inputValue}}" onchange="changeValue" /><text style="color: #0faeff;">鍵盤收起后,即可滑動或點擊呼出抽屜</text><text class="txt" onclick="maxWidth">設置抽屜為最大寬度</text><text class="txt" onclick="minWidth">設置抽屜為最小寬度</text></div><drawer id="drawer" mode="{{drawerShowMode}}" width="{{drawerWidth}}" mask-click="true" @drawerchange="change"><tabs class="tabs" style="width: {{drawerWidth}}px;"><tab-content class="tabcontent"><list class="list"><block for="listarray"><list-item class="list-item" type="item" onclick="chooseItem($idx)"><text>第{{ $item }}章測試目錄</text></list-item></block></list><text>this is second page</text></tab-content><tab-bar class="tabbar"><text class="text">part one</text><text class="text">part two</text></tab-bar></tabs></drawer></stack></div> </template><style>.container {flex-direction: column;}/* 自定義內容屬性 */.content {flex-direction: column;justify-content: center;align-items: center;width: 100%;height: 100%;}.txt {width: 80%;height: 80px;background-color: #0faeff;border-radius: 10px;text-align: center;margin-left: 80px;margin-top: 10px;margin-bottom: 10px;}.input {width: 80%;height: 80px;border: 1px solid #000000;margin-left: 80px;}/* 標題屬性 */.title {height: 120px;width: 100%;align-items: center;background-color: #0faeff;padding-left: 20px;}.page-title {font-size: 40px;padding-left: 150px;}.icon {width: 60px;height: 60px;flex-direction: column;justify-content: space-around;}.icon-item {height: 4px;background-color: rgb(212, 212, 212);width: 100%;}.tabs {height: 100%;background-color: rgb(248, 230, 230);}.tabcontent {width: 100%;height: 90%;}.tabbar {width: 100%;height: 10%;}.text {width: 50%;height: 100%;font-size: 50px;text-align: center;}.list {flex: 1;width: 100%;}.list-item {height: 90px;width: 100%;padding: 0px 20px;border-bottom: 1px solid #f0f0f0;align-items: center;justify-content: space-between;} </style><script>import prompt from '@system.prompt';module.exports = {data: {componentData: {},display: false,listarray: '',drawerWidth: 360,inputValue: '',drawerShowMode: 'right',movestartX: 0},onInit() {this.listarray = this.getList(20);},isOpen() {this.display = !this.display;if (this.display) {this.showDrawer();} else {this.closeDrawer();}},// 打開抽屜showDrawer(e) {this.$broadcast('broaddrawerstate', {isOpen: true})},// 關閉抽屜closeDrawer(e) {this.$broadcast('broaddrawerstate', {isOpen: false})},// 抽屜狀態(tài)發(fā)生變化觸發(fā)change(e) {console.info("change e=" + JSON.stringify(e));this.display = e.detail.showDrawer;},getList(num) {let list = []for (let i = 1; i <= num; i++) {list.push(i)}return list},switchLocation() {if (this.drawerShowMode === 'left') {this.drawerShowMode = 'right';} else {this.drawerShowMode = 'left';}},changeValue(e) {if (e.value >= 600) {this.drawerWidth = 600} else if (e.value <= 300) {this.drawerWidth = 300} else {this.drawerWidth = e.value}console.log("hjj", this.drawerWidth);if (e.value.length === 3) {this.$element('input').focus({ focus: false })}},maxWidth() {this.drawerWidth = 600},minWidth() {this.drawerWidth = 300},chooseItem(index) {prompt.showToast({message: `該內容為簡單示例,點擊了第${index + 1}條`,})},touchstart(e) {console.info("touchstart");this.movestartX = e.touches[0].offsetX;},touchend(e) {console.info("touch end e:" + JSON.stringify(e));let moveEndX = e.changedTouches[0].offsetX;if (this.drawerShowMode === "left") {//在屏幕左邊緣從左往右邊滑動時,呼出抽屜if (this.movestartX < 30) {let dis = moveEndX - this.movestartX;if (dis > 30) {this.showDrawer();}}} else {//在屏幕右邊緣從右往左邊滑動時,呼出抽屜if (this.movestartX > 720) {let dis = moveEndX - this.movestartX;if (dis < -30) {this.showDrawer();}}}},} </script>

    總結

    以上是生活随笔為你收集整理的快应用中实现自定义抽屉组件的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。