移動端適配

把為PC端設計的網頁展示到移動端瀏覽器上,也許第一個面臨的就是設備寬度不夠帶來的排版錯亂問題,為了讓頁面在不同尺寸的設備上都能合理排版顯示,我們需要做一些適配措施。


前置知識

1. 設備的一些物理屬性

image.png
  • 屏幕尺寸:屏幕對角線的長度
  • 設備像素:設備像素是物理概念,指的是屏幕的發光點
  • 屏幕分辨率是指屏幕的像素點數。分辨率2400*1080指有2400*1080(= 2592000)個像素點
  • PPI(Pixels Per Inch)是像素密度(Screen density)的單位,指每英寸的像素數量。屏幕尺寸和分辨率已知,(根據分辨率計算對角線上的像素數量 / 屏幕尺寸),就能計算出像素密度

2. 設備獨立像素(device-independent pixel,簡稱DIP或DP)

設備獨立像素是與設備無關的邏輯像素,【獨立于設備】用于【邏輯上衡量尺寸】的單位,這樣我們就【不需要關注設備的物理分辨率】了
我們在web開發布局時使用的px都是邏輯像素

  • 當我們在布局時將一個元素設置為100px(不管屏幕分辨率和尺寸是多少)
    —— 如果這個設備的視口寬度是100dp(也就是設備提供給你的這個視覺窗口內能顯示的邏輯px值是100),則這個元素顯示的寬度剛好充滿屏幕
    —— 如果這個設備的視口寬度是200dp,則這個元素顯示的寬度為屏幕的一半

  • 從上面可以看出px是個相對單位,即使元素都設置為100px,顯示出的物理尺寸也可能是不同的。

3. DPR(設備像素比,devicePixelRatio )

我們布局時使用的是邏輯像素,設備顯示時需要將邏輯像素轉換為實際的物理像素。DPR就描述的是設備物理像素與邏輯像素之間的轉換關系:

設備像素比(DPR):window.devicePixelRatio = 物理像素 / 邏輯像素。

  • 當像素比為1:1(DPR=1)時,即使用1個物理像素顯示1個CSS像素;
  • 當像素比為2:1(DPR=2)時,使用4個物理像素顯示1個CSS像素;


    dpr.png

<略>了解了物理像素、邏輯像素、DPR之后,我們知道影響元素物理尺寸的是:元素所設置的px值、dpr、ppi:px值和dpr決定了顯示元素所使用的物理像素數量,再由ppi值(每英寸的像素數量)計算物理顯示尺寸

4. <略>PC端瀏覽器視口的邏輯寬度

image.png
  • 瀏覽器視口的邏輯寬度與我們調整的顯示器分辨率和縮放值有關。上圖配置,瀏覽器全屏時視口寬度為 1920 / 1.5 = 1280dp。如果我們將縮放調至100%,視口寬度為1920dp

  • 瀏覽器縮放,不斷放大時,元素顯示的物理尺寸變大,說明顯示1px所用的物理像素在變多,即dpr值在變大,而且視口的邏輯寬度在變小。下圖中可以看到這種改變:


    dpr.gif

移動端適配1——viewport

1. 布局視口

  • layout viewport,這個視口就是HTML頁面布局的區域,開發者可以自定義它的寬高(主要是加寬),使得原本為PC端設計的頁面結構不會在移動設備上被破壞。用戶可以在視覺視口(visual viewport,手持設備屏幕的可視區域)中拖動或者縮放網頁,來獲得良好的瀏覽效果。使用viewport meta標簽,可以創建一個虛擬的布局視口

  • 移動設備上的瀏覽器會給viewport設置默認的尺寸,常見寬度為980px和1024px。可以通過document.documentElement.clientWidth查看。

  • 因為移動設備的默認布局視口往往大于設備邏輯寬度,此時就會在橫向出現滾動條才能完整的容納頁面。例:在邏輯寬度為375px的設備上瀏覽網頁:


    不設置viewport,默認寬度.gif

2. 理想視口

移動端瀏覽器為viewport設置默認尺寸雖然不會排版錯亂,但是有滾動條問題

理想視口,最理想的視口大小——用戶無需縮放或滾動就可以瀏覽橫向的全部內容。它只是一種概念,用于指導開發者設計最理想的頁面大小,它的寬度應為設備邏輯寬度。

移動端適配的第一步,將視口設置為理想視口,這時我們需要能自己定義視口大小:

3. 自定義viewport的尺寸

<meta name="viewport" content="">

  • viewport 標簽只對移動端瀏覽器有效,對 PC 端瀏覽器是無效的
content支持的屬性
width 定義視口寬度,單位為px。值可以自定義或設為device-width,device-width表示設備邏輯寬度
height 設置layout viewport 的高度
initial-scale 定義初始頁面的縮放值,[0.0-10.0](chrome 105上測試小于0.25的視為0.25,大于5的視為5)
minimum-scale 定義用戶可縮小最小比例
maximum-scale 定義用戶可放大最大比例
user-scalable 定義是否允許用戶手動縮放頁面,yes/no,默認值yes
  • <meta name="viewport" content="width=device-width">將viewport寬度設置為設備邏輯寬度,達到理想視口:

    image.png

  • <meta name="viewport" content="initial-scale=1">也能將viewport的寬度設置為設備邏輯寬度。因為縮放是相對于 ideal viewport來進行縮放的,當縮放值為1的時候,就得到了 ideal viewport了。

  • 為什么同時寫width=device-width, initial-scale=1

    image.png

  • 同時設置這兩個屬性還有一個原因:如果設置了viewport寬度大于設備邏輯寬度,如果不設置initial-scale,照理來說會出現橫向滾動條,chrome devTools下模擬是有滾動條的,但是手機上測試沒有,它會自動計算一個縮放值,縮放至不出現橫向滾動條的狀態,導致頁面內容顯示很小,所以還是需要設置initial-scale來覆蓋這種默認行為,不要自動縮放,而是【讓它正常出現滾動條】。

視口寬度設置為800,沒有出現滾動條,而是自動縮放
對比width設置為device-width
  • 如果同時設置initial-scale和width,而且有沖突?
    則會取較大的值。
    <略>例如當設備邏輯寬度為375px時,設置initial-scale為3.0,則viewport的寬度為375px / 3 = 125px;(initial-scale = 設備邏輯寬度 / viewport寬度, initial-scale設置的越大,viewport的寬度就越小)。比如同時設置了width為400,則viewport的寬度為400px

將viewport設置到理想視口,使用戶不需要進行拖動和縮放,是在移動端瀏覽網頁獲得良好體驗的第一步

移動端適配2——媒體查詢

rem和vw的方式都是等比縮放元素,但是移動設備越來越大并不是為了讓元素越來越大,而是為了展示更多的內容。當設備尺寸差別過大可能布局設計已經不一樣了,不再是單純縮放,這時就需要使用 媒體查詢

@media not|only mediatype and (mediafeature and|or|not mediafeature) {
  CSS-Code;
}

移動端適配3——相對單位之 rem

保持頁面設計結構不變,大屏時讓元素顯示的更大,小屏則尺寸小一點,是適配各種設備的一種方法,使用rem能便捷地達到這種效果

  • rem也是相對單位,相對于根元素(html)的font-size值來計算,假如根元素的字號為20px,則1rem為20px
:root { font-size: 20px; }

1. 如何根據設計稿來使用rem?

  1. 先不考慮ui設計稿的整體尺寸,將設計稿上的1rem視為100px(因為100方便換算,不設置為10px的原因:chrome不支持小于12px的字號),也就是說設計稿上根節點font-size為100px
  2. 將設計稿上元素尺寸換算為rem:假如設計稿上某元素字體為20px,則換算為0.2rem,那么在開發時,任何設備,這個字體設置時都是0.2rem
  3. 當我們能把設計稿上的px都換算為rem,就只需要關注在各種設備上將根節點的字號設置為多少px
  4. 以我們移動端ui設計稿為例,寬度為750px,1rem視為100px。當設備邏輯寬度為375px,這個設備上1rem = 375 / (750 / 100) = 50px
  5. 需要js動態獲取當前設備邏輯寬度,計算根節點的font-size值


    移動端.png

2. 是把設計稿上所有px都轉成rem嗎?

  • rem只是我們工具箱中的一個,對于需要適配屏幕等比縮放的元素可以選用 rem 作為單位,不需要等比縮放的元素可以依舊選用其他單位。
  • 這需要我們自己分辨,例如對font-size使用rem,對border使用px,對padding、margin、border-radius等使用em,聲明容器的寬度用百分比等。當然這都不是絕對的

3. 使用插件將px換算成rem

  • 雖然將1rem視為100px已經便于換算,但畢竟還是多了一步計算,最好可以直接把設計稿上的px值拿來直接用!
  • 我們移動端就使用了postcss-pxtorem插件,以1rem為100px為基準將代碼中的px值轉換成rem,這樣開發時就能直接用設計稿上的px值了。
    配置.png

移動端適配4——相對單位之 vw

vw和vh是相對于viewport的單位,也能實現【設備寬度不同時,網頁元素寬高等比縮放】的效果
1vw = 1% viewport width
1vh = 1% viewport height

1. 換算

  • 計算邏輯:假如設計稿寬度750px,那么30px則換算為 30 / 750 * 100 = 4vw,不需要關注各種設備實際的vw是多少
  • 插件:手動計算顯然過于麻煩,postcss-px-to-viewport 同樣可以自動將px轉換為vw(配置viewportWidth為設計稿的寬度),這樣就能在開發時直接使用設計稿上的px值了

2. 與rem的對比

  • 如果使用rem:px → 換算成rem → 計算不同viewport下根節點的font-size
  • 如果使用vw:px → 換算成vw

所以,使用vw比rem方便之處在于——這是個純css方案,不需要使用js去關注當前設備的vw值。

<略>3. 100vh有點方便
我們總會遇到這種場景:底部按鈕固定,上方表單區域滾動顯示:

        :root {
            --height: 98px;
        }

        .form {
            height: calc(100vh - var(--height));
            overflow: auto;
        }

        .btn {
            height: var(--height);
        }
        .container {    
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        .form {
            flex: 1;
            overflow: auto;
        }

        .btn {
            height: 98px;
        }

<略>4. vw和vh混用可能會導致元素變形

  • 以750x1334的設計稿為例,假如元素是一個30 x 30px的正方形
  • 如果網頁的vw和vh分別是375px x 667px(iPhone6的理想視口),則此元素寬為30 / 375 * 100 = 8vw,高為30 / 667 * 100 ≈ 4.498vh
  • 將元素寬高設置為8vw和4.498vh
  • 如果網頁的vw和vh分別是414px x 896px(iPhone XR的理想視口),按照8vw和4.498vh計算,這個元素的尺寸為33.12 x 40.3px,就不是一個正方形了,也就是會發生變形,所以建議不要混用vw和vh

5. 兼容性

最后

1. 微信小程序的rpx

image.png

微信小程序規定所有設備上邏輯寬度都是750rpx。設計師只要按照iPhon6的750*1334進行設計,開發者可以將設計稿中的px以1:1直接替換為rpx,不再需要自己考慮自適應的問題

image.png
  • 使用rem,是開發者根據設備邏輯寬度和設計稿寬度算出根節點的font-size值,即1rem為多少px作為基準
  • 使用rpx,是小程序根據設備邏輯寬度和自己規定的750rpx算出1rpx為多少px作為基準,省去了我們自己去計算基準值的步驟。

2. 移動端1px問題

當設計師要求邊框寬度為1px(750x1334px的設計稿),開發時設置為0.01rem或者1px時,那么高dpr設備中看這個邊框,會比在設計稿中看起來要粗。

<略>這里所說的border更粗,是分別看設計稿和設備上的border和其他元素的比例,而不是去比較2個不同dpr的設備上顯示1px border的物理尺寸
<略>不同的瀏覽器處理小于1px的方式不同,有些采用四舍五入,有些大于某個值展示1px否則就不展示,有些只是線條的顏色變淺了,從視覺上看就變細了

  • 邊框設置為0.01rem,在邏輯寬度為375px的設備上計算為0.5px(根元素字號50px),如果設備將0.5四舍五入為1px
  • 或者由于設備可能不支持小于1px的值而直接設置border-width: 1px

也就是說,設計師要求的1px是使用1排物理像素點的寬度,而開發時設置的1px是使用1排邏輯像素,而在高dpr設備中,顯示1px會使用多個物理像素點,實際上我們使用了多排物理像素來顯示這1px

image.png

1.1 媒體查詢
在高dpr設備中再使用小于1的值

.border {
  border: 1px solid #fff;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2) {
  .border {
    border-width: 0.5px;
  }
}

但這種方法只在ios上支持,安卓上小于1px的可能會四舍五入或當做0處理。所以出現了第二種方式:

1.2 偽元素模擬邊框 + scale縮放
為偽元素設置1px的邊框,在此基礎上再利用transform: scale()縮小,避免直接使用小于1px值時各設備兼容性處理不同。

.border { position: relative; }
.border::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;

 /*---------注意這里----------*/
  box-sizing: border-box;
  width: 200%; /* 因為之后要縮小到0.5 所以初始尺寸先放大 */
  height: 200%;
  border: 1px solid #000;
  transform: scale(0.5);
  transform-origin: top left;
 /*--------------------------*/
}

dpr為1的設備其實只需要正常使用1px,不需要縮放;dpr為2時scale(0.5),dpr為3時應scale(0.3333)。可以配合媒體查詢在不同dpr時設置不同的縮放值

:root {
  --borderScale: 1;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2) {
  :root {
    --borderScale: 0.5;
  }
}

@media only screen and (-webkit-min-device-pixel-ratio: 3) {
  :root {
    --borderScale: 0.3333;
  }
}

.border { position: relative; }
.border::after {
  /* ... */
  transform: scale(var(--borderScale));
  /* ... */
}

這種方法的缺點是對于已有偽元素的元素需要多層嵌套。
在uView框架中也使用了縮放來處理線條:

image.png

image.png

3. 小數問題

根據rem或vw去計算時,都會出現小數點問題,插件配置時給我們提供了小數位精度的選項,但如果需要展示小于1物理像素的部分,是無法精確處理的

4. 劉海屏

5. 1px → 0.667px

開發過程中遇見設置border為1px而實際computed style為0.667px,后來發現是顯示器分辨率導致的,但只有border屬性有這個問題

-
image.png
-
image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容