網易和淘寶移動WEB適配方案再分析

a330222803 7年前發布 | 29K 次閱讀 淘寶 網易 前端技術

最近把移動WEB適配相關的問題梳理了一遍,學習了幾篇文章,其中從網易與淘寶的font-size思考前端設計稿與工作流

分析了網易和淘寶對移動WEB適配問題的解決方案,干貨不少,但是一些概念和思路不是很清晰。我在這里結合一些其他的文章和自己的實驗思考對兩種適配方案再做分析,順便把相關的知識點做個總結。

移動適配中涉及到的一些 基本概念和原理

在這里提取一些比較重要的規則

  1. 布局視口(layout viewport) 可以看作是html元素的上一級容器即頂級容器,默認情況或者將html元素的width屬性設為100%時,會占滿這個頂級容器,此時用
    document.documentElement.clientWidth
    獲取到html元素的布局寬度也就是布局視口的寬度,使用媒體查詢時 max-width 和 min-width 的值指的也是布局視口的寬
  2. 布局視口的寬度和高度單位是像素px,這個單位是CSS和JS中使用的抽象單位,為了便于區分稱作CSS像素。布局視口的寬度有一個默認值,一般在 768px ~ 1024px 之間,最常見的寬度是 980px,在這個默認值下進行布局得到的頁面比較接近PC端布局的效果
  3. 可視視口(visual viewport) 是指用戶可見頁面區域,其寬度值為橫向可見CSS像素數,從另一個角度理解,可視視口的寬度決定了將屏幕橫向分為多少份,每份對應一個CSS像素,使用
    window.innerWidth
    可以獲取到可視視口的寬度
  4. 理想視口(ideal viewport) 是一個比較適合頁面布局使用的視口尺寸,作為計算布局視口和可視視口尺寸時的一個基準值,下面代碼中 device-width 的值就是理想視口的寬度
    <meta name="viewport" content="width=device-width">
    使用
    screen.width
    也可以獲取到理想視口的寬度
  5. 三個視口中 只有理想視口的尺寸是不能改變 的,由設備和瀏覽器決定,與設備的物理像素數存在著比例關系,這個比例就是 設備像素比 (device pixel ratio, dpr),即有 設備像素比 = 物理像素數 / 理想視口尺寸 ,舉例iphone5屏幕橫向有640個物理像素,其理想視口寬為320,則 dpr=2,可以使用
    window.devicePixelRatio
    獲得dpr,但在安卓系統下這個值可能不符合預期
  6. 可視視口的尺寸收到縮放比例的影響 ,因此用戶手動縮放和在 meta 標簽中設置 initial-scale 的值都會改變可視視口的尺寸,可視視口的尺寸越小即顯示的CSS像素數越少,則單位CSS像素對應的可使區域越大,對應的縮放比也就越大。 縮放比例是相對于理想視口而言的 ,即有 縮放比例 = 理想視口尺寸 / 可視視口尺寸 。對iphone5,設置
    <meta name="viewport" content="initial-scale=0.5">
    則其可視視口尺寸為640個CSS像素
    縮放比例也有默認的值,沒有設置 initial-scale 時,瀏覽器會取適當的縮放比例使 布局視口正好鋪滿屏幕即有 布局視口尺寸 = 可視視口尺寸
  7. 布局視口的寬度受到 meta 標簽中的 width 和 initial-scale 的影響
    僅設置 width 的值時,這個值就是布局視口的寬度 ,width的值可以為正整數或特殊值 device-width
    <meta name="viewport" content="width=400">
    此時由于沒有禁用縮放,一般可以通過雙擊屏幕對頁面進行縮放,但手動縮放不會影響布局適口的尺寸,只會影響到可視視口的尺寸,而且可能導致布局視口小于可視視口
    設置 initial-scale 的值時,布局視口的尺寸與可視視口的計算方式相同,但不受手動縮放的影響
    同時設置 width 和 intial-scale 的值時,布局視口的寬取上述兩個值中較大的一個
  8. 布局視口寬 = 可視視口寬時 html 元素正好橫向鋪滿窗口(但其后代元素若有橫向 overflow 的情況,仍然會出現滾動條),布局視口寬 > 可視視口寬時,出現橫向滾動條

由上述規則可以得到一些有用的結論

  1. 將 meta 標簽中的 width 設為 device-width 同時禁用手動縮放可以使 布局視口尺寸 = 可視視口尺寸 = 理想視口尺寸 ,此時 設備像素比 = 物理像素數 / 理想視口尺寸 = 物理像素數 / 布局視口尺寸 ,對iphone5,一個CSS像素對應4個物理像素
  2. 為 initial-scale 設置任意合法的值同時禁用手動縮放就可以使 布局視口尺寸 = 可視視口尺寸
  3. 將 initial-scale 設置為 1 也可以使 布局視口尺寸 = 可視視口尺寸 = 理想視口尺寸

下面具體分析網易和淘寶的適配方案

其實兩種方案在處理元素尺寸的適配時采用了相同的思路,即 使用相對單位 rem 并將設備的可視視口寬度乘以一個系數得到 html 元素的 font-size,元素布局時不超出可視視口寬度即可

實際上我們并不需要特別設置布局視口的寬度——只要所有需要展示的元素填滿可視視口即可,理論上如果設置了 meta 中的 width 值且大于可視視口的計算結果,會出現橫向滾動條,但網易和淘寶的方案都沒有設置 width,此時布局視口的寬等于可視視口,只要沒有超出可視視口寬度的元素,就不會出現滾動條

先分析一下網易新聞的方案

  1. 第一個要解決的關鍵問題是如何為設備選擇可視視口尺寸 ,網易新聞的方案相對簡單,采用理想視口尺寸作為可視視口尺寸,代碼也十分簡單,只需要將縮放比定為 1
    <meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
  2. 第二個要解決的問題是計算 html 元素的 font-size

    ,要將可視視口的寬度乘以一個系數

    理論上這個系數可以是任意值,假設將這個系數取 1,則 html 元素的 font-size 即1 rem等于可視視口的寬度,此時以 rem 為單位的長度 n rem 就可以理解為 n 倍可視視口的長,這個系數取 0.01 時,1 rem 等于可視視口寬的 1/100,也就等于布局視口寬的 1/100,也就等于 1vw。實際使用過程中這個系數的選擇盡可能方便將設計稿長度數值換算為css中的長度數值

    網易新聞 手機網易網 選擇的系數為 100 / 750,這個系數可以如下推出:

    750px 是設計稿的寬度(以iphone6的物理像素數為標準),100是期望的換算比例,即設計稿中 100px 的長度對應css中 1rem,將設計稿中的長度數值除以 100 得到的就是以 rem 為單位的 css 長度的數值,設計稿的寬換算為以 rem 為單位的 css 長度應為 (750/100) rem,同時設計稿的寬對應可視視口的寬,即有 (750/100) rem = 可視視口寬,1 rem = 可視視口寬 * (100/750),(100/750)就是我們要的系數

    在頁面初始化時設置一下 html 元素的 font-size

    document.documentElement.style.fontSize = window.innerWidth / 7.5 + 'px';
  3. 第三個要解決的是將可視視口的寬度換算為 rem 單位的數值 ,布局時元素不能超出這個值由 (750/100) * font-size = 可視視口寬 可知 可視視口寬為 (750/100) rem
    網易新聞的解決方案中將這個值設置到了 body 元素的 width 上但并沒有對 overflow 進行處理,所以實際上沒有起到什么作用
    輸出的html如下(iphone6,device-width = 375,dpr = 2)
    <html style="font-size: 50px;">
    ...
    <meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
    ...
  4. 在最新的網易新聞頁面中字體的單位也使用了 rem

然后對比地分析一下淘寶的方案

可視視口的寬度為 10 rem, 對應設計稿的寬度,則 1 rem 對應設計稿寬度的 1 / 10,換算時將設計稿中的長度數值除以 (設計稿寬度/10) 就可以了。

在 meta 標簽中的縮放比例發生變化時,設置 html 元素的 font-size

document.documentElement.style.fontSize = window.innerWidth / 10 + 'px';
  1. 在為設備選擇合適的可視視口時淘寶的方案顯得復雜,但是有其巧妙之處,在他們的開源項目 使用Flexible實現手淘H5頁面的終端適配 · Issue #17 · amfe/article 中提到了縮放比的算法
  2. if (!dpr && !scale) {
        var isAndroid = win.navigator.appVersion.match(/android/gi);
        var isIPhone = win.navigator.appVersion.match(/iphone/gi);
        var devicePixelRatio = win.devicePixelRatio;
        if (isIPhone) {
            // iOS下,對于2和3的屏,用2倍的方案,其余的用1倍方案
            if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
                dpr = 3;
            } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                dpr = 2;
            } else {
                dpr = 1;
            }
        } else {
            // 其他設備下,仍舊使用1倍的方案
            dpr = 1;
        }
        scale = 1 / dpr;
    }

    概括一下,就是取 dpr 的倒數作為縮放比例,對 iOS 設備 dpr = window.devicePixelRatio ,其他設備認為 dpr 為 1

    對 iOS 設備,令上面提到的公式 縮放比例 = 理想視口尺寸 / 可視視口尺寸,設備像素比 = 物理像素數 / 理想視口尺寸 中 設備縮放比 = 1 / 縮放比例 可以推出 可視視口尺寸 = 物理像素數,同時由于沒有設置 meta 標簽的 width 值,有 布局視口尺寸 = 可視視口尺寸 = 物理像素數,這意味著布局視口中的像素單位是和物理像素一一對應的,css單位中1px嚴格等于一個物理像素。這就是淘寶方案的巧妙之處了,對于 iOS 下高分辨率的設備,提供了更好的支持,解決了 1px border 問題和高清圖片的問題,詳細的解釋見 移動端高清、多屏適配方案 - Div.IO

    對非 iOS 設備,將 dpr 設為 1,縮放比例也為 1,和網易新聞的方案相同。

  3. 在計算 html 的 font-size 時淘寶的方案直接將可視視口的寬度乘以一個系數 0.1,由此推導換算比例:
  4. 由于可視視口的寬度就是 10 rem,對元素進行布局時只要不超過 10rem 即可,另外淘寶的方案將 body 的 width 設為 100% 并對 overflow 進行 hidden,這個 100% width 會計算為布局視口的寬,也是 10rem,那么 body 內布局超出 10rem 的元素不會導致布局視口出現滾動條
  5. 淘寶的方案中對字體使用了 px 單位,這就需要對不同的視口寬度(也就是不同dpr)的設備分別進行適配,淘寶的方案是將 dpr 數值設置為 html 的 data-dpr 屬性,通過css選擇器選擇不同 dpr 設備下的元素
    div {
        width: 1rem; 
        height: 0.4rem;
        font-size: 12px; // 默認寫上dpr為1的fontSize
    }
    [data-dpr="2"] div {
        font-size: 24px;
    }
    [data-dpr="3"] div {
        font-size: 36px;
    }

對比總結

從可視視口的選擇可以看出 淘寶方案對 iOS 高分辨率的設備提供了特別的支持,而對其他設備一律采用了精確度較低的處理方式,網易新聞則對所有設備都采用了精確度較低的處理方式。在這一點上,淘寶方案可看作是網易方案的增強版,而在 dpr = 1 的設備下,淘寶方案可以退化為網易方案。淘寶方案還可以使用 px 單位對字體進行精確度比較高的適配,而對于網易方案,使用 px 作為字體的單位意義不大。

在 html 元素 font-size 的計算上,網易方案選擇了一個比較人性化的換算比例,根據這個比例去推算 font-size 的系數和對應的視口尺寸的 rem 值,淘寶方案反過來將可視視口定為 10rem和 font-size 的系數,由此推算換算比例。比較而言網易方案進行人工換算和檢查時更容易些,淘寶方案雖然可以借助工具完成自動換算,但在閱讀輸出的 css 樣式時就有點費力了。

實際運用中,可以將兩種方案的優點結合起來,形成更好的適配方案。

其他方案

這篇文章 移動端適配方案(下) - WEB前端 - 伯樂在線 中提到的第二種方案是另外一種思路,將布局視口尺寸固定,然后通過縮放使可視視口尺寸等于布局視口尺寸,進行元素布局時按照固定好的布局視口。原理用動圖展示最為直觀:

文中給出的實現代碼如下:

<meta name="viewport" content="width=640,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no">

縮放比例根據理想視口計算的, 縮放比例 = 理想視口尺寸 / 可視視口尺寸 ,使 可視視口尺寸 = 布局視口尺寸 ,則 縮放比例 = 理想視口尺寸 / 布局視口尺寸

布局視口的尺寸取設計稿的尺寸時,css像素對應設計稿中的 px,不需要進行單位換算

文中給出了兩個例子 金幣商城 荔枝FM,人人都是播客_聽音樂相聲評書脫口秀鬼故事廣播劇網絡電臺 ,但查看兩個例子的代碼發現其實現方式是另一種

<meta content="target-densitydpi=device-dpi,width=640,user-scalable=no" name="viewport">

測試發現這種方式也可以使 可視視口尺寸 = 布局視口尺寸 , 但有資料顯示target-densitydpi是一個將被拋棄的屬性,因此不推薦使用

 

來自:https://zhuanlan.zhihu.com/p/25216275

 

 本文由用戶 a330222803 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!