寫在前面
在移動網民規模不斷擴大的今天,手機、平闆等移動裝置俨然已成了網民主要上網終端。迎着這個勢頭,我們這些前端汪們也接下了不少的移動web頁面開發需求。當在感歎終于不需要相容pc端低端浏覽器時卻面臨了移動多終端螢幕适配這一問題。
本人經驗尚淺,在大牛們得出的數種适配方案的基礎上也來談談這一問題。 //其實是入職前太頹廢了,找點事做
由于本文目的不在于普及基礎,是以移動端相關概念(
viewport
,
css
像素,獨立像素,實體像素,
dpr
,
dpi
,
ppi
)的闡明不再贅述。
可以參考:
- 走進視網膜
- TGidears——深入了解viewport和px
尚未有時間研究原理的同行們,可以跳過這些直接看下面的解決方案。
這裡是之前記錄的以 iPhone 6 例子的一個總結:
何為多終端适配?viewport 有多重要?
多終端适配即使某一套網頁方案能完美地展示在不同作業系統、尺寸、分辨率、dpr的裝置上。适配的原因相比也不用多說了——就是為了使網站各終端的使用者都不流失。
來看看浏覽器産商為我們提供的最原始的解決方案。
在iphone誕生前,為了留住移動端使用者,手機浏覽器嘗試通過調整内容來适應網頁,取得了不同程度的成功。
iphone上的safari沒有做絲毫的嘗試,取而代之的是在各種各樣的虛拟視窗上展示網頁,這些虛拟視窗被稱為“視圖”。使用者可以通過放大來檢視網頁的部分内容或縮小來檢視網頁的全部内容。為了給開發者提供一定程度的展現頁面的控制權,蘋果公司提供了
的
viewport
meta
元素,它可以指定虛拟視窗的大小。
——《HTML5觸摸界面設計與開發》
iphone的
viewport
(視口)預設大小為980px。意味着會使得在pc端顯示為980px的網頁以一定比例縮小(浏覽器會替我們完成這一工作)成恰好可以容納在4.7英寸(以iphone6為例)螢幕内。這樣一來,整個網頁縮小得我們不容易直接觀測到裡面的資訊,必須通過手勢放大頁面擷取。
如圖,這個頁面我沒設定
viewport
,設計稿基于iphone6,是以頁面内容寬度定為750px。很明顯看出左右兩旁的空白,即寬度為達到視口寬980px所産生的。
so——建構移動頁面時,設定合理的viewport值是首要任務。
那麼什麼是合理的viewport值呢?其實每個浏覽器的viewport都不盡相同:
- Safari iPhone:980px
- Opera:850px
- Android WebKit:800px
- IE:974px
面對衆多的預設viewport,使其統一為一個值是業界的普遍做法。常見的設定有:
<meta name="viewport" content="target-densitydpi=device-dpi,width=設計稿尺寸,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no" />
<meta name="viewport" content="target-densitydpi=device-dpi,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
是以一般來說,
viewport
的寬度會設定為
device-width
(裝置寬度,與css邏輯像素同等大小,可通過
document.documentElement.clientWidth
擷取)或者為設計稿尺寸(比如設計稿基于iphone6,
width=750
;基于ip5,
width=640
)。
這裡需要注意:
initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5
這幾個縮放數值通常是通過
ua
檢測,再通過
js
自動設定的。一般不會固定為1或者某一特定數值。但也有特殊情況。稍後讨論。
解決方案
前提
設計師給出的視覺稿應該嚴格按照某一移動裝置的螢幕實體像素制定寬度,高度視頁面内容而定。業界一般是基于iphone5或者6給出相應尺寸。是以最常見的視覺稿有750px、640px的。而1080px的大多是基于6plus的。而其他的奇葩尺寸真心不建議了。是以應事先約定好。
對于多倍圖處理可以看看 @南宮瑞揚 翻譯的這張圖:
一、自适應适配方案
1、使用場景
布局簡單,主要内容以清單形式展示:條目内容垂直分布。定位元素少,且不為主要内容元素。如知乎、美團網、微網誌一般結構為:
- 頂欄:搜尋框或網站logo等資訊
- banner
- 橫向導航:一般為若幹個平分水準空間的tap
- 内容:含有标題和描述或還包含略所圖
2、解決方案
a.設定viewport:
<meta name="viewport" content="target-densitydpi=device-dpi,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />;
b.水準方向寬度自适應,垂直方向高度固定
若元素無其他平分空間的兄弟元素,寬度可定為設計稿元素寬度/設計稿寬度*100%;若有寬度定為100%/平分空間元素個數,也可以用flex的方式定義;
c.小塊的元素(如logo,裝飾性圖檔)寬高都固定;
d.注意圖檔在retina螢幕下應使用多倍圖,但仍以1倍圖的大小确定圖檔的寬高。若是sprite圖,可設
background-size: x1圖檔寬 auto;background-position: 以x1 sprite圖檔定義
。
3、弊端
元素高度和圖檔大小以同一個尺寸在多種不同尺寸的裝置下顯示,沒達到真正适配效果。無法适應結構複雜的頁面布局。
二、固定viewport為設計稿大小
1、使用場景
頁面隻考慮移動裝置,不考慮自适應,不需要做響應式布局。非h5專題活動頁。如網易新聞、我之前做過的一個團購網站cocolife也用的是這種方式
2、解決方案
a.設定
viewport
<meta name="viewport" content="target-densitydpi=device-dpi,width=設計稿寬度,user-scalable=no" />
或通過
js
寫入
meta
var scale= parseInt(window.screen.width)/設計稿寬度;
document.write('<meta name="viewport" content="width=設計稿寬度, initial-scale = '+scale+', maximum-scale = '+scale+', maximum-scale = '+scale+', target-densitydpi=device-dpi">');
}
注意這裡的設計稿是指初始的設計稿大小。一般以
iphone6:750px;iphone5:640px
定寬。
b.頁面所有元素的寬高都按設計稿元素本身大小直接定義。
3、弊端
乍眼看上去這種方法挺好的,全部按設計稿給出的元素大小寫成固定px值,不需要各種機關轉換。所看即所得,縮放交給浏覽器,完全按視覺稿切圖。但,為什麼沒有得到普片推廣呢?
第一點是不再支援響應式布局。比如我們定義的`@media
(min-width:320px)`這裡的width實際上指的是viewport的寬度值,而我們的viewport已經固定成統一寬度了(如640px),是以,無論在哪個尺寸的螢幕下,這句媒體查詢也不再起作用了。
第二點是這種方式會讓浏覽器去縮放,那麼就可能會造成字型模糊,圖像失真。
1px border
在 不同分辨率下的顯示也會有所不同。
第三點是如果頁面要嵌入到App中時,App是以webview的形式渲染頁面的。webview實際上也是webkit核心,而最新的webkit核心對定寬支援不是很好,預設是以device-width來渲染的。
第四點是可能存在縮放失效的問題,某些安卓機不能正常的根據 meta 标簽中 width 的值來縮放 viewport,需要配合 initial-scale(即上述js方式) 。
三、淘寶的做法
1、使用場景
普遍網站都可以采用這種方式。但不包括h5專題活動頁。
2、解決方案
這裡首先推薦大漠前輩的一篇文章使用Flexible實作手淘H5頁面的終端适配。
淘寶做法的原理相信大家可能比較熟悉。簡單地說就是動态設定
html
下的
font-size
值,然後為頁面的元素采用
rem
的方式制定寬高。
我們知道
rem
的占據像素值是由根元素的
font-size
值大小決定的。打個比方,若
html
下設定了
font-size:16px;
那麼
1rem
就相當于
16px
。
淘寶的方案大緻如下:
把頁面分成10等份,若視覺稿初始寬度為
750px
,那麼每份大小為
75px
,将值給予
font-size
,那麼
1rem=75px
。加上初始的縮放值
使得寬度為
10rem
的盒子正好容納在
iphone6
視口(
375px
)上。若此時視口的大小變成
320px
,那麼js就會将
html
下
font-size
值動态改變為
64px
。
來看看
js
是怎麼動态改變
font-size
的
//UA檢測
var isAndroid = win.navigator.appVersion.match(/android/gi),
isIPhone = win.navigator.appVersion.match(/iphone/gi);
//對ios裝置的dpr進行判斷,安卓統一設定為1
var dpr= isIPhone ? Math.min(win.devicePixelRatio, 3) : 1;
scale = 1 / dpr;
var docEl = document.documentElement;
var metaEl = doc.createElement('meta');
//設定dpr和meta
docEl.dataset.dpr = dpr;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
docEl.documentElement.firstElementChild.appendChild(metaEl);
//動态設定根元素下的font-size大小
var width = docEl.getBoundingClientRect().width;
//pc等大螢幕下
if (width / dpr > 540) {
width = 540 * dpr;
}
//将頁面分為10等份,易得出750px視覺稿下根元素font-size為75px
var rem = width / 10;
docEl.style.fontSize = rem + 'px';
上面的
js
粗略地描述了一下淘寶的方案。即将
font-size
值設定成
docWidth(視覺稿大小)/10
,那麼
1rem
大小同為
docWidth/10
。是以頁面裡的元素寬高為
width/(docWidth/10)
。
舉個栗子:
若我們拿到一個基于
iphone6
的視覺稿,對其運用該種方式進行适配。由于iphone6視覺稿寬度為
750px
,那麼隻需要将視覺稿上的
元素大小初始寬高/75
寫入css。
如寬度為
750px
的
wrap
,則
width=10rem
;寬度為
375px
的
article
,則
width=5rem
。
scss
中
$ppr(pixel per rem)
變量寫法:
$ppr: 750px/10/1rem
。
元素尺寸寫法:
html { font-size: $ppr*1rem; } body { width: 750px/$ppr; }
。
四、專題活動 h5
1、使用場景
針對專題活動的滑屏h5頁面,特點是主題内容圖檔居多,并且基本采用絕對定位方式。
2、解決方案
原理:給每屏下的主要内容加一個
container
包裹元素,并設定
margin: 0 auto
使其水準居中。(保證内容在視線中央)使用
js
對
container
整體進行
transform:scale()
縮放,
scale
值根據螢幕視窗大小動态設定。
2.1 推薦一個架構以快速搭建适配的h5:pageResponsive
2.2 不使用架構的具體做法:
a.設定viewport
<meta name="viewport" content="target-densitydpi=device-dpi,width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />;
b.頁面結構
一般為
<ul>
/*---第一屏---*/
<li><div class="container"><div><li>
/*---第二屏---*/
<li><div class="container"><div><li>
/*---第三屏---*/
<li><div class="container"><div><li>
...
<ul>
一般來說h5有contain和cover兩種适配模式。
contain即将頁面固定在一個特定大小的盒子中。背景一般以與h5背景顔色相近的純色替代。像這樣:
cover即将h5每一屏的背景或者從緊貼邊緣的圖像通過調整
background-size
使之恰好容納在視窗中。像這樣:
下面說說兩種模式的h5寫法(預設遵循上述的html結構):
contain:
對于每一屏下背景或者從邊緣開始顯示的大圖定義在
li
下,采用
background-size:100% 圖像高度
的方式。
cover:
把所有元素放置在
.container
下統一進行縮放。背景定義在
.container
下。同時給
.container
設定固定的寬高,一般
320*520
的居多。
c.進行動态縮放的js
// 響應式縮放
var autoScale = function() {
var ratio = 320/508,
winW = document.documentElement.clientWidth,
winH = document.documentElement.clientHeight,
ratio2 = winW/winH,
scale;
//判斷寬和高以哪個為縮放基準
if (ratio < ratio2) {
scale = (winH/508).toString().substring(0, 6);
} else {
scale = (winW/320).toString().substring(0, 6);
}
var cssText = '-webkit-transform: scale('+ scale +'); -webkit-transform-origin:top center; opacity:1;';
$(".container").attr('style', cssText);
};
//這裡使用setTimeout是由于擷取文檔寬高時有時候不能立刻得到,造成縮放失效
setTimeout(function() {
if (document.documentElement.clientWidth/document.documentElement.clientHeight !== 320/508) {
autoScale();
} else {
$('.container').css({'opacity': 1});
}
}, 300);
總結
通過上述分析,得出以下總結供大家對适配方案進行選擇。
拿到視覺稿
↓
判斷布局是哪種類型
↓
1.内容少,絕對定位圖檔少,布局簡單,一般為上下結構
↓
是否需要适配PC?
是 → 自适應方式
否 → 自适應、rem、viewport方式
↓
是否要嵌入APP或運用響應式布局?
是 → 自适應、rem方式
否 → 自适應、rem、viewport方式 //推薦使用自适應方式
2.内容适中,絕對定位元素少
↓
是否要嵌入APP或運用響應式布局?
↓
是 → rem方式
否 → rem方式或viewport方式 //推薦使用rem方式
3.整屏專題H5;絕對定位元素多
↓
是否需要适配PC?
↓
是 → H5專題頁面的cover方式
否 → H5專題頁面的contain方式
^^感謝閱讀!