一、適配方案的利弊
縮放
最早是直接用 px 來寫,然後用 meta 標籤裏的 scale 來縮放整個頁面,簡單粗暴。好處是簡單快速,壞處是不能控制部分樣式的縮放,一些邊框之類的在小屏會變得很細。據説早期天貓首頁就是這麼幹的。其實之後所有適配方案都是這個原理,在編碼的時候以設計稿為標準,到手機顯示時則根據不同機型的顯示寬度而縮放,只是縮放的技術不同。
rem
- 1rem 等於 <html> 的 fontSize 大小;
- rem 適配方案只需要給 <html> 的 fontSize 設置任意大小。因為設計稿一般為 750px 寬,所以 fontSize 設置為 75px 是比較常見的經驗標準,太大或太小對最終結果的精準度可能有影響。之後就可以確定 1rem = 75px,然後在還原設計稿時將 px 計算轉成 rem 即可。並且要在 JavaScript 中加入腳本(flexable.js),根據屏幕顯示寬度來動態改變 <html> 的 fontSize,屏幕寬度越大則 fontSize 越大;
- rem 的優勢是根節點 fontSize 可控(即 rem 的縮放標準完全可控),兼容性好。但始終是為了模擬出 vw 的效果,存在多一層轉換,麻煩、不直觀且精準度稍差;
vw
- 1vw 等於當前屏幕顯示區域的百分之一;
- vw 就是為動態佈局而生的,所以不需要輔助任何腳本。一般設計稿寬度為 750px,那麼還原設計稿的時候,1vw = 7.5px,根據這個將設計稿上的 px 換算成 vw 即可;
- vw優勢是精確、便捷,但兼容性稍差,標準的實現比 rem 晚 3 年;
em
- em現在一般不用來做移動端適配,這裏只是順帶一講;
- em 是唯一一個可以在局部實現相對長度的單位;
em 在 font-size 中使用是相對於父元素的字體大小,在其他屬性中使用是相對於自身的字體大小,如 width(來自:MDN)
- em的出現是為了解決文字無法跟着頁面一起放大這個問題。在ie6的那個年代,若使用px來設置 font-size ,當用户使用 Ctrl+滾輪 來放大頁面時,文字是不會跟着放大的。但是現代的瀏覽器已經沒有這個問題了;
- 利用 em 根據自身字體動態適配的特性,可以實現很多需求,如根據字體大小動態改變行高、寬度、邊距等;
二、邏輯像素和物理像素
邏輯像素就是我們平時所講的像素,也就是 CSS 中的 px ,所以邏輯像素是一種抽象化的數字單位。而物理像素是指電子屏幕的最小發光單元。
其實最早的時候,不需要這樣嚴格區分,因為那時候 1 邏輯像素就是用 1 物理像素來顯示,也就是設配像素比(DPR,物理像素和邏輯像素的比例)為 1。後來 iphone4 打破了這個規則,它那塊視網膜屏幕可以用 2 倍物理像素來顯示 1 邏輯像素,從而使手機的顯示效果更細膩。如下圖,即 DPR2 ⇒ 橫縱各有 2 個像素。
手機還有屏幕像素密度(PPI as Pixels Per Inch)的概念,也就是每英寸所包含的物理像素數量,一般來説在手機的使用視距下 PPI 達到 300 人眼就無法看出屏幕的像素點,這也就是蘋果的視網膜屏幕的名稱來由。
三、移動端圖片模糊
移動端適配圖片,由於移動端 dpr 不同,導致相同的位圖會在高 dpr 的手機上模糊,需根據 dpr 使用N倍圖可解決,即 dpr = 2 使用 2 倍圖。
原因是,位圖的像素信息(位置、顏色)是固定的,但在高 dpr 的屏幕顯示時,會用數倍的物理像素去顯示同樣的邏輯像素,底層的算法並不會將物理像素一一對應上圖片像素,而是進行就近取色,最終導致圖片模糊。如下圖:
為什麼要就近取色,而不是直接通過多個物理像素顯示同一顏色呢?是因為用數倍物理像素顯示一張圖片時,如果只是簡單粗暴的用 N 個物理像素直接顯示 1 個圖片像素,在移動端的高素質屏幕下鋸齒感會很明顯,為了解決這個問題則採用了平滑處理技術(就近取色屬於其中一個操作)。假設有一張顯示 0 的圖片,則下面是處理過程:
從上圖可以看出,dpr2 在圖像的細膩程度上能比 dpr1 處理得更好,所以才不是簡單地用 4 個物理像素代替 1 個物理像素。最終結論是不是不能直接取色,而是就近取色更好。
四、小數像素導致的圖片比例失調
做移動端適配的時候因為 px 是由 rem/vw 轉換後的值,所以肯定會存在小數的情況,小數邏輯像素無法與物理像素完美對齊的,不可能讓 1 個完整的物理像素去顯示 0.333 的邏輯像素,所以面對這種情況webkit內核會有兩種對齊方案:
藍色代表計算尺寸,黑線框代碼實際對齊後的尺寸。圖片來自:[](https://trac.webkit.org/wiki/...)https://trac.webkit.org/wiki/...
enclosingIntRect 是簡單地將渲染面積擴大 1px,保證能完全覆蓋渲染的物理像素,這個方案只在少部分地方用到,如渲染svg,為了保證盒子能完整包裹矢量圖。
pixelSnappedIntRect 則顧名思義,是指在容器內折斷像素的方案。但並不是簡單的四捨五入,因為如果僅僅四捨五入,會導致最終撐開或與容器相差太大,這個方案是為了保證最終渲染結果跟計算結果至多相差 1px。一個容器內有三張寬 14.25px 的圖片真正的大致渲染計算過程是:
- 首先將容器第一張圖寬度四捨五入,那麼第一個張圖變成 14px 寬;
- 因為第一張圖四捨五入後少了 0.25px ,那第二張圖則需要相應加上 0.25px,14.25 + 0.25 = 14.5px,四捨五入後則為 15px;
- 第二張圖多用了 15 - 14.5 = 0.5px,所以第三張圖要減去這 0.5px,14.5 - 0.5 四捨五入後為 14px;
-
最終,容器都計算寬度為 14.25 * 3 = 42.75,實際對齊物理像素後是14 +15 +14 = 43,這跟我們聲明的寬度僅誤差了 0.25px,所以最終渲染結果與小數像素的誤差不超過 1px。
從上面的計算結果來看三個 div 中第二個的寬度是不同的,所以這也就是小數像素導致樣式偏差的原因。因為這種樣式偏差在各種佈局盒子裏都會存在,但因為在圖片上效果更明顯(圖片擠壓),所以通常只在圖片顯示時需要處理這個問題。目前沒有比較好的解決方案,只能儘量寫死像素,圖片則可以使用 svg。
這一塊是關於 LayoutUint 的知識,是用來解決頁面縮放時的渲染問題的,具體可以查看 webkit 團隊的文章
五、移動端適配方案的思考(來源:CSS3 的字體大小單位「rem」到底好在哪:
- 在嵌入進客户端時,會出現不同尺寸手機的web適配和native適配不一致,導致用户體驗割裂;
- 在設計層面,當客户使用更大屏幕期望地是能看到更多的內容,而這個適配方案卻僅僅是讓5寸屏幕和4寸屏幕到的信息量是一樣的;
- 關於字體的問題,從用户體驗來説用户希望看到的字體是絕對大小的,就像看報紙,多大張的報紙,字體都應該是一樣大,有一個最適合閲讀的字體大小,而自適應打破了這種設計哲學。知乎的移動端頁面,則完全使用了 px;
參考資料:
CSS的值與單位
LayoutUnit & Subpixel Layout
rem 產生的小數像素問題
LayoutUnit
深入瞭解canvas在移動端繪製模糊的問題