移動端優雅佈局實踐

前言:移動端有很是多的坑,佈局首當其衝。

背景

移動端應用有各類複雜的頁面需求,不只要解決單屏、多屏、固定頭部或底部等多個場景,還要兼容ios和Android內核,在經歷了項目實戰(手機模式打開)事後,總結出了一些經驗,在這裏和你們分享一下。html

這篇文章是基於 →
Next輕量級框架與主流工具的整合
最新的代碼在這裏 →
next-mobile-complete-demoios

心路歷程

首先,須要實現的首頁相似於app應用。git

截圖示例

  • 第一站:統一flex佈局的坑

    flex教程

    首頁如上面的圖片所示,而後偷了個懶,直接用了antd-mobile的tab標籤欄,它須要指定容器高度。因而,在項目最開始的時候直接經過js將容器設置爲瀏覽器html窗口的高度:github

// html高度 = body高度 = 主容器高度
doc.body.style.height = docEl.clientHeight + 'px';

這樣作也有許多好處:縱向佈局十分方便,不論是tab標籤欄、tab標籤頁、頭部固定元素或者底部固定元素實現起來都很簡單;也能很簡單就能實現元素居中對齊、兩端對齊、自動分配空間等;同時也避免了Android輸入框引發傳統佈局的問題。segmentfault

不過這裏有個惟一的很差的地方就是兼容不了ios的前進返回的操做欄和上下滾動回彈:瀏覽器

  • 不能在滾動時隱藏前進返回的操做欄、
  • 在上下滾動時容易觸發頁面的回彈效果,形成滾動卡頓。
這裏補充一下:在ios上若是以正常流佈局,內容超過必定高度時,向下滾動會隱藏底部的前進返回欄,向上滾動的時候再顯示出來。當滾動到底部或者頂部時,再繼續拉動頁面,會有一個回彈的效果。

這兩個問題極大的下降了ios的用戶體驗,又恰逢項目間隔期,有大把的時間,因而被推着到了佈局優化上面。安全

  • 第二站:尋找良好用戶體驗的佈局案例

這裏咱們在尋找的過程當中發現兩個具備表明性的移動端應用:antd

  1. bilibili的m站騰訊新聞

    它用的是傳統式流佈局,頭部和底部fixed固定,全部的內容所有向下平鋪。app

  2. 花瓣網H5

    它採用的是設置主容器的position屬性爲absolute來脫離文檔流的形式。框架

這兩種佈局方式在ios上都用戶體驗極好。但同時也發現他們都存在的一些問題,好比:登陸頁面明明不足一屏,卻依然存在滾動;沒有兼容iPhone X的安全域;彈窗滾動穿透等等。

若是能將這兩種佈局和flex進行整合一下,聚合各自的優勢,基本上能打造一個用戶體驗和兼容性都使人滿意的移動端應用了。

那麼,如何基於現有的flex佈局去整合這兩種佈局又成了一個問題。

  • 第三站:基於flex調整佈局結構

在調整以前,個人預期是這樣的:去掉外層高度限制,去掉縱向flex佈局(除極少數單屏頁面),將頂部、底部固定和彈窗設置爲fixed

考慮到少數單屏頁面須要繼承html窗口的高度,因而採用了主容器脫離文檔流的方式。

調整事後dom結構是這樣的:

html > body > div#root > div.main-content(position: absolute)

只須要給main-content加上height: 100%,就能夠知足單屏頁面的需求。

好事多磨!

按照上面的想法進行改造事後又發現了新的問題:脫離文檔流事後,切換頁面,html會保持最後滾動的位置

  • 第四站:滾動條位置、滾動穿透、兼容iPhone X

  1. 有可能出現滾動條位置被記錄的問題
    找了一下發現history有個scrollRestoration的屬性,它有兩個值,一個是默認值auto還有一個是manual。若是設置爲manual就能夠手動設置滾動的位置。

    if ('scrollRestoration' in history) {
      history.scrollRestoration = 'manual';
    }

    而後在切換路由事後設置滾動的位置爲起始位置:

    Router.events.on('routeChangeComplete', () => {
    document.scrollingElement.scrollTop = 0;
    });

    這樣每次切換頁面都是從初始位置開始。

    開發碰見的問題,這個demo中沒有出現。這裏有點雞肋,前進後退也不會記錄滾動位置。若是須要前進後退記錄滾動的位置,就不能用這種脫離文檔流的形式,須要用body滾動的形式,也就是。
  2. 解決滾動穿透
    這個問題,須要針對有滾動的彈窗和無滾動的彈窗單獨處理。

    • 針對無滾動的彈窗,在彈窗彈出的時候禁用touchmove事件,隱藏的時候移除事件監聽。
    • 有滾動的彈窗,須要找到頁面的滾動的容器設置overflow:hidden
  3. 兼容iPhone X

    在meta標籤後增長viewport-fit=cover

    <meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover"/>

    而後留出安全域距離

    padding-bottom: 0.49rem; // 底部button的高度
    padding-bottom: calc(0.49rem + constant(safe-area-inset-bottom));
    padding-bottom: calc(0.49rem + env(safe-area-inset-bottom));

    這裏須要注意些padding-bottom的時候須要寫三個,第一個是爲了兼容Android。

總結

總的來講flex佈局仍是帶了十分大的便利,尤爲是能根治居中、適配等一些老大難的問題。

每種佈局的方式都有必定的缺陷,到目前爲止尚未一種萬能的方案來解決移動端各類複雜的場景。仍是須要根據本身的需求還選擇使用哪一種實現方式。

*前面這三種方案各自的缺陷:

縱向flex佈局(不建議使用) body流式平鋪(傳統) absolute脫離文檔流(正在使用)
ios前進後退的操做欄沒法隱藏、回彈效果引發卡頓 內部容器沒法繼承html窗口高度 路由跳轉可能出現滾動條被記錄
相關文章
相關標籤/搜索