h5頁面適配小結

大概是去年的7月想寫這個內容去加深本身的理解。如今終於回來補上這篇入門小結了。css

1.問題描述

適配的目標:在不一樣尺寸的手機設備上,頁面「相對性的達到合理的展現(自適應)」或者「保持統一效果的等比縮放(看起來差很少,但不是徹底等比例,對於字體咱們並不喜歡等比例的去放縮)」。html

問題:手機設備的尺寸不一樣,讓頁面在不一樣的手機設備上顯示的效果看起來大體相同或者展現效果比較合理就成了一個問題。 目前移動端比較通用的幾個方案css3

  • 媒體查詢和rem 適配
  • viewport 縮放, rem 佈局,js計算
  • vw適配方案(之後可能的方案)

開始使用這幾個方法的時候,在想爲啥要這樣作,優缺點是啥咧?算法

2.關於viewport

知道了通用方法進入代碼的正題,寫一個如圖簡單的HTML頁面,在Chrome瀏覽器iPhone6模擬器下調試發現頁面的可視區域寬爲980px。npm

爲啥是980px了?默認的。瀏覽器廠商爲了讓那些傳統的爲桌面瀏覽器設計的網站在小屏幕下也可以很好顯示,因此把**佈局視口(layout viewport)**寬度設置地很大,通常在768px ~ 1024px之間,最多見的寬度是980px。瀏覽器

那爲啥980px佈局寬,能在375px屏幕寬的設備下無缺顯示了? 由於縮小。bash

Narrow screen devices (e.g. mobiles) render pages in a virtual window or viewport, which is usually wider than the screen, and then shrink the rendered result down so it can all be seen at once. Users can then pan and zoom to see different areas of the page. For example, if a mobile screen has a width of 640px, pages might be rendered with a virtual viewport of 980px, and then it will be shrunk down to fit into the 640px space. This is done because many pages are not mobile optimized, and break (or at least look bad) when rendered at a small viewport width. This virtual viewport is a way to make non-mobile-optimized sites in general look better on narrow screen devices. (Using the viewport meta tag to control layout on mobile browsers)app

大概翻譯:窄屏幕設備(例如移動設備)在虛擬窗口或視口中渲染頁面,該窗口或視口一般比屏幕寬,而後縮小渲染結果,以即可以當即看到它們。例如,若是移動屏幕的寬度爲640px,則可能使用980px的虛擬視口渲染頁面,而後縮小頁面以適應640px空間。 這樣作是由於許多頁面不是移動優化的,而且在以小視口寬度渲染時會中斷(或者至少看起來很糟糕)。 此虛擬視口是一種使非移動優化網站在窄屏設備上看起來更好的方法。雖然已經很人性化的設計了,但以下圖不經過用戶縮放和橫向滾動滾動條,仍是很難看清楚頁面內容的。框架

2.1ppk的 關於三個viewport的理論

除了上文中大概提到的 layout viewport, virtual viewport,還有ideal viewport。iphone

  • layout viewport:佈局視口,在呈現頁面以前,瀏覽器須要知道佈局視口的寬度,如沒有任何進一步的說明(如設置),瀏覽器本身選擇寬度。瀏覽器選擇了佈局視口的尺寸,使其在徹底縮小模式下徹底覆蓋屏幕。
  • virtual viewport:可見視口,可視視口是當前在屏幕上顯示的頁面的部分。用戶能夠縮放以更改可視視口的大小。
  • ideal viewport:理想的視口,它提供了設備上理想的網頁大小。所以,理想視口的尺寸因設備而異。(ideal viewport 的意義在於,不管在何種分辨率的屏幕下,針對ideal viewport 而設計的網站,不須要手動縮放和橫向滾動條均可以完美地呈現給用戶)

layout viewport 和 virtual viewport的關係用下文的話來描述再好不過了

想象一下,佈局視口是一個不會改變大小或形狀的大圖像。如今你有一個較小的框架,經過它你能夠看到大圖像。小框架周圍被不透明材料包圍,這些材料遮擋了除大部分圖像以外的全部部分的視圖。您能夠經過框架看到的大圖像部分是可視視口。您能夠在保持框架的同時遠離大圖像(縮小)以一次查看整個圖像,或者您能夠靠近(放大)以僅查看一部分。您也能夠更改框架的方向,但大圖像(佈局視口)的大小和形狀永遠不會更改。

這樣看來layout viewport, virtual viewport,對移動端瀏覽器的顯示幫助仍是不夠的(瀏覽器廠商設置的一個寬度,經過必定的「自由「放縮顯示在手機設備上)。如今須要一個基礎的寬度設定,放縮比例是可控的,而後頁面恰好徹底顯示在屏幕上(指寬度上)。ppk第三個視口ideal viewport,就出現了。

三篇值得一看的文章

A tale of two viewports — part one(Concept: device pixels and CSS pixels,這個例子灰常好)

A tale of two viewports — part two,

Meta viewport

ideal viewport 和 virtual viewport 的關係

可視視口寬度=理想視口寬度/縮放係數
縮放係數 =理想視口寬度/可視視口寬度

簡單理解一下,若是理想視口寬度=設備寬度=375px,而後後縮放係數爲0.5,計算出可視視口寬度爲750px;
若是此時的佈局視口恰好等於750px;頁面的顯示是否是很是完美了(頁面不再是「自由「放縮顯示了)複製代碼

2.2viewport meta tag 的引入

爲了更好的控制視口的大小比列,蘋果公司在其safari瀏覽器中引入meta viewport(UsingtheViewport),安卓以及各大瀏覽器廠商也都紛紛引入。下面這個標籤是不少人接觸移動端頁面都會看到的。那麼這個標籤作了什麼了?

①將佈局視口寬度設置爲理想的視口寬度width=device-width,

②根據初始縮放係數和理想視口寬度計算出但是視口,

③將佈局視口寬度設置爲剛剛計算的可視視口寬度(佈局視口寬度取②,③計算中值大的)。 (如今看來,頁面的初始佈局寬度,以及放縮係數是可控靠譜的了,不容易啊,雖然這個標記被流行的移動瀏覽器支持,但目前仍是草案)

<meta name="viewport" content="width=device-width,initial-scale=1">複製代碼

3.DPR,設備像素,備獨立像素

用以下的標籤歡快的按着設計稿,以px爲單位寫着代碼,而後設計師看了效果圖就跑來了,這個邊框怎麼這麼粗呀?圖標,這個圖標怎麼看起來這麼模糊啊?還有怎麼這個圖標在這個小屏幕手機上面這麼大......

<meta name="viewport" content="width=device-width,initial-scale=1">複製代碼

3.1一些須要的概念

物理像素(physical pixel) :一個物理像素是顯示器(手機屏幕)上最小的物理顯示單元,在操做系統的調度下,每個物理像素都有本身的顏色值和亮度值。從屏幕在工廠生產出的那天起,它上面物理像素點就固定不變了,單位 pt(同設備像素)。

設備獨立像素:(又稱設備無關像素 Device Independent Pixels 、密度獨立性 Density Independent或設備獨立像素,簡稱DIP或DP)是一種物理測量單位,基於計算機控制的座標系統和抽象像素(虛擬像素),由底層系統的程序使用,轉換爲物理像素的應用。

css像素: CSS像素是一個抽像的單位 ,1個 CSS 像素的大小在不一樣物理設備上看上去大小老是差很少。(爲了保證瀏覽的一致性)關於一些長度單位的介紹(有關於css像素描,圖片來源於此)

設備分辨率對像素單元的影響:1px乘1px的區域被低分辨率設備(例如典型的計算機顯示器)中的單個點覆蓋,而同一區域被16個點覆蓋 在更高分辨率的設備(如打印機)中。在不一樣的設備之間,1個CSS像素所表明的物理像素是能夠變化的。

DPR:設備像素比簡稱爲dpr,其定義了物理像素和設備獨立像素的對應關係。它的值能夠按下面的公式計算獲得:

設備像素比 = 物理像素 / 設備獨立像素

問題:iphone5 的dpr 是 2 屏幕寬度320px 那麼它的設備物理像素寬是多少?(640)複製代碼

來看看圖兒(內容來源A tale of two viewports — part one

①CSS像素與設備像素徹底重疊。

②CSS像素拉伸,如今一個CSS像素與幾個設備像素重疊。

③CSS像素開始縮小,一個設備像素如今與幾個CSS像素重疊。

在一樣一個設備上,1個CSS像素所表明的物理像素是能夠變化的;

3.2圖像模糊的由來,1px邊框問題

位圖:是由像素(Pixel)組成的,像素是位圖最小的信息單元,存儲在圖像柵格中。

圖像問題:理論上,1個位圖像素對應於1個物理像素,圖片能夠完美清晰的展現。一個位圖像素是柵格圖像(如:png, jpg, gif等)的最小數據單元。在Retina屏幕下(此時dpr假設爲2)200 ×200大小的圖片,樣式大小也設置爲width:200px;,heigth:200px;此時1px像素被4個物理像素點填充。1個位圖像素對應了4個物理像素,因爲單個位圖像素不能夠再進一步分割,因此只能就近取色,從而致使圖片模糊。( 移動端高清、多屏適配方案,此段內容觀點和圖片來源於此)

同理反過來在普通屏幕下(此時dpr假設爲1),400 ×400大小的圖片,樣式大小也設置爲width:200px;,heigth:200px;(咱們習慣說此時用的2倍圖)_。_一個物理像素點對應4個位圖像素點,因此它的取色也只能經過必定的算法獲得,顯示結果就是一張只有原圖像素總數四分之一的圖,(咱們稱這個過程叫作downsampling)肉眼看上去圖片不會模糊,可是會以爲圖片缺乏一些銳利度,或者是有點色差。

1px 物理像素邊框問題:在頁面不設置放縮的狀況下是很難實現的。


如圖1dpr倍屏,2dpr倍屏,3dpr倍屏,須要實現1px 物理像素邊框border: 1px;,border: 0.5px;border: 0.33px; 然而有的瀏覽器並不能識別0.5px,0.33px。

4.移動端頁面適配的簡單解決

一些Relative lengths,rem,em vw,vh......此處用到了 rem ,rem的官方定義來一下~~(www.w3.org/TR/css3-val…)相對於根元素(即html元素)font-size計算值的倍數。

舉個例子,若是頁面的html的font-size 設置 爲 20px,那麼 1rem= 20px;

再舉個例子:以iPhone6的設計稿爲爲基礎來計算(由於我家設計師喜歡出iPhone6的稿子)

設 備 設備寬度 根元素font-size/px 屏幕寬
iPhone5 320 17.066(約等於) 18.75rem
(baseWidth)iPhone6 375 20 18.75rem
iPhone6 Plus 414 22.080(約等於) 18.75rem
1 //假設屏幕屏幕寬度 等於佈局寬度 等於可視窗口寬度。
 2 // iPhone6 (18.75份是隨便取的)
 3 以iPhone6爲基礎,屏幕寬度爲375px,將屏幕寬度分紅18.75份,每一份寬度爲20px;
 4 設置html的font-size 爲20px; 1rem = 20px;
 5 // iPhone5
 6 iPhon5,屏幕寬度爲320px,將屏幕寬度分紅18.75份,每一份寬度爲17.066;
 7 設置html的font-size 爲17.066; 1rem 約等於 17.066;
 8 
 9 以iphonp6的設計稿某div的高爲20xp 寬爲20px 寫了一個樣式
10 .haha {
11     width: 1rem;  /* iphonp6 下顯示爲20px */;
12     height: 1rem; /* iphonp6 下顯示爲20px */
13 }
14 //上述那段css在iPhone5下 表達的寬高是多少了
15 .haha {
16     width: 1rem;  /* (320/18.75)px*/;
17     height: 1rem; /* (320/18.75)px*/
18 }
19 // 看一組數字 320/375 = (320/18.75)/20 屏幕寬度比,等於設計稿圖片放縮比。
20 // 這樣設計稿就成比例在不一樣寬度手機屏幕上面顯示了複製代碼

根元素fontSize公式:width/fontSize = baseWidth/baseFontSize

4.1媒體查詢和rem 適配

先參考一下 好比微博和京東,咦~~ 用的是媒體查詢設置。

@media only screen and (max-width: 640px) and (min-width: 414px) { 
    html {
        font-size: 22.08px;   
    }
}
@media only screen and (max-width: 414px) and (min-width: 375px) {
    html {
        font-size: 18.75px;    
    }
}
@media only screen and (max-width: 375px) {    
    html {
        font-size: 17.066px;
    }
}
// 這樣不是徹底的運用了width/fontSize = baseWidth/baseFontSize 這個公式,只是選了幾個寬度區間
// 來設置複製代碼

相對於根元素(即html元素)的font-size值設置好了,而後就是按照設計稿寫代碼了,問題來了px單位轉換成rem人工計算頭有點大(在iPhone6 下面每個都要除以20 換算出rem單位)。

比推薦的方法有兩種一種

  • px2rem (npm安裝)
  • Sass函數、混合宏功能來實現
// 方法一  例子從文檔上面抄下來的
.selector {
    width: 150px;
    border: 1px solid #ddd; /*no*/
}
//轉換事後
.selector {
    width: 7.5rem;
    border: 1px solid #ddd;
}
//方法二
$rem-base: 20px !default; // baseFontSize 
@function rem($value, $base-value: $rem-base) {
  $value: strip-unit($value) / strip-unit($base-value) * 1rem;
  @if ($value == 0rem) { $value: 0; } // Turn 0rem into 0
  @return $value;
}

@function strip-unit($num) {
  @return $num / ($num * 0 + 1);
}
.haha {
    width: rem(150); //經過 rem($value, $base-value: $rem-base) 計算出來 7.5rem。
}複製代碼

我的更喜歡方法一,由於別人寫的UI組件一般用的是px。而後就是字體,字體大小建議不要轉rem。

用媒體查詢查詢的方法就比較要關注手機屏幕寬度了(若是作得細緻,仍是要針對每一個屏幕寬劃分區間),且對於圖片的問題仍是沒有解決。

4.2viewport 縮放,rem 佈局,js計算

動態的設置根元素(即html元素)font-size的值,也有兩種方式(一般代碼在head加載,避免頁面重繪)

// 方法一 (iPhone 6尺寸做爲設計稿基準)
//document.documentElement.clientWidth /18.75
var e = (document.documentElement.clientWidth / 375) *20 ;
document.documentElement.style.fontSize = e + "px"
添加標籤到HTML
<meta name="viewport" content="width=device-width,initial-scale=1">
這樣作就使得全部屏幕都是基於iphone6的設計稿等比例顯示了

// 方法二 (iPhone 6尺寸做爲設計稿基準)動態寫入 viewport 放縮

var e = (document.documentElement.clientWidth / 375) *20 ;
document.documentElement.style.fontSize = e + "px"
var initScale = 1 / window.devicePixelRatio; // initScale  = 1/2;
viewPortMeta = window.document.createElement("meta");
viewPortMeta.setAttribute("name", "viewport");
viewPortMeta.setAttribute("content", "width=device-width, initial-scale=" +
      initScale + ", user-scalable=no");
//iphone6 的物理像素是 750pt*1334pt
// initScale  = 1/2
// device-width = 375
// 頁面但是視口大小 = 750px; 佈局視口大小也就等於750px
// 此時一個物理像素 對應 一個css像素  (此時圖片模糊問題就解決了)複製代碼

方法一:有1px物理像素問題,和圖片問題(網上有衆多解決方法能夠看看),

// JS判斷是否支持0 .5 px的邊框, 是的話, 則加上hairlines的類名。(以iphone6爲例)

if (window.devicePixelRatio && devicePixelRatio >= 2) {
    var testElem = document.createElement('div');
    testElem.style.border = '.5px solid #000';
    document.body.appendChild(testElem);

    //當div存在
    if (testElem.offsetHeight == 1) {
        document.querySelector('html').classList.add('hairlines');
    }

    //添加完hairlines類名後,則刪除div
    document.body.removeChild(testElem);
}
// 圖片的能夠考慮實際位置,加載不一樣倍數的圖片(我的以爲沒有必要都用2倍圖)
// 不少網站 採用的都是這個方法複製代碼

方法二: 安卓機的dpr神奇,且部分機型放縮狀況怪異,因此一般會在iphone下考慮放縮,安卓選擇放棄,安卓機的作法就跟方法一 同樣了。

//計算 initScale 的修改
dpr = win.devicePixelRatio;
dpr = isIphone ? (dpr >= 3 ? 3 : (dpr >= 2 ? 2 : 1)) : 1;
initScale = 1 / dpr;複製代碼

方法二:須要注意字體的問題,不建議字體跟着屏幕大小變化。一般用js的方法還會給根元素多加一個類或屬性來控制字體顯示。

//給<html>元素添加data-dpr屬性,而且動態改寫data-dpr的值,或者動態寫一個dpr的class到根元素上
//eg. <html data-dpr="1" class="dpr1">
// 動態寫dpr的class 
@mixin font-dpr ($font-size) {
  font-size: $font-size;
  .dpr1 & {
    font-size: $font-size * 1;
  }
  .dpr2 & {
    font-size: $font-size * 2;
  }
  .dpr3 & {
    font-size:  $font-size * 3;
  }
}
//動態改寫data-dpr的值
@mixin font-dpr($font-size){
  font-size: $font-size;
  [data-dpr="2"] & {
      font-size: $font-size * 2;
  }
  [data-dpr="3"] & {
      font-size: $font-size * 3;
  }
}複製代碼

4.3vw適配方案(之後可能的方案)

vw unit: Equal to 1% of the width of the initial containing block.

vh unit:Equal to 1% of the height of the initial containing block.

1vw = 1%視口寬度,看到這個表達是否是內心面一驚喜,3.2的方法就是將屏幕分紅多少份,而後根元素(即html元素)的font-size值,每一份用rem來表示。如今vw的出現就更符合技術須要了由於它自動將視口寬度分紅了100份。

//假設屏幕屏幕寬度 等於佈局寬度 等於可視窗口寬度。
//iPhone 6尺寸做爲設計稿基準
//<meta name="viewport" content="width=device-width,initial-scale=1">
// 此時 1vw = 375/100 =3.75px;
// 此時就差將px 轉換爲 vw了(此處Sass函數舉個例子)

$base_width: 375;
@function pxToVW($px) {
    @return ($px / $base_width) * 100vw;
}
// iPhone 6 設計稿中 某 div 寬度爲 75 px 高度 75px 表達以下
.haha {
    width: pxToVW(75); // 20vw
    height: pxToVW(75); // 20vw
}
//上述那段css在iPhone5下 表達的寬高是多少了
.haha {
    width: pxToVW(75); // 20vw   320/100*20 = 64px
    height: pxToVW(75); // 20vw   320/100*20 = 64px
}
// 看下數字題 64/75 = 320/375  設計稿在屏幕上的顯示等比放縮了複製代碼

vw好用,但它還存在兼容性問題,能夠經過這個網站查閱 Can I use。不過也有大神寫了文章介紹怎麼在實際項目去使用vw。

5.小結

文中例子比較粗糙,理解不許確之處,還請教正。關於移動端適配部分方法,本文也是描述基礎思想原理,還有不少細節,兼容問題沒有說起,要真的去理解它,還需多看文檔,代碼實踐。

相關文章
相關標籤/搜索