weex元件-popup

yuwanli發表於2019-03-29

weex-coms

weex元件庫,這裡主要是分享下weex開發過程中封裝的一些weex元件,如popup、rich-text、lamp(努力持續更新),會分享自己遇到的一些坑,以及自己的一個做法和想法。

專案啟動

  • 下載當前工程
git clone https://github.com/yuwanli/weex-coms.git
複製程式碼
  • 安裝依賴
npm install --registry=https://registry.npm.taobao.org
複製程式碼
  • weex -v確認已經安裝weex-previewer,若沒有安裝則
npm install weex-previewer --save
複製程式碼
  • 安裝軟體

因為跑馬燈lamp是使用了bindingx的,所以建議下載bindingx的app,拉到頁面底部有二維碼,掃碼下載

  • 啟動頁面(以popup為例)
weex-previewer examples/popup/index.vue
複製程式碼
  • 掃碼檢視相應頁面

元件相關說明

元件 描述 引數
weex-popup 彈窗元件支援居中的淡入淡出、向上向下彈出的形式 showPopup(boolean):是否顯示彈窗,預設false
popupType(string):彈窗型別(centertopbottom),預設center
defaultClose(boolean):是否點選蒙層關閉彈窗,預設true
popupColor(string):蒙層顏色,預設rgba(0,0,0,0.6)
weex-lamp 跑馬燈元件,支援單條和容器式跑馬燈
weex-rich-text 富文字元件,彌補weex不支援v-html/v-text的短板
可能有人會說,不是有weex-ui了麼,我這個是不是在重複造輪子?

weex-ui確實封裝了很多實用的元件,很多元件我們也在實際的專案中在使用,所以當然我不會重複造輪子。封裝的這些元件時為了解決其他的一些問題,或者說是做了一些優化,實現的功能上也會有些許差異,具體的細節可以檢視

看元件相關內容時,我希望你已經(點選瞭解更多)

weex-popup

weex彈窗元件。支援居中淡入淡出彈窗及向下向上彈出彈窗。

演示圖
weex-popup

如何使用

  • 安裝weex-coms
npm install weex-coms -i
//若403無法安裝,則切換原有npm,再安裝
npm config set registry https://registry.npmjs.org/
複製程式碼
  • 元件引入
import {weexPopup} from 'weex-coms';
複製程式碼
  • 具體使用程式碼如下
<template>
  <div class="weex-demo">
    <text class="button" @click="showPopup = true" style="background-color:#f21818">居中淡入</text>
    <text class="button" @click="showPopup2 = true" style="background-color:yellowgreen">向上彈出</text>
    <text class="button" @click="showPopup3 = true" style="background-color:#3c8dbc">向下彈出</text>
    <weex-popup :show-popup="showPopup" @maskClick="maskClick('居中彈窗')" popup-type="center" @hidePopup="showPopup = false" :default-close=true popup-color="rgba(255,0,0,0.6)">
      <div class="popup center">
        <text class="popup__title">這是一個居中彈窗</text>
        <text class="popup__desc">點選蒙層關閉彈窗(default-close=true)</text>
        <text class="popup__desc">popup-type=center</text>
        <text class="popup__desc">居中彈窗預設顯示</text>
        <text class="popup__desc">這裡是內容這裡是內容</text>
      </div>
    </weex-popup>
    <weex-popup :show-popup="showPopup2" @maskClick="maskClick('向上彈出')" popup-type="bottom" @hidePopup="showPopup2 = false" :default-close=false  popup-color="rgba(0,255,0,0.6)">
      <div class="popup bottom">
        <div class="popup-wrapper">
          <text class="popup__title">這是一個向上彈出的彈窗</text>
          <text class="popup__desc">點選蒙層不可關閉彈窗(default-close=false)</text>
          <text class="popup__desc">popup-type=bottom</text>
          <text class="popup__desc">這裡是內容這裡是內容</text>
          <text class="popup__desc">這裡是內容這裡是內容</text>
        </div>
        <text class="popup__close" @click="showPopup2 = false">關閉</text>
      </div>
    </weex-popup>
    <weex-popup :show-popup="showPopup3" @maskClick="maskClick('向下彈出')" popup-type="top" @hidePopup="showPopup3 = false" :default-close=true  popup-color="rgba(0,0,255,0.6)">
      <div class="popup top">
        <div class="popup-wrapper">
          <text class="popup__title">這是一個向下彈出的彈窗</text>
          <text class="popup__desc">點選蒙層不可關閉彈窗(default-close=false)</text>
          <text class="popup__desc">popup-type=top</text>
          <text class="popup__desc">這裡是內容這裡是內容</text>
          <text class="popup__desc">這裡是內容這裡是內容</text>
        </div>
        <text class="popup__close" @click="showPopup3 = false">關閉</text>
      </div>
    </weex-popup>
  </div>
</template>
<script>
  import {weexPopup} from 'weex-coms';
  const modal = weex.requireModule('modal');
  export default {
    components: { 
      weexPopup,
    },
    data: {
      showPopup: true,
      showPopup2: false,
      showPopup3: false
    },
    methods: {
      maskClick(str) {
        modal.toast({
          message: `${str}蒙層點選`,
          duration: 0.2
        })
      }
    }
  };
</script>

<style lang="less" scoped>
.weex-demo{
  flex: 1;
}
.button{
    height: 88px;
    line-height: 88px;
    text-align: center;
    border-radius: 10px;
    margin-bottom: 20px;
    margin-top: 20px;
    color: #fff;
} 
.popup{
  background-color: #ffffff;
  align-items: center;
  &__title{
    font-size: 36px;
    font-weight: bold;
    margin-bottom: 24px;
  }
  &__desc{
    font-size: 28px;
    color: #aaa;
    line-height: 40px;
  }
  &__close{
    height: 88px;
    line-height: 88px;
    border-top-color: #ccc;
    border-top-width: 1px;
    border-top-style: solid;
    width: 750px;
    text-align: center;
    margin-top: 24px;
  }
  &-wrapper{
    align-items: center;
  }
}
.center{
   padding: 24px;
   width: 540px;
   border-radius: 10px;
}
.bottom{
  width: 750px;
  padding: 24px 24px 0;
  justify-content: space-between;
  align-items: center;
}
.top{
  width: 750px;
  padding: 24px 24px 0;
  justify-content: space-between;
  align-items: center;
}
</style>
複製程式碼
  • 可執行頁面進行檢視效果(如何執行安裝及檢視頁面可看前一頁
weex-previewer examples/popup/index.vue
複製程式碼

為啥要再次封裝?

weex-ui裡其實已經有popup元件了,使用也很簡單。但是遇到了一個比較麻煩的問題就是,要準確傳入height,這個就導致如果彈窗主體內容的高度是自適應的時候,需要先獲取內容的高度,準確給wxc-popup賦值height才行,所以weex-popup主要的功能點就是不用傳入heightwxc-popoup的具體用法如下(注意需要傳入height="500"):

<template>
  <div>
    <wxc-button text="Open Popup"
                @wxcButtonClicked="buttonClicked">
    </wxc-button>
    <wxc-popup height="500"
               pos="bottom"
               :show="isShow"
              @wxcPopupOverlayClicked="overlayClicked">
    </wxc-popup>
  </div>
</template>

<script>
  import { WxcButton, WxcPopup } from 'weex-ui';
  module.exports = {
    components: { WxcButton, WxcPopup },
    data: () => ({
      isShow: false
    }),
    methods: {
      buttonClicked () {
        this.isShow = true;
      },
      overlayClicked () {
        this.isShow = false;
      }
    }
  };
</script>
複製程式碼

為什麼一定要傳入一個高度呢?

核心原因就在於weex中單位不支援百分比

大家可以想下在h5中我們要一個元素的移入和移出的效果怎麼寫。以下是我封裝h5的彈窗的程式碼(擷取樣式的一部分)

.popup{
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 10000;
    background-color: rgba(0,0,0,0.6);
    transition: all 0.3s ease;
    &[data-type='top']{
        .popup-content{
            top: 0;
            left: 0;
            z-index: 3000;
            transition: all 0.3s ease;
        }
        &.fade-leave-active{
            background-color: rgba(0,0,0,0.6);
            .popup-content{
                opacity: 1;
                transform: translateY(0);
            }
        }
        &.fade-leave-to{
            background-color: rgba(0,0,0,0);
            .popup-content{
                opacity: 0;
                transform: translateY(-100%);
            }
        }
        &.fade-enter-active{
            background-color: rgba(0,0,0,0);
            .popup-content{
                opacity: 0;
                transform: translateY(-100%);
            }
        }
        &.fade-enter-to{
            background-color: rgba(0,0,0,0.6);
            .popup-content{
                opacity: 1;
                transform: translateY(0);
            }
        }
    }
    &-content{
        position: absolute;
    }
}
複製程式碼

核心就是利用了transitiontransform,大家注意,我們利用了100%,這樣我們就不用在乎元素的高度是多少,當一個置於訂部的元素,我們給加上transform: translateY(-100%);,元素就會完全移出螢幕,然後在配合transition,就可以達到我們想要的移入移出的效果。

那weex中呢,首先的侷限就在於無法用百分比,所以偏移量只能寫明確的畫素,所以這也是為什麼weex-ui要暴露一個height引數,需要明確指定高度。

似乎傳高度也沒有什麼問題,好多時候彈窗設計出來的時候高度就是固定的。但是如果彈窗高度是自適應的呢,這個高度該如何獲取?如果彈窗的內容是後端控制的呢?

weex-popup的實現過程

實現的過程(拿置底向上彈出的彈窗為例)

  1. 首次獲取主內容的高度h,並快取高度(居中顯示的無需獲取高度)
  2. 進行h高度的正向偏移使其離開螢幕
  3. 用animation進行tranition動畫使其向上淡入
  4. 用animation進行tranition動畫使其向下淡出
  5. 後續就3.4重複即可,無需再次獲取高度
1.獲取主內容的高度h
//...
const getHeight = () => new Promise((resolve) => {
    // center型別無需獲取高度,用flex佈局就可以讓自適應的內容居中
    // initFlag 用於只獲取一次高度,防止重複獲取
    if (!this.initFlag && this.popupType !== 'center') {
        this.initFlag = true;
        dom.getComponentRect(this.$refs.popupContent, (option) => {
            if (option.result) {
                this.contentHeight = option.size.height;
                // 不能立馬resolve,先讓元素進行h高度的偏移離開螢幕
                setTimeout(() => {
                    resolve();
                }, 30);
            }
        });
    } else {
        resolve();
    }
});
//...
複製程式碼
2. 進行h高度的正向偏移使其離開螢幕
<div :class="['weex-popup_content','weex-popup_content_'+popupType]" :style="computedStyle" ref="popupContent" @click.stop>
    <slot></slot>
</div>
...
computed: {
    computedStyle() {
        if (this.popupType === 'bottom') {
            return {
                transform: `translate(0px, ${this.contentHeight}px)`,
            };
        }
        if (this.popupType === 'top') {
            return {
                transform: `translate(0px, ${-1 * this.contentHeight}px)`,
            };
        }
        return {};
    },
},
...
複製程式碼
3.用animation進行tranition動畫使其向上淡出
...
let resObj = {
    popupMask: this.$refs.popupMask,
    popupContent: this.$refs.popupContent,
    maskStyle: {
        opacity: +flag,
    },
    contentStyle: {
        opacity: +flag,
    },
};
...
animation.transition(resObj.popupMask, {
    styles: resObj.maskStyle,
    duration: 500,
    timingFunction: 'ease',
}, () => {
    this.showInCurrent = flag;
});
getHeight().then(() => {
    animation.transition(resObj.popupContent, {
        styles: resObj.contentStyle,
        duration: 300,
        timingFunction: 'ease',
    });
});
複製程式碼

關於setTimeout

細心的同學會發現這邊用的是setTimeout,可能會有疑問,為啥不用nextTick? 因為實際使用發現nextTick會不準,跟weex的渲染機制有關,因為真正渲染的是原生的元素,vue中的nextTick是把回掉放在微任務佇列中(支援promise的情況下),放在渲染函式之後。可是這一套渲染機制放在weex中就不適用了。所以這邊也是踩了好幾次的坑,最後得出的結論,是30s

showInCurrent的作用

因為主內容整個用v-if='showPopup'包裹,當關閉彈窗的時候,我們希望彈窗消失是淡出(居中),或者移出(向上或者向下)的,而不是直接關閉掉的,所以這裡引入了另一個引數showInCurrent,可以把它理解為當前狀態彈窗是否可顯示。

showInCurrent的狀態變更過程
  • 當彈窗開啟時showPopup=true,showInCurrent會在動畫執行完成被置成true
  • 當彈窗關閉時showPopup=true,由於現在showInCurrenttrue所以彈窗一直顯示,當動畫執行完成,再把改成置成false,整個彈窗就關閉了

weex-popup待優化

當然待優化的點還有很多,這塊我會盡量持續更新,大家有什麼issue或者希望擴充套件的需求可以提,以下幾點會持續去完成

  • 支援h5版本(目前只支援在客戶端中使用即native)
  • 引數的擴充套件,如:動畫的執行時間、timingFunction
  • 效能的檢測和優化
  • 向左向右出的彈窗?

相關文章