天天看點

關于H5頁面适配的問題

 1.問題描述

 适配的目标:在不同尺寸的手機裝置上,頁面“相對性的達到合理的展示(自适應)”或者“保持統一效果的等比縮放(看起來差不多,但不是完全等比例,對于字型我們并不喜歡等比例的去放縮)”。

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

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

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

 2.關于viewport

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

關于H5頁面适配的問題

為啥是980px了?預設的。浏覽器廠商為了讓那些傳統的為桌面浏覽器設計的網站在小螢幕下也能夠很好顯示,是以把**布局視口(layout viewport)**寬度設定地很大,一般在768px ~ 1024px之間,最常見的寬度是980px。哪為啥980px布局寬,能在375px螢幕寬的裝置下完好顯示了? 因為縮小。

 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)

大概翻譯:窄螢幕裝置(例如移動裝置)在虛拟視窗或視口中渲染頁面,該視窗或視口通常比螢幕寬,然後縮小渲染結果,以便可以立即看到它們。例如,如果移動螢幕的寬度為640px,則可能使用980px的虛拟視口渲染頁面,然後縮小頁面以适應640px空間。 這樣做是因為許多頁面不是移動優化的,并且在以小視口寬度渲染時會中斷(或者至少看起來很糟糕)。 此虛拟視口是一種使非移動優化網站在窄屏裝置上看起來更好的方法。雖然已經很人性化的設計了,但如下圖不通過使用者縮放和橫向滾動滾動條,還是很難看清楚頁面内容的。

關于H5頁面适配的問題

 2.1ppk的 關于三個viewport的理論

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

  • layout viewport:布局視口,在呈現頁面之前,浏覽器需要知道布局視口的寬度,如沒有任何進一步的說明(如設定),浏覽器自己選擇寬度。浏覽器選擇了布局視口的尺寸,使其在完全縮小模式下完全覆寫螢幕。
  • virtual viewport:可見視口,可視視口是目前在螢幕上顯示的頁面的部分。使用者可以縮放以更改可視視口的大小。
  • ideal viewport:理想的視口,它提供了裝置上理想的網頁大小。是以,理想視口的尺寸因裝置而異。(ideal viewport 的意義在于,無論在何種分辨率的螢幕下,針對ideal viewport 而設計的網站,不需要手動縮放和橫向滾動條都可以完美地呈現給使用者)
關于H5頁面适配的問題

 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像素所代表的實體像素是可以變化的。

關于H5頁面适配的問題

 DPR:裝置像素比簡稱為dpr,其定義了實體像素和裝置獨立像素的對應關系。它的值可以按下面的公式計算得到:

裝置像素比 = 實體像素 / 裝置獨立像素

問題:iphone5 的dpr 是 2 螢幕寬度320px 那麼它的裝置實體像素寬是多少?(640)      

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

①CSS像素與裝置像素完全重疊。②CSS像素拉伸,現在一個CSS像素與幾個裝置像素重疊。③CSS像素開始縮小,一個裝置像素現在與幾個CSS像素重疊。

關于H5頁面适配的問題

在同樣一個裝置上,1個CSS像素所代表的實體像素是可以變化的;

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

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

圖像問題:理論上,1個位圖像素對應于1個實體像素,圖檔可以完美清晰的展示。一個位圖像素是栅格圖像(如:png, jpg, gif等)的最小資料單元。在Retina螢幕下(此時dpr假設為2)200 ×200大小的圖檔,樣式大小也設定為width:200px;,heigth:200px;此時1px像素被4個實體像素點填充。1個位圖像素對應了4個實體像素,由于單個位圖像素不可以再進一步分割,是以隻能就近取色,進而導緻圖檔模糊。( 移動端高清、多屏适配方案,此段内容觀點和圖檔來源于此)

關于H5頁面适配的問題

同理反過來在普通螢幕下(此時dpr假設為1),400 ×400大小的圖檔,樣式大小也設定為width:200px;,heigth:200px;(我們習慣說此時用的2倍圖)_。_一個實體像素點對應4個位圖像素點,是以它的取色也隻能通過一定的算法得到,顯示結果就是一張隻有原圖像素總數四分之一的圖,(我們稱這個過程叫做downsampling)肉眼看上去圖檔不會模糊,但是會覺得圖檔缺少一些銳利度,或者是有點色差。

1px 實體像素邊框問題:在頁面不設定放縮的情況下是很難實作的。

<meta name="viewport" content="width=device-width,initial-scale=1">      
關于H5頁面适配的問題

如圖1倍屏,2倍屏,3倍屏,需要實作1px 實體像素邊框border: 1px;,border: 0.5px;border: 0.33px; 然而有的浏覽器并不能識别0.5px,0.33px。

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

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

關于H5頁面适配的問題

舉個例子,如果頁面的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
關于H5頁面适配的問題
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 // 這樣設計稿就成比例在不同寬度手機螢幕上面顯示了      
關于H5頁面适配的問題

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

 4.1媒體查詢和rem 适配

 先參考一下 比如微網誌和京東,咦~~ 用的是媒體查詢設定。

關于H5頁面适配的問題
關于H5頁面适配的問題
@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 這個公式,隻是選了幾個寬度區間
// 來設定      
關于H5頁面适配的問題

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

比推薦的方法有兩種一種

  • px2rem (npm安裝)
  • Sass函數、混合宏功能來實作
關于H5頁面适配的問題
// 方法一  例子從文檔上面抄下來的
.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。
}      
關于H5頁面适配的問題

個人更喜歡方法一,因為别人寫的UI元件通常用的是px。然後就是字型,字型大小建議不要轉rem。

用媒體查詢查詢的方法就比較要關注手機螢幕寬度了(如果做得細緻,還是要針對每個螢幕寬劃分區間),且對于圖檔的問題還是沒有解決。

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

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

關于H5頁面适配的問題
// 方法一 (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像素  (此時圖檔模糊問題就解決了)      
關于H5頁面适配的問題

方法一:有1px實體像素問題,和圖檔問題(網上有衆多解決方法可以看看),

關于H5頁面适配的問題
// 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倍圖)
// 很多網站 采用的都是這個方法      
關于H5頁面适配的問題

方法二: 安卓機的dpr神奇,且部分機型放縮情況怪異,是以通常會在iphone下考慮放縮,安卓選擇放棄,安卓機的做法就跟方法一 一樣了。

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

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

關于H5頁面适配的問題
//給<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;
  }
}      
關于H5頁面适配的問題

 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份。

關于H5頁面适配的問題
//假設螢幕螢幕寬度 等于布局寬度 等于可視視窗寬度。
//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  設計稿在螢幕上的顯示等比放縮了      
關于H5頁面适配的問題

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

 5.小結

 文中例子比較粗糙,了解不準确之處,還請教正。關于移動端适配部分方法,本文也是描述基礎思想原理,還有很多細節,相容問題沒有提及,要真的去了解它,還需多看文檔,代碼實踐。

寫完收工,沒寫之前和寫了之後認知看法又不一樣了。