动态

详情 返回 返回

移動端法門:自適應方案和高清方案 - 动态 详情

筆者從畢業開始做前端到現在,90% 的項目是移動端打交道,所以當簡歷上寫了“移動H5”幾個字時,必會被問到自適應方案與高清方案

”自適應“講的是一套UI(例如750*1334),在多端下展示近乎一樣的效果;而”高清“是因為 DPR 提升而所做的各種精度適配

這篇文章講講筆者理解的自適應方案和高清方案

先説結論

自適應方案

  • rem

    • 適配思路

      • 選擇一個尺寸作為設計和開發基準
      • 定義一套適配規則,自動適配剩餘的尺寸
      • 特殊適配效果給出設計效果
    • 屬於歷史產物,CSS 視窗單位未得到主流瀏覽器的支持
    • 原理

      • 根據視窗寬度動態調整根元素 html 的 font-size 的值
      • 把總寬度設置為 100 份,每一份被稱為一個單位 x,同時設置 1rem 單位為 10x
    • 缺點

      • 需要加載 js 腳本,而且根據設備的視窗寬度進行計算,影響性能
    • 影響力:從2015年出世至今,在 H5 適配領域佔據一定比例
    • 相關技術庫:flexible、px2rem
  • vw

    • 適配思路(如上)
    • 原理

      • 利用 CSS 視窗的特性,總寬度為 100vw,每一份為一個單位 1vw,設置 1rem 單位為 10vw
    • 缺點

      • 因為是根據視圖的寬度計算,所以不適用平板和PC
    • 影響力:2018年出的方案,目前 H5 適配主流
    • 相關技術庫:postcss-px-to-viewport
  • px + calc + clamp

    • 適配思路

      • 根據 CSS 的新特性:css變量、calc()函數、clamp()、@container函數實現
    • 特點

      • 解決了rem、vw佈局的致命缺點:失去像素的完美性,而且一旦屏幕低於或高於某個閾值,通常就會出現佈局的移動或文字內容的溢出
      • 大漠在2021年提出,最先進,但沒看到大廠使用(clamp函數瀏覽器支持率暫且不高),具體可以看看大漠的這篇:如何構建一個完美縮放的UI界面
    • 缺點

      • 因為方案先進,暫沒看到大廠使用

高清方案

  • 1 像素問題的解決方案
  • 不同 DPR 下圖片的高清解決方案

綜上,自適應方案是解決各終端的適配問題,高清方案是解決Retina屏的細節處理

寫在前面

在説移動端適配方案之前先整明白一些技術概念

設備獨立像素

設備獨立像素(DIP)=== CSS 像素 === 邏輯像素,在 Chrome 中能直接看到 375* 667

chrome中查看css像素

當你看到設備獨立像素時,不要慌,它表示 CSS 像素,而它的長寬就是在 Chrome 中所查到的。可這樣記憶,“設備獨立像素”,字數長,文縐縐就是 CSS 像素,也是理論上人為給定的指標,也叫邏輯像素

物理像素

物理像素可以理解為手機廠商在賣手機時宣傳的分辨率,即物理像素 = 分辨率,它表示垂直和水平上所具有的像素點數

也就是説設備屏幕的水平方向上有 1920 像素點,垂直方向有 1080 像素點(假設屏幕分辨率為1920*1080),即屏幕分辨率表示物理像素,它在出廠時就定下來,單位為 pt,1pt=0.376mm

手機分辨率

物理像素,又被稱為設備像素,即表示 設備像素 === 物理像素。可這樣記憶,設備在物理世界能測量的長度

DPR(Device Pixel Ratio)

而設備像素比(DPR)是什麼?

DPR = 設備像素 / 設備獨立像素,它通常與視網膜屏(Retina 屏)有關

以 iPhone7 為例子,iPhone7 的 DPR = iPhone7 物理像素 / iPhone7 設備獨立像素 = 2

寬 1334 / 667 = 2

高 750 / 375 = 2

得到 iPhone7 的 DPR 為 2,也就是我們常説的視網膜屏幕,而這就是營銷術語,它就是因為技術的進步,使得一個 CSS 像素塞入更多的物理像素

營銷術語還有哪些:農夫山泉的大自然的搬運工、元氣森林的“気”

筆者是這麼記憶的:

  • CSS 像素(設備獨立像素)就像一個容器,以前是一比一塞入,所以 DPR 為 1,後來技術發展進步了,一個容器中能塞入更多的真實像素(物理像素)
  • DPR = 設備像素 / 設備獨立像素
  • DPR = 物理像素(真實)/ CSS 像素(虛的)

在視網膜屏幕中,以 DPR = 2 為例,把 4(2x2)個物理像素當一個 CSS 像素使用,這樣讓屏幕看起來更加清晰(精緻),但是元素的大小(CSS像素)本身不會改變

DPR對比

隨着硬件的發展,像 iPhone13 Pro 等手機的 DPR 已經為 3,未來 DPR 突破 4 不是問題

説回來,DPR 為 2 或 3 會有什麼問題?我們以 CSS 為最小單位來寫代碼的,展示在屏幕上也是以 CSS 為最小單位來展示,也就是説在 DPR 為 2 時,我們想要模擬 1 單位物理像素是做不到的(如果瀏覽器支持用 0.5px CSS 的話,可以模擬,但是DPR為 3 呢,用 0.333px?);又因為手機的設備獨立像素(CSS 像素)固定,使用傳統靜態佈局(固定 px)時,會出現樣式的錯位

iPhone 5/SE: 320 * 568 DPR: 2

iPhone 6/7/8: 375 * 667 DPR: 2

iPhone 6/7/8 Plus: 414* 736 DPR: 3

iPhone X: 375 * 812 DPR: 3

所以我們要適配各終端的 CSS 像素以及不同 DPR 下,出現的 1 像素問題、圖片高清問題等。隨着技術的發展,前端們擺脱了 IE 的兼容,同時陷入了各大手機品牌的兼容沼澤

自適應方案

Rem 佈局——天下第二

簡介:rem 就是相對於根元素 html 的 font-size 來做計算

與 rem 相關聯的是 em:

em 作為 font-size 單位時,其代表父元素的字體大小,em 作為其它屬性單位時,代表自身字體大小

rem 作用於非根元素時,相對於根元素字體大小,rem 作用於根元素字體時,相對於其初始字體大小

本質:等比縮放,是通過 JavaScript 來模擬 vw 的特性

假設將屏幕寬度平均分為 100 份,每一份的寬度用 x 表示,x = 屏幕寬度 / 100,如果將 x 作為單位,x 前面的數值就代表屏幕寬度的百分比

p { width: 50x } /* 屏幕寬度的 50% */ 

如果想要頁面元素隨着屏幕寬度等比變化,我們就需要上面的 x,這個 x 就是 vw,但是 vw 是在瀏覽器支持後才大規模使用,在此之前,js + rem 可模擬這種效果

之前説了,rem 作用於非根元素時,相對於根元素字體大小,所以我們設置根元素單位後,非根元素使用 rem 做相對單位

html { font-size: 16px }
p { width: 2rem } /* 32px */

html { font-size: 32px }
p { width: 2rem } /* 64px */

問題來了,我們要獲取到一個動態的根元素 font-size,並以此變化各個元素大小

有趣的是,我司兩個項目目前的做法是通過媒體查詢設置根元素,分為四檔,默認16px

筆者對這種做法表示不理解,原開發人員説我們這套運行了6年,UI適配也沒人説什麼問題。這裏就有個疑問了,真的如他所説UI適配的很好嗎,”媒體查詢根元素+rem“也能適配好嗎?是否完美呢?

後續筆者也會在 demo 中展示這種做法

但是根元素的 font-size 怎麼變化,它不可能一直是 16px,在中大屏下還可以,但是在小屏下字體就太大了,所以它的大小也應該是動態獲取的。如何讓其動態化,就是上文所説,讓根元素的 font-size 大小恆等於屏幕寬度的 1/100

html { font-size: width / 100};

如何設置 html 的字體大小恆等於屏幕寬度的百分之一呢?可以通過 js 來設置,一般需在頁面 dom ready、resize 和屏幕旋轉中設置

document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px';
flexible 源碼就如以上思路寫的

我們設置了百分之一的寬度後,在寫 css 時,就需要利用 scss/less 等 css 處理器來對 css 編譯處理。假設給出的設計圖為 750 * 1334,其中一個元素寬度為 200 px,根據公式:

width: 200 / 750 * 100 = 26.67 rem

在 sass 中,需要設置設計圖寬度來做換算:

@use 'sass:math';

$width: 750px;

@function px2rem($px) {
  @return #{math.div($px, $width) * 100}rem;
}

上面編譯完後

div {width: 26.667rem}

在不同尺寸下,它的寬度不同

機型 尺寸 width
iPhone 5/SE 320 * 568 170 * 170
iPhone 6/7/8 375 * 667 200 * 200
iPhone 6/7/8 Plus 414 * 736 220.797 * 220.797
iPhone X 375 * 812 200 * 200

效果如下(特意説明:圖中演示的是引入 flexible 庫,它的根元素的 font-size 為屏幕的 1/10)

rem佈局

REM 佈局(flexible)demo

優點:rem 的兼容性能低到 ios 4.1,android 2.1

缺點:

  • 等比放大(可以説優點也可以理解為缺點,不同場景下使用)

    • 用户選擇大屏幕有幾個出發點,有些人想要更大的字體,更大的圖片,有些人想要更多的內容,並不想要更大的圖標
  • 字體大小不能使用 rem(一般使用媒體查詢控制 font-size 大小)
  • 在 PC 端瀏覽破相,一般設置一個最大寬度
var clientWidth = document.documentElement.clientWidth;
clientWidth = clientWidth < 780 ? clientWidth : 780;
document.documentElement.style.fontSize = clientWidth / 100 + 'px';
body {
    margin: auto;
    width: 100rem;
}
  • 如果用户禁止 js 怎麼辦?

    • 添加 noscripe 標籤提示用户
    • <noscript>開啓JavaScript,獲得更好的體驗</noscript>
    • 給 HTML 添加一個 默認字體大小

相關技術方案:flexible(amfe-flexible 或者 lib-flexible) + postcss-pxtorem

Viewport 佈局——天不生我VW,適配萬古如長夜

vw 是基於 Viewport 視窗的長度單位,這裏的視窗(Viewport) 指的是瀏覽器可視化的區域,而這個可視區域是 window.innerWidth/window.innerHeight 的大小

根據 CSS Values and Units Module Level 4: vw 等於初始包含塊(html元素)寬度的1%,也就是

  • 1vw 等於 window.innerWidth 的數值的 1%
  • 1vh 等於 window.innerHeight 的數值的 1%

看圖理解

屏幕的寬高

在説 rem 佈局時,曾經舉過 x 的例子,x 就是 vw

/* rem 方案 */
html { font-size: width / 100}
div { width: 26.67rem }

/* vw 方案 */
div { width: 26.67vw }

vw 還可以和 rem 方案結合,這樣計算 html 字體大小就不需要 js 了

html { font-size: 1vw }
div {width: 26.67rem }

效果如下:

vw適配

vw 適配是 CSS 原生支持,而且目前兼容性大多數手機是支持的,也不需要加載 js ,也不會因為 js引發性能問題

vw 確實看上去很不錯,但是也存在一些問題

  • 也沒能很好的解決 1px 邊框在高清屏下的顯示問題,需要自行處理
  • 由於 vw 方案是完全的等比縮放,在 PC 端上會破相(和 rem一樣)

相關技術方案:postcss-px-to-viewport

VW 佈局demo

px適配——一力降十會

不用 rem/vw,用傳統的響應式佈局也能在移動端佈局中使用,需要設計規範

使用css 變量適配(篇幅原因暫不詳細介紹,可直接看代碼)

使用場景:新聞、內容型的網站,不太適用 rem,因為大屏用户想要看到更多的內容,如網易新聞、知乎、taptap

PX + CSS變量 demo

媒體查詢——可有我一席?

上文講到我司原先H5端採用媒體查詢的方式來做適配,筆者嘗試復刻了下,只能説大差不差,能看出媒體查詢想做成這件事,但還是心有餘而力不足

採用rem、vw、px等方法能實現非標準尺寸(375 667設計稿)下 header 的高度為 165.59px,而 media 因為大屏,將根font-size 設置為17px,結果 header 的高度成為 159.38px(17 9.375rem)

如下GIF所示:

媒體查詢佈局與其他佈局對比

所以説僅用媒體查詢還是差強人意

媒體查詢佈局demo

各種適配的對比

vw、rem 適配的本質都是等比例縮放,px 直接寫,孰優孰劣看自己

REM佈局 VW佈局 PX + css變量佈局
容器最小寬度 支持 不支持 支持
容器最大寬度 支持 不支持 支持
高清設備1px邊框 支持 支持 支持
容器固定縱橫比 支持 支持 支持
優點 1.老牌方案
2.支持高清設備1px邊框時,可按以往方式直接寫
1.無需引入js
2. 天然支持,寫法規範
同VW
缺點 1. 需要引入 js 設置 html 的font-size
2. 字體大小不能使用 rem
3. 在 PC 端瀏覽會破相,一般需設置最大寬度
1.在PC端會破相
2.不支持老舊手機
同VW

除此之外,還有搭配 vw 和rem 的方案

  • 給根元素大小設置隨着視窗變化而變化的vw單位,動態變化各元素大小
  • 限制根元素字體大小的最大最小值,配合body加上最大寬度和最小寬度
// rem 單位換算:定為 75px 只是方便運算,750px-75px、640-64px、1080px-108px,如此類推
$vm_fontsize: 75; // iPhone 6尺寸的根元素大小基準值
@function rem($px) {
     @return ($px / $vm_fontsize ) * 1rem;
}
// 根元素大小使用 vw 單位
$vm_design: 750;
html {
    font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw; 
    // 同時,通過Media Queries 限制根元素最大最小值
    @media screen and (max-width: 320px) {
        font-size: 64px;
    }
    @media screen and (min-width: 540px) {
        font-size: 108px;
    }
}
// body 也增加最大最小寬度限制,避免默認100%寬度的 block 元素跟隨 body 而過大過小
body {
    max-width: 540px;
    min-width: 320px;
}

高清方案

1像素問題

1像素指在 Retina 屏顯示 1單位物理像素

很好理解,CSS 像素(設備獨立像素)是我們人為規定的,當 DPR 為 1 時,1像素(指我們寫的 CSS 像素) 等於 1物理像素;但當 DPR 為 3 時,1像素就為 3 物理像素

  • DPR = 1,此時 1 物理像素 等於 1 CSS 像素
  • DPR = 2,此時 1 物理像素等於 0.5 CSS 像素

    • border-width: 1px,這裏的 1px 其實是 1 CSS 像素寬度,等於 2 物理像素,設計師其實想要的是 border-width: 0.5px
  • DPR = 3,此時 1 物理像素等於 0.33 CSS 像素

    • 設計師想要的是 border-width: 0.33px

1像素問題

解決思路

使用 0.5px 。有侷限性,iOS 8及以上,蘋果系統支持,但是 iOS 8以下和 Android(部分低端機),會將0.5px 顯示為 0px

既然 1 個 CSS 像素代表 2(DPR 為2)、3(DPR為3)物理像素,設備又不認識 0.5px 的寫法,那就畫 1px,然後想辦法將寬度減少一半

方案

  • 漸變實現

    • background-image: linear-gradient(to top, ,,,)
  • 使用縮放實現

    • transform: scaleY(0.333)
  • 使用圖片實現

    • base64
  • 使用 SVG 實現

    • 嵌入 background url
  • border-image

    • 低端機下支持度不好

以上都是通過 CSS 的媒體查詢來實現的

@media only screen and (-webkit-min-device-pixel-ratio: 2),
    only screen and (min-device-pixel-ratio: 2) {}
@media only screen and (-webkit-min-device-pixel-ratio: 3),
    only screen and (min-device-pixel-ratio: 3) {
        
}

圖片適配和優化

圖像通常佔據了網頁上下載資源絕大部分,優化圖像通常可以最大限度地減少從網站下載的字節數以及提高網站性能

通常可以,有一些通用的優化手段:為不同 DPR 屏幕提供最適合的圖片尺寸

各大廠商的適配分析

看了不少文章,類似如:大廠是怎麼做移動端適配的

各大廠,有用rem適配的、也有用vm適配的、也有vm+rem結合適配的,純用 px 方案的也有

  • 新聞、社區等可閲讀內容較多的場景:px+flex+百分比

    • 如攜程、知乎、TapTap
  • 對視覺組件種類較多,依賴性較強的移動端頁面:vw+rem

    • 如電商、論壇

總結

rem 方案,引入 amfe-flexible

設計:設計出圖是 750 * 1334,設計切好圖後,上傳藍湖,按照尺寸寫 px。

開發:

  • 使用 rem 方案

    • 引入 amfe-flexible
    • 安裝 px2rem 之類的 px 轉 rem 工具
    • 配置 px2rem
    • 在項目中寫 px ,輸出時是 rem
    • 適用任何場景
  • 使用 vw 方案

    • 安裝 px2vw 之類的 px 轉 vw 工具
    • 配置 px2vw
    • 在項目中寫 px,輸出時是 vw
    • 適用任何場景
  • 使用 px 方案

    • 該怎麼樣就怎麼寫,不過因為有設計規劃,按鈕的大中小尺寸固定、icon 的尺寸有標準、TabBar 的高度也是寫死的,當一切都有標準後,寫頁面就方便了
    • 例如

      • 左邊固定 100 * 50,右邊 flex 佈局
      • 左邊固定 100 * 50,右邊 calc(100% - 100px)(使用 CSS3 中的 calc 計算)

其他

caniuse 網站測試CSS屬性與瀏覽器的兼容性問題

疑問

Q:為什麼 H5 移動端UI庫單位大都是用 px?這樣不會有適配問題嗎?

其實我們寫好 px 後,如果項目採用 rem 寫業務,引入 px2rem(已經六年沒有維護了) 即可轉換。

在有贊 vant 庫中,它對瀏覽器適配的介紹是:

Viewport 佈局

Vant 默認使用 px 作為樣式單位,如果需要使用 viewport 單位(vw、vh、vmin、vmax),推薦使用 postcss-px-to-viewport 進行轉換

postcss-px-to-viewport 是一款 PostCSS 插件,用於將 px 單位轉化為 vw/vh 單位

Rem 佈局

如果需要使用 rem 單位進行適配,推薦使用以下兩個工具:

  • postcss-pxtorem 是一款 PostCSS 插件,用於將 px 單位轉化為 rem 單位
  • lib-flexible 用於設置 rem 基準值

demo 合集:線上demo

參考資料

  • 前端基礎知識概述 -- 移動端開發的屏幕、圖像、字體與佈局的兼容適配
  • Rem佈局的原理解析
  • 再談Retina下1px的解決方案
  • 再聊移動端頁面的適配
  • 如何在Vue項目中使用vw實現移動端適配
  • 細説移動端 經典的 REM 佈局 與 新秀 VW 佈局
user avatar Leesz 头像 zaotalk 头像 yinzhixiaxue 头像 littlelyon 头像 chongdianqishi 头像 qishiwohendou 头像 daqianduan 头像 nqbefgvs 头像 bugDiDiDi 头像 kitty-38 头像 nznznz 头像 lovecola 头像
点赞 56 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.