移動端多屏適配rem方案
1. 開發移動端H5頁面
2. 面對不同分辨率、dPR的手機
3. 面對不同屏幕尺寸的手機
一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,在操作系統的調度下,每一個設備像素都有自己的顏色值和亮度值。
設備獨立像素(也叫密度無關像素),可以認為是計算機坐標系統中得一個點,這個點代表一個可以由程序使用的虛擬像素(比如: CSS像素),然后由相關系統轉換為物理像素。
所以說,物理像素和設備獨立像素之間存在著一定的對應關系,這就是接下來要說的設備像素比。
設備像素比(簡稱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:4)。
1. 現主流的iphone6寬度750px(以前是iphone4的320)
2. 在設計過程中注意兼容最小尺寸(320),如圖片中的文字、最小字體等...
問題:
1. 對于dpr=2的手機,為什么畫布大小×2,就可以解決高清問題?
2. 對于2倍大小的視覺稿,在具體的css編碼中如何還原每一個區塊的真實寬高(也就是布局問題)?
一個位圖像素是柵格圖像(如:png, jpg, gif等)最小的數據單元。每一個位圖像素都包含著一些自身的顯示信息(如:顯示位置,顏色值,透明度等)。
解決方案:@2x/@3x,然后圖片容器縮小。
2、背景圖片問題
解決方案:
width: 200px;
height: 300px;
background-image: url(image@2x.jpg);
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而非rem單位,且div的盒子模型為:border-box
這大概是前端最敏感,最關心的問題了。
先來,來看看下面的圖:
上面兩張圖分別是在iphone3gs(dpr=1)和iphone5(dpr=2)下面的測試效果,對比來看,對于1px的border的展示,它們是一致的,并無區別。
那么retina顯示屏的優勢在哪里,設計師為何覺得高清屏下(右圖)這個線條粗呢?明明和左右一樣的~
還是通過一張圖來解釋:
上圖中,對于一條1px寬的直線,它們在屏幕上的物理尺寸(灰色區域)的確是相同的,不同的其實是屏幕上最小的物理顯示單元,即物理像素,所以對于一條直線,iphone5它能顯示的最小寬度其實是圖中的紅線圈出來的灰色區域,用css來表示,理論上說是0.5px。
方案:
使用rem 針對不同手機屏幕尺寸和dpr動態的改變根節點html的font-size大小(基準值),再使用fis3-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代碼,但個人覺得是這是目前最好的方案了。
新聞熱點
疑難解答