天天看點

小程式如何優雅的實作半頁面彈窗

背景

半頁面彈窗是前端移動端中非常常用的元件,效果如下:

小程式如何優雅的實作半頁面彈窗

一個使用者體驗好的半頁面彈窗,應該有如下特點:

  1. 出現彈窗時,背景有個黑色半透明遮罩層,應該是逐漸出現的,透明度漸變。
  2. 出現彈窗時,彈窗是從下至上移動出現的,而不是閃現的。
  3. 關閉彈窗時,背景色也要逐漸消失,彈窗從上至下移動走。

一個好用的半頁面彈窗,應該有如下特點:

  1. 動畫樣式實作不耦合彈窗的高度。無論裡面放多少内容,彈窗有多高,不改樣式的情況下,漸入漸出的動畫都表現正常,且出現時,底部應該貼合螢幕底部。
  2. 控制出現/隐藏的方法簡單,自動播放出現/隐藏的漸變動畫,而非手動調用API播放漸變動畫。

本文參考WeUI的實作,介紹如何優雅的實作半頁面彈窗。

遮罩層樣式

.mask {
    position: fixed;
    z-index: 1000;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    background: rgba(0,0,0,.6);
}      

遮罩層動畫

我們可以讓遮罩層預設是隐藏狀态(opacity為0),當要展示時,需要開發者設定一個新的class(比如叫show)給mask對應的标簽。

然後,我們給遮罩層設定個transition即可。代碼如下:

.mask{
    opacity: 0;
    visibility: hidden;
    transition: opacity .3s;
}
.mask.show {
    opacity: 1;
    visibility: visible;
}      

注意,我們必須設定​

​visibility​

​​屬性。因為當你設定​

​opacity​

​​後,隻是修改了透明度,它會在頂部擋住所有的互動事件。是以我們需要修改​

​visibility​

​,這樣它就不會擋住那些互動事件了。

彈窗樣式

為了讓彈窗無論什麼高度都可貼緊螢幕底部,我們直接設定​

​bottom: 0​

​即可。

此外,為了防止彈窗太低不美觀,彈窗太高擋住頁面,最好設定個​

​min-height​

​​和​

​max-height​

​​。歡迎閱讀​​《你真的了解 width height 嗎?》​​​了解​

​min-height​

​​和​

​max-height​

​的優先級。

.dialog {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    min-height: 255px;
    max-height: 75%;
    z-index: 5000;
    border-top-left-radius: 12px;
    border-top-right-radius: 12px;
    overflow: hidden;
    padding: 0 24px;
    padding: 0 24px constant(safe-area-inset-bottom) 24px;
    padding: 0 24px env(safe-area-inset-bottom) 24px;
}      

也許你會好奇為什麼設定3個​

​padding​

​​:下面的合法的padding會覆寫前面的,​

​constant​

​​和​

​env​

​​可能在一些浏覽器并不支援,這時候下面的就不合法了,會使用兜底的​

​0 24px​

​這個padding值。

為什麼要用​

​env(safe-area-inset-bottom)​

​呢?主要是iPhone最近幾年的産品,底部有一個條帶,如下圖:

小程式如何優雅的實作半頁面彈窗

如果你貼緊底部放置文本,使用者是看不清的。是以就有了「安全區域」概念,給彈窗設定底部的安全區域到padding裡,保證了彈窗内容不會跟這個條帶重合,保障了iOS使用者體驗。

在近代iPhone系列産品中,env(safe-area-inset-bottom)通常是34px,其它産品中,這個值是0。

彈窗動畫

如果沒有​

​.show​

​,就隐藏,把他放在螢幕底下,通過transform的translateY實作。為了讓動畫跟元素高度像素值無關,我們使用100%,代表元素整體高度。

.dialog {
    transform: translateY(100%);
    transition: transform .3s;
}
.dialog.show {
    transform: translateY(0);
}      

看看wxml怎麼寫

這裡以小程式為例:

<view aria-role="dialog" aria-modal="true" aria-hidden="{{!showDialog}}">
  <view class="mask {{showDialog ? 'show' : ''}}" catchtouchmove="return"></view>
  <view class="dialog {{showDialog ? 'show' : ''}}" catchtouchmove="return">
    彈窗内容
  </view>
</view>      

注意這裡的​

​catchtouchmove="return"​

​,是為了在打開彈窗後,攔截了使用者滑動事件,避免彈窗下面的文檔被滑動。

看看html怎麼寫

繼續閱讀