h5頁面適配小結


 

 1.問題描述

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

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

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

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

 

 2.關於viewport

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

為啥是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空間。 這樣做是因為許多頁面不是移動優化的,並且在以小視口寬度渲染時會中斷(或者至少看起來很糟糕)。 此虛擬視口是一種使非移動優化網站在窄屏設備上看起來更好的方法。雖然已經很人性化的設計了,但如下圖不通過用戶縮放和橫向滾動滾動條,還是很難看清楚頁面內容的。

 

 

 2.1ppk的 關於三個viewport的理論

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

  • 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 物理像素邊框問題:在頁面不設置放縮的情況下是很難實現的。

<meta name="viewport" content="width=device-width,initial-scale=1">

如圖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計算值的倍數。

舉個例子,如果頁面的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.小結

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM