原文連結:blog.sjfkai.com/2019/01/29/…
轉載請註明出處
最近剛接觸前端開發,接手了一個移動端H5專案。著實體會掉了前端的坑之多,和H5移動端的坑之多多。
如今專案告一段落,在這裡做一總結
螢幕自適應方案
介紹方案之前,首先還是交代一下專案背景與需求,畢竟一切方案也不能脫離實際需求。
需求與背景
- 裝置寬度 > 800px, body寬度為800px
- 320px < 裝置寬度 < 800px, 寬度根據裝置寬度自適應
- 裝置寬度 > 320px, body寬度為320px
- 大部分字型不隨寬度變化而縮放
- 設計圖寬度:1080px
自適應方案
關於自適應方案,google一搜就會有很多結果,但是總的來說個人認為最有用的還是手淘的大漠寫的一系列文章,後面會給出原文連結。總的來說主流的方案有rem
和vh
兩種。
REM(flexible)
rem
(font size of the root element)是指相對於根元素的字型大小的單位。簡單的說它就是一個相對單位。看到rem
大家一定會想起em
單位,em
(font size of the element)是指相對於父元素的字型大小的單位。它們之間其實很相似,只不過一個計算的規則是依賴根元素一個是依賴父元素計算。
所以簡而言之,就是根據螢幕寬度設定 html
標籤的 font-size
。 再在佈局時使用 rem
單位來佈局,就可以達到自適應的目的。
使用此方案,可以藉助手淘的開源專案lib-flexible。它可以自動幫你設定html
標籤的 font-size
等。將1rem
設定為螢幕的1/10
。
關於 rem
方案,大漠老師在使用Flexible實現手淘H5頁面的終端適配中進行了詳細的介紹。建議大家閱讀一下。
如你所見,大漠老師也在近期對文正進行了更新,建議大家使用更方便的 vw
方案。
VW
vw
是視口寬度的1/100,用 vw
來做自適應再合適不過了。
比如如果你的設計圖是 750px
的寬度。 對於 75px
的元素就可以設定為 10vw
。 這樣在寬度為 375px
的手機上的表現就是37.5px
。
當然,如果我們把每個 px
標註都手動轉換的話,那也是很大的工作量,
postcss-px-to-viewport可以自動幫你轉換為 vw
。 你只需要在配置時指定設計圖寬度就可以了。
同樣,強烈建議你去閱讀以下大漠老師關於 vw
佈局的文章 再聊移動端頁面的適配
我的方案 vw + rem
vw
雖好,可惜卻無法滿足我的需求。因為 vw
是整個視口寬度的1%,如果單純採用 vw
方案,是無法限制 body
最大、最小寬度的。
於是我便採用了 vw
+ rem
。 如果螢幕寬度在需要自適應的寬度之內,則將html
標籤的 font-size
設定為 10vw
。如果螢幕寬度超過最大或最小限制的話。則將html
標籤的 font-size
設定為固定值。類似於lib-flexible
,將1rem
設為了 body 寬度的1/10
。
具體 css 如下:
html {
height: 100%;
font-size: 10vw;
}
body {
font-size: 16px;
width: 100%;
height: 100%;
margin: 0 auto;
}
@media screen and (max-width: 320px) {
html{
font-size: 32px;
}
body{
min-width: 320px;
}
}
@media screen and (min-width: 800px) {
html{
font-size: 80px;
}
body{
max-width: 800px;
}
}
複製程式碼
當然,這樣在佈局時,我們就需要使用rem
單位來佈局了。 設計圖示註 px
轉 rem
單位同樣也有現成的工具。博主使用的是postcss-pxtorem。
最終的效果:
坑
以上介紹的適配方案,基本上就可以滿足大部分的需求了。 下面我們來聊一聊我都遇到了哪些坑。
小數畫素問題
由於我們的方案,所有元素根據螢幕寬度來自適應。因而很難保證轉換後的畫素為整數畫素。
在未接觸前端,或者說H5開發之前並沒有認真考慮過小數畫素的問題,最初以為就是在可現實的精度上四捨五入。真正開發時發現並不是這樣的。
比如下面這個例子,同樣的畫素值表現就不一樣:線上例項
IOS
、macOS
裝置最小畫素好像支援到了0.5px,所以上面的例子在蘋果裝置上表現並不是很明顯。
但是畢竟大部分裝置還是Android
和windows
系統。
那麼,到底瀏覽器是如何處理小數畫素的呢? rem 產生的小數畫素問題 這篇文章給出了答案:
瀏覽器在渲染時所做的舍入處理只是應用在元素的渲染尺寸上,其真實佔據的空間依舊是原始大小。
也就是說如果一個元素尺寸是 0.625px,那麼其渲染尺寸應該是 1px,空出的 0.375px 空間由其臨近的元素填充;同樣道理,如果一個元素尺寸是 0.375px,其渲染尺寸就應該是 0,但是其會佔據臨近元素 0.375px 的空間。
複製程式碼
那麼在我們的方案裡會出現什麼問題呢?
- 縮放到低於1px的元素會時隱時現
- 兩個同樣寬度的元素因為各自周圍的元素寬度不同,導致兩元素相差1px
- 寬高相同的正方形,長寬不相等了
border-radius: 50%
畫的圓不圓了
對於第一個問題,一般都會出現在標註為1px
的地方。所以大部分的外掛 postcss-pxtorem 或者 postcss-px-to-viewport 都提供了最小轉換畫素的選項。 我們只要指定最小轉換畫素,對於比較小的畫素(如:1px
),就不轉換為rem
或vw
了。當然1px
在視網膜屏同樣存在過粗的問題,我們在之後會討論。
對於剩下的幾個問題,目前本人也沒找到特別好的辦法,畢竟很多地方相差1px
是可以接受的。只有一些比較小的元素會表現的比較明顯,本人的解決辦法是不通過外掛自動轉換為rem
或vw
,而是通過js
根據裝置寬度,計算出該元素在該裝置下實際的px
。取整後動態地設定到元素的style
上。這樣就不會出現上述問題了。
如果各位有更好的解決方案的話。歡迎留言討論。
0.5px問題
由於上面小數畫素的問題,我們並沒有對1px
的元素進行轉換,所以對於750px
的設計圖上1px
的細線,在螢幕寬度為375px
的iphone6
上依舊為1px
,按比例應該是0.5px
。所以設計同學會問:“為什麼這條細線變粗了?” 我們也很無奈啊,因為0.5px
顯不出來啊……
但是轉念一想,對於DPR=2
甚至更高的裝置,1px
是由多個物理畫素渲染的,其實是可以顯示更細的線的。那麼這樣才能畫出更細的線呢?
大漠老師又出場了,《再談Retina下1px的解決方案》中給出了幾種方案:
viewport
放大為device-width
的dpr
倍數,然後縮小1/dpr
倍顯示border-image
設為一個一半透明一半顯示的圖片,以達到將邊框一分為二的目的- 同樣是上面的原理,但是使用
svg
繪製圖片 - 媒體查詢配合偽元素,為偽元素設定
1px
的邊框,然後縮小1/dpr
倍顯示
以上方案各有各的特點,2、3兩個方案畫出來的其實是0.5px
,而1、4兩個方案畫出來的更接近物理畫素的1px
cursor:pointer 元素點選背景變色的問題
對於新增了 cursor:pointer
屬性的元素,在移動端點選時,背景會高亮。
為元素新增 -webkit-tap-highlight-color: transparent;
屬性可以隱藏背景高亮。
Android瀏覽器下line-height垂直居中偏離的問題
我們常用的垂直居中方式就是使用line-height
,但是這種方法在Android
裝置下並不能完全居中。
具體原因是因為Android
中文字型排版的問題,可以參考 知乎:Android瀏覽器下line-height垂直居中為什麼會偏離?
通過設定字型,確實能夠解決一部分偏離的問題。但仍然會出現一些略微偏離的情況,據說與行高奇數偶數有關。不過已經不太容易分辨了,如果還是不能接受的話建議通過設定上下padding的方式進行垂直居中,再根據具體情況進行微調。
參考文章
- 使用Flexible實現手淘H5頁面的終端適配
- 再聊移動端頁面的適配
- rem 產生的小數畫素問題
- 再談Retina下1px的解決方案
- Android瀏覽器下line-height垂直居中為什麼會偏離?
歡迎關注公眾號 “大前端開發者”。給你帶來更多的前端技術與資訊