1. 程式人生 > >移動端螢幕適配 fis3+rem方案

移動端螢幕適配 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:4)。

二、視覺稿要求

1. 現主流的iphone6寬度750px(以前是iphone4320)

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程式碼,但個人覺得是這是目前最好的方案了。