移動端螢幕適配 fis3+rem方案
移動端多屏適配rem方案
背景
1. 開發移動端H5頁面
2. 面對不同解析度、dpr的手機
3. 面對不同螢幕尺寸的手機
一、概念
1、物理畫素(physical pixel)
一個物理畫素是顯示器(手機螢幕)上最小的物理顯示單元,在作業系統的排程下,每一個裝置畫素都有自己的顏色值和亮度值。
2、裝置獨立畫素(density-independent pixel)
裝置獨立畫素(也叫密度無關畫素),可以認為是計算機座標系統中得一個點,這個點代表一個可以由程式使用的虛擬畫素(比如: css畫素),然後由相關係統轉換為物理畫素。
所以說,物理畫素和裝置獨立畫素之間存在著一定的對應關係,這就是接下來要說的
3、裝置畫素比(device pixel ratio )
裝置畫素比(簡稱dpr)定義了物理畫素和裝置獨立畫素的對應關係,它的值可以按如下的公式的得到:
裝置畫素比 = 物理畫素 / 裝置獨立畫素 // 在某一方向上,x方向或者y方向
在javascript中,可以通過window.devicePixelRatio獲取到當前裝置的dpr。
在css中,可以通過:
-webkit-device-pixel-ratio,
-webkit-min-device-pixel-ratio,
-webkit-max-device-pixel-ratio
進行媒體查詢,對不同dpr的裝置,做一些樣式適配(這裡只針對webkit核心的瀏覽器和webview)。
綜合上面幾個概念,一起舉例說明下:
以iphone6為例:
1. 裝置寬高為375×667,可以理解為裝置獨立畫素(或css畫素)。
2. dpr為2,根據上面的計算公式,其物理畫素就應該×2,為750×1334。
用一張圖來表現,就是這樣:
上圖中可以看出,對於這樣的css樣式:
width: 2px;
height: 2px;
在不同的螢幕上(普通螢幕 vs retina螢幕),css畫素所呈現的大小(物理尺寸)是一致的,不同的是1個css畫素所對應的物理畫素個數是不一致的。
在普通螢幕下,1個css畫素 對應 1個物理畫素(1:1)。在retina 螢幕下,1個css畫素對應 4個物理畫素(
二、視覺稿要求
1. 現主流的iphone6寬度750px(以前是iphone4的320)
2. 在設計過程中注意相容最小尺寸(320),如圖片中的文字、最小字型等...
問題:
1. 對於dpr=2的手機,為什麼畫布大小×2,就可以解決高清問題?
2. 對於2倍大小的視覺稿,在具體的css編碼中如何還原每一個區塊的真實寬高(也就是佈局問題)?
三、圖片問題
一個位影象素是柵格影象(如:png, jpg, gif等)最小的資料單元。每一個位影象素都包含著一些自身的顯示資訊(如:顯示位置,顏色值,透明度等)。
1、retina下,圖片高清問題
解決方案:@2x/@3x,然後圖片容器縮小。
2、背景圖片問題
解決方案:
width: 200px;
height: 300px;
background-image: url([email protected]);
background-size: 200px 300px; // 或者: background-size: contain;
缺點:
1. 普通螢幕下同樣下載了@2x的圖片,造成資源浪費。
2. 普通螢幕下圖片由於downsampling,會失去了一些銳利度(或是色差)。
所以最好的解決辦法是:不同的dpr下,載入不同的尺寸的圖片。
不管是通過css媒體查詢,還是通過javascript條件判斷都是可以的。
那麼問題來了,這樣的話,不就是要準備兩套圖片了嘛?(@1x 和@2x)
我想,做的好的公司,都會有這麼一個圖片伺服器,通過url獲取引數,然後可以控制圖片質量,也可以將圖片裁剪成不同的尺寸。
所以我們只需上傳大圖(@2x),其餘小圖都交給圖片伺服器處理,我們只要負責拼接url即可。
如,這樣一張原圖:
https://img.alicdn.com/tps/TB1AGMmIpXXXXafXpXXXXXXXXXX.jpg // 原圖
可以類似這樣,進行圖片裁剪:
// 200×200
https://img.alicdn.com/tps/TB1AGMmIpXXXXafXpXXXXXXXXXX.jpg_200x200.jpg
// 100×100
https://img.alicdn.com/tps/TB1AGMmIpXXXXafXpXXXXXXXXXX.jpg_100x100.jpg
(ps: 當然裁剪只是對原圖的等比裁剪,得保證圖片的清晰嘛~)
四、border:1px問題
解決方案:border使用1px而非rem單位,且div的盒子模型為:border-box
這大概是前端最敏感,最關心的問題了。
先來,來看看下面的圖:
上面兩張圖分別是在iphone3gs(dpr=1)和iphone5(dpr=2)下面的測試效果,對比來看,對於1px的border的展示,它們是一致的,並無區別。
那麼retina顯示屏的優勢在哪裡,設計師為何覺得高清屏下(右圖)這個線條粗呢?明明和左右一樣的~
還是通過一張圖來解釋:
上圖中,對於一條1px寬的直線,它們在螢幕上的物理尺寸(灰色區域)的確是相同的,不同的其實是螢幕上最小的物理顯示單元,即物理畫素,所以對於一條直線,iphone5它能顯示的最小寬度其實是圖中的紅線圈出來的灰色區域,用css來表示,理論上說是0.5px。
五、前端佈局要求
方案:
使用rem 針對不同手機螢幕尺寸和dpr動態的改變根節點html的font-size大小(基準值),再使用fis-postprocessor-px2rem外掛讓px自動轉化成rem
1)、配置程式碼(750px的設計稿):
fis.match('*css', {
postprocessor: fis.plugin('px2rem', {
baseDpr: 2, // dpr基準
remUnit: 75, // rem 基準,由設計稿決定(750/10)
remPrecision: 6// rem 精確位數
});
});
這樣fis3就會自動按照750的大小將px轉換為rem單位;前端無需自己計算轉換;
對於動態改變根節點html的font-size,要求CSS和JS配合使用(解決閃爍問題)。
2)、CSS設定(通過裝置寬度來媒體查詢來改變html的font-size,即min-device-width/10,與fis3配置對應):
@media(min-device-width:320px){html{font-size: 32px;/*no*/}}
@media(min-device-width:360px){html{font-size: 36px;/*no*/}}
@media(min-device-width:375px){html{font-size: 37.5px;/*no*/}}
@media(min-device-width:384px){html{font-size: 38.4px;/*no*/}}
@media(min-device-width:412px){html{font-size: 41.2px;/*no*/}}
@media(min-device-width:414px){html{font-size: 41.4px;/*no*/}}
@media(min-device-width:480px){html{font-size: 48px;/*no*/}}
@media(min-device-width:540px){html{font-size: 54px;/*no*/}}
@media(min-device-width:560px){html{font-size: 56px;/*no*/}}
@media(min-device-width:600px){html{font-size: 60px;/*no*/}}
@media(min-device-width:768px){html{font-size: 76.8px;/*no*/}}
@media(min-device-width:800px){html{font-size: 80px;/*no*/}}
@media(min-device-width:1080px){html{font-size: 108px;/*no*/}}
@media(min-device-width:1280px){html{font-size: 128px;/*no*/}}
@media(min-device-width:1440px){html{font-size: 144px;/*no*/}}
@media(min-device-width:1600px){html{font-size: 160px;/*no*/}}
缺點:不夠精確。
3)、javascript設定,通過上面的公式,計算出基準值rem,然後寫入樣式,大概如下:
var dpr, rem;
var docEl = document.documentElement;
var fontEl = document.createElement('style');
dpr = window.devicePixelRatio || 1;
rem = docEl.clientWidth / 10;
// 設定data-dpr屬性,留作的css hack之用
docEl.setAttribute('data-dpr', dpr);
// 動態寫入樣式
docEl.firstElementChild.appendChild(fontEl);
fontEl.innerHTML = 'html{font-size:' + rem + 'px!important;}';
// 給js呼叫的,某一dpr下rem和px之間的轉換函式
window.rem2px = function(v) {
v = parseFloat(v);
return v * rem;
};
window.px2rem = function(v) {
v = parseFloat(v);
return v / rem;
};
window.dpr = dpr;
window.rem = rem;
這種方式,可以精確地算出不同螢幕所應有的rem基準值,缺點就是要載入這麼一段js程式碼,但個人覺得是這是目前最好的方案了。