致謝
一直以來移動端適配便是困擾自己的一個難題,今天休息,查閱了很多資料將其整理出來。本文中很多做法是直接借鑑了網上的文章,主要有從網易與淘寶的font-size思考前端設計稿與工作流跟手機端頁面自適應解決方案—rem佈局進階版(附原始碼示例),向大神致謝。
移動裝置的解析度
裝置 | 螢幕尺寸 | 邏輯解析度(pt) | Reader | 物理解析度(px) |
---|---|---|---|---|
iPhone 3GS | 3.5寸 | 320*480 | @1x | 320*480 |
iPhone 4/4S | 3.5寸 | 320*480 | @2x | 640*960 |
iPhone 5/5S/5C | 4.0寸 | 320*568 | @2x | 640*1136 |
iPhone 6/6S | 4.7寸 | 375*667 | @2x | 750*1134 |
iPhone 6/6S Plus | 5.5寸 | 414*736 | @3x | 1242*2208 |
- pt是邏輯解析度,簡單理解就是跟螢幕的尺寸有關係,是個長度跟視覺的單位。
- px是物理解析度,簡單理解為畫素點,跟螢幕的尺寸是沒關係的。
小結:pt與px的關係是,一個單位的pt裡包含幾個px,包含的px越多,則越清晰。但因為人視網膜的關係,最多隻能識別單位pt裡2個畫素點,大於2個畫素點,人眼識別不出,所以6plus看上去不會比6更清晰。
移動端web開發的設計稿與工作流
移動端web開發的難點之一是適應各種解析度的移動裝置——眾所周知,應該使用rem。 但實際操作中,如何確定html上的font-size,是一個難點。一般有以下幾種方式:
使用CSS3媒體查詢
html{font-size:10px}
@media screen and (min-width:321px) and (max-width:375px){html{font-size:11px}}
@media screen and (min-width:376px) and (max-width:414px){html{font-size:12px}}
@media screen and (min-width:415px) and (max-width:639px){html{font-size:15px}}
@media screen and (min-width:640px) and (max-width:719px){html{font-size:20px}}
@media screen and (min-width:720px) and (max-width:749px){html{font-size:22.5px}}
@media screen and (min-width:750px) and (max-width:799px){html{font-size:23.5px}}
@media screen and (min-width:800px){html{font-size:25px}}
複製程式碼
用CSS3媒體查詢明顯不足有:
- 需要寫大量的媒體查詢以適應不同的裝置
- 媒體查詢的範圍不一定合適
- 每個媒體查詢裡的font-size難以定義
- 每次給元素設定rem都需要根據某個解析度html的font-size去算,工作量大。
小結:CSS3媒體查詢理論上可以,但操作起來不靈活
簡單問題簡單解決
有些web app比較簡單,記住一個開發原則就好:文字流式,控制元件彈性,圖片等比縮放。以圖描述:
網易的做法
網易的頁面複雜度較高,隨著解析度增大,網易頁面的效果也會發生明顯變化,要達到這種效果,就需要使用rem作為單位,且html的font-size是通過js計算出來的。 網易移動web的工作流可以總結如下:
第一步 設定視口的viewport
<meta name="viewport" content="initial-scale=1,maximum-scale=1, minimum-scale=1">
第二步 以iphone6/6S設計稿為基準確定body及頁面元素的尺寸
網易網頁的設計稿是基於iphone6/6S,物理解析度(設計稿寬度)為750px,邏輯解析度(deviceWidth)為375。
為了計算方便,先拿設計稿豎著的橫向解析度除以100得到body元素的寬度:750 / 100 = 7.5rem
同理,佈局時頁面上的元素的尺寸,可以拿設計圖示註的尺寸除以100得到。
第三步 計算html的fontSize以適配各個尺寸的螢幕
document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';
複製程式碼
第四步 細節調整
當deviceWidth大於設計稿的橫向解析度時,html的font-size始終等於橫向解析度/body元素寬。
之所以這麼幹,是因為當deviceWidth大於640時,則物理解析度大於1280,應該去訪問pc網站了。只需將第三步做下調整即可。
var deviceWidth = document.documentElement.clientWidth;
if(deviceWidth > 640) deviceWidth = 640;
document.documentElement.style.fontSize = deviceWidth / 6.4 + 'px';
複製程式碼
可能需要額外的媒介查詢
網頁上有一些結構是不需要隨著螢幕變大而相應調整,比如底部導航欄,此時可以使用媒體查詢。
淘寶的做法
知識預備,瞭解viewport
通常我們採用如下程式碼設定viewport:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
複製程式碼
這樣整個網頁在裝置內顯示時的頁面寬度就會等於裝置邏輯畫素大小,也就是device-width。這個device-width的計算公式為:裝置的物理解析度/(devicePixelRatio * scale)
devicePixelRatio稱為裝置畫素比,每款裝置的devicePixelRatio都是已知,並且不變的,目前高清屏,普遍都是2,不過還有更高的,比如2.5,3等。淘寶觸屏版佈局的前提就是viewport的scale根據devicePixelRatio動態設定.
- 在devicePixelRatio為2的時候,scale為0.5
- 在devicePixelRatio為3的時候,scale為0.3333
這麼做目的是為了保證頁面的大小與設計稿保持一致,比如如果是750的橫向物理解析度,那麼實際頁面的device-width也等於750。
接下來要解決的問題是:
元素的尺寸該如何計算,比如說設計稿上某一個元素的寬為150px,換算成rem應該怎麼算呢?這個值等於設計稿標註尺寸/該設計稿對應的html的font-size
。拿淘寶來說的,他們用的設計稿是750的,所以html的font-size就是75,如果某個元素時150px的寬,換算成rem就是150 / 75 = 2rem。
第一步 動態設定viewport的scale
var scale = 1 / devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content','initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
複製程式碼
第二步 動態計算html的font-size
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
複製程式碼
因為第一步已經根據devicePixelRatio動態設定scale,此時device-width便等於頁面的橫向物理解析度(document.documentElement.clientWidth),也就是設計稿的的橫向物理解析度。 淘寶的做法是,設計稿是750,那麼html的font-size就是75。
第三步 確定佈局時各個元素的尺寸
佈局的時候,各元素的css尺寸 = 設計稿標註尺寸/設計稿橫向解析度/10 = 設計稿標註尺寸/html的font-size
第四步 細節調整
- font-size可能需要額外的媒介查詢,並且font-size不使用rem,這一點跟網易是一樣的。
- 跟網易一樣,淘寶也設定了一個臨界點,當裝置豎著時橫向物理解析度大於1080時,html的font-size就不會變化了,原因也是一樣的,解析度已經可以去訪問電腦版頁面了。
與網易的做法比較
共同點:
- 都能適配所有的手機裝置,對於pad,網易與淘寶都會跳轉到pc頁面,不再使用觸屏版的頁面 都需要動態設定html的font-size
- 佈局時各元素的尺寸值都是根據設計稿標註的尺寸計算出來,由於html的font-size是動態調整的,所以能夠做到不同解析度下頁面佈局呈現等比變化
- 容器元素的font-size都不用rem,需要額外地對font-size做媒介查詢
- 都能應用於尺寸不同的設計稿,只要按以上總結的方法去用就可以了
不同點
- 淘寶還需要動態設定viewport的scale,網易不用
- 網易的做法,rem值很好計算,淘寶的做法可能需要使用less和sass的css處理器
阿里團隊的高清方案佈局
高清方案的原始碼
'use strict';
/**
* @param {Boolean} [normal = false] - 預設開啟頁面壓縮以使頁面高清;
* @param {Number} [baseFontSize = 100] - 基礎fontSize, 預設100px;
* @param {Number} [fontscale = 1] - 有的業務希望能放大一定比例的字型;
*/
const win = window;
export default win.flex = (normal, baseFontSize, fontscale) => {
const _baseFontSize = baseFontSize || 100;
const _fontscale = fontscale || 1;
const doc = win.document;
const ua = navigator.userAgent;
const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);
const UCversion = ua.match(/U3\/((\d+|\.){5,})/i);
const isUCHd = UCversion && parseInt(UCversion[1].split('.').join(''), 10) >= 80;
const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);
let dpr = win.devicePixelRatio || 1;
if (!isIos && !(matches && matches[1] > 534) && !isUCHd) {
// 如果非iOS, 非Android4.3以上, 非UC核心, 就不執行高清, dpr設為1;
dpr = 1;
}
const scale = normal ? 1 : 1 / dpr;
let metaEl = doc.querySelector('meta[name="viewport"]');
if (!metaEl) {
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
doc.head.appendChild(metaEl);
}
metaEl.setAttribute('content', `width=device-width,user-scalable=no,initial-scale=${scale},maximum-scale=${scale},minimum-scale=${scale}`);
doc.documentElement.style.fontSize = normal ? '50px' : `${_baseFontSize / 2 * dpr * _fontscale}px`;
};
複製程式碼
原理
與上述淘寶的原理一致。
動態設定 html 的font-size, 同時根據裝置DPR調整頁面的縮放值,此時device-width便等於頁面的橫向物理解析度(document.documentElement.clientWidth),也就是設計稿的的橫向物理解析度。
優勢
- 引用簡單,佈局簡便
- 根據裝置螢幕的DPR,自動設定最合適的高清縮放。
- 保證了不同裝置下視覺體驗的一致性。(老方案是,螢幕越大元素越大;此方案是,螢幕越大,看的越多)
- 有效解決移動端真實1px問題(這裡的1px 是裝置螢幕上的物理畫素)
使用
此方案也是預設 1rem = 100px,佈局時,根據設計稿尺寸/100即可得到物理解析度的尺寸
注意事項
- 如果元素的寬度超過效果圖寬度的一半(效果圖寬為640或750),果斷使用百分比寬度,或者flex佈局。可以避免類似於在 iphone6 上沒問題, 在 iphone5上會有橫向滾動條的問題
- 此方案是根據裝置的dpr動態設定html的font-size,且預設DPR是2,1rem = 100px,如果設計稿是iphone 6 sp (dpr = 3),將程式碼的最後的
flex(false, 100, 1)
修改成flex(false, 66.66667, 1)
如何與設計師協作
- 視覺設計階段,設計師按寬度750px(iPhone 6)做設計稿,除圖片外所有設計元素用向量路徑來做。設計定稿後在750px的設計稿上做標註,輸出標註圖。同時等比放大1.5倍生成寬度1125px的設計稿,在1125px的稿子裡切圖。
- 輸出兩個交付物給開發工程師:一個是程式用到的@3x切圖資源,另一個是寬度750px的設計標註圖。之所以要在@3x的圖裡切,這是因為現在市面上也有不少像魅藍note這種超高清螢幕,devicePixelRatio已經達到3了,這個切圖保證在所有裝置都清晰顯示。
- 開發工程師拿到750px標註圖和@3x切圖資源,完成iPhone6(375pt)的介面開發,需要使用上文提到的適配方法。
- 適配除錯階段,基於iPhone 6的介面效果,分別向上向下除錯iPhone 6 plus(414pt)和iPhone 5S及以下(320pt)的介面效果。由此完成大中小三屏適配。