移動端H5解惑-頁面適配(二)

村口蹲一郎發表於2018-08-04

本文原文發表於2016年我的github,但是直到現在為止還有很多童鞋問我相關概念,於是整理下再分享一下。

原文連結:github.com/sunmaobin/s…

一、基礎概念

在瞭解如何做H5頁面適配前,大家都應該把移動端涉及的一些概念搞明白,比如:dpr 是什麼意思?

移動端H5解惑-概念術語(一)

二、為什麼要做頁面適配

2.1 PC端為什麼要解決瀏覽器相容

因為在PC端,由於瀏覽器種類太多啦,比如幾個常用的:IE、火狐、Chrome、Safari等。同時,由於歷史原因,不同瀏覽器在不同時期針對當時的WEB標準有一些不一樣的處理(雖然大部分一樣),比如:IE6、IE8、IE10+等對於一些JS事件處理、CSS樣式渲染有所不同。

而恰恰又有一些人在使用著不同型別的瀏覽器,以及不同瀏覽器的不同版本。所以,為了能讓你的網站讓這些不同的人看到效果一致,你就不得不做相容,除非這些人不是你的目標使用者。

2.2 移動端為什麼要做適配

在移動端雖然整體來說大部分瀏覽器核心都是webkit,而且大部分都支援CSS3的所有語法。但是,由於手機螢幕尺寸不一樣,解析度不一樣,或者你需要考慮橫豎屏的問題,這時候你也就不得不解決在不同手機上,不同情況下的展示效果了。

另外一點,UI一般輸出的視覺稿只有一份,比如淘寶就會輸出:750px 寬度的(高度是動態的一般不考慮)(詳情),這時候開發人員就不得不針對這一份設計稿,讓其在不同螢幕寬度下顯示 一致

一致是什麼意思?就是下面提到的幾個主要解決的問題。

三、頁面適配主要解決的問題

  1. 元素自適應問題
  2. 文字rem問題
  3. 高清圖問題
  4. 1畫素問題
  5. 橫豎屏顯示問題
  6. 手機字型縮放問題

3.1 元素自適應問題

舉個例子:

1080px 的視覺稿中,左上角有個logo,寬度是 180px(高度問題同理可得)。

那麼logo在不同的手機螢幕上等比例顯示應該多大尺寸呢?

其實按照比例換算,我們大致可以得到如下的結果:

  • 在CSS畫素是 375px 的手機上,應該顯示多大呢?結果是:375px * 180 / 1080 = 62.5px
  • 在CSS畫素是 360px 的手機上,應該顯示多大呢?結果是:360px * 180 / 1080 = 60px
  • 在CSS畫素是 320px 的手機上,應該顯示多大呢?結果是:320px * 180 / 1080 = 53.3333px

以下就是一些實現思路:

方案1:使用css的媒體查詢 @media

@media only screen and (min-width: 375px) {
  .logo {
    width : 62.5px;
  }
}

@media only screen and (min-width: 360px) {
  .logo {
    width : 60px;
  }
}

@media only screen and (min-width: 320px) {
  .logo {
    width : 53.3333px;
  }
}
複製程式碼

這個方案有2個比較突出的問題:

  1. 如果再多一種螢幕尺寸,就得多寫一個 @media 查詢塊;
  2. 頁面上所有的元素都得在不同的 @media 中定義一遍不同的尺寸,這個代價有點高。

方案2:使用 rem 單位

注意我們的推導公式:

  • 在CSS畫素是 375px 的手機上,應該顯示多大呢?結果是:375px * 180 / 1080 = 62.5px
  • 在CSS畫素是 360px 的手機上,應該顯示多大呢?結果是:360px * 180 / 1080 = 60px
  • 在CSS畫素是 320px 的手機上,應該顯示多大呢?結果是:320px * 180 / 1080 = 53.3333px
@media only screen and (min-width: 375px) {
  html {
    font-size : 375px;
  }
}

@media only screen and (min-width: 360px) {
  html {
    font-size : 360px;
  }
}

@media only screen and (min-width: 320px) {
  html {
    font-size : 320px;
  }
}

.logo{
	width : 180rem / 1080;
}
複製程式碼

方案2有效的解決了方案1中,同一個元素需要在多個 media 中寫的問題,這裡只需要多定義幾個 media 就可以大致解決問題了。

但是本方案仍然有以下問題:

  1. 針對不同的手機解析度,需要寫多套 @media 查詢語句,多一種機型就需要多寫一套查詢語句,而且隨著現在手機的層出不窮,這個頁面很有可能在一些新出的機型上有問題。
  2. 每個元素都需要除以1080這個設計稿的尺寸。

針對除以1080我們能不能直接放到html的font-size上,變成這樣:

@media only screen and (min-width: 375px) {
  html {
    font-size : 375px / 1080;
  }
}

@media only screen and (min-width: 360px) {
  html {
    font-size : 360px / 1080;
  }
}

@media only screen and (min-width: 320px) {
  html {
    font-size : 320px / 1080;
  }
}

.logo{
	width : 180rem;
}
複製程式碼

如果變成這樣,那麼 .logo 的css中就只需要按設計稿的尺寸大小寫就可以了。到底可不可以呢?

答案是:不可以

主要原因是,瀏覽器有最小字型限制:

  • PC上最小 font-size=12
  • 手機上最小 font-size=8

如果小於最小字型,那麼字型預設就是最小字型。

再來看上面的css,比如:

@media only screen and (min-width: 375px) {
  html {
    font-size : 375px / 1080; //0.347222px
  }
}

.logo{
	width : 180rem; //期望結果:375px / 1080 * 180 = 62.5px
}
複製程式碼

所以當你這麼設定font-size時,在手機上由於小於最小字型8px,所以頁面會按照預設字型算。

所以,最終就相當於你是這麼設定的:

@media only screen and (min-width: 375px) {
  html {
    font-size : 8px;
  }
}

.logo{
	width : 180rem; //實際結果:1440px
}
複製程式碼

所以,大家在設定html的font-size的時候一定要保證最小等於8px!

因而為了解決這個問題,建議大家使用Sass這種Css開發語言,可以定義公式的,這樣寫css就方便了。

最終使用Sass的程式碼如下:

@media only screen and (min-width: 375px) {
  html {
    font-size : 375px;
  }
}

@media only screen and (min-width: 360px) {
  html {
    font-size : 360px;
  }
}

@media only screen and (min-width: 320px) {
  html {
    font-size : 320px;
  }
}

//定義方法:calc
@function calc($val){
    @return $val / 1080;
}

.logo{
	width : calc(180rem);
}
複製程式碼

以上方案雖然解決了問題,但任然有以下缺陷:

  1. 不同的尺寸需要寫多個 @media
  2. 依賴css的開發工具,比如:sass/less等
  3. 所有涉及到使用rem的地方,全部都需要呼叫方法 calc() ,這個也挺麻煩的。

方案3:js動態設定根字型

由於方案2最主要的問題就是需要針對不同的螢幕尺寸,定義多個 @media,所以我們先將這個字型設定改為動態設定。

注意我們的推導公式:

  • 在CSS畫素是 375px 的手機上,應該顯示多大呢?結果是:375px * 180 / 1080 = 62.5px
  • 在CSS畫素是 360px 的手機上,應該顯示多大呢?結果是:360px * 180 / 1080 = 60px
  • 在CSS畫素是 320px 的手機上,應該顯示多大呢?結果是:320px * 180 / 1080 = 53.3333px
//獲取手機螢幕寬度
var deviceWidth = document.documentElement.clientWidth;

//將方案二中的media中的設定,在這裡動態設定
//這裡設定的就是html的font-size
document.documentElement.style.fontSize = deviceWidth + 'px';
複製程式碼

需要注意

document.documentElement.clientWidth 這個語句能獲取到的準確的手機尺寸的前提是建立在html中設定瞭如下標籤:

<meta name="viewport" content="width=device-width">
複製程式碼

要不然獲取到的結果將始終是:980(檢視原因

然後Sass中就可以按照設計稿的尺寸大小去寫就行了:

//定義方法:calc
@function calc($val){
    @return $val / 1080;
}

.logo{
	width : calc(180rem);
}
複製程式碼

如果不考慮別的因素,只是頁面元素大體適配的話,該方案基本就滿足要求了,但是現實中我們其實還有很多問題,所以我們的方案還需要繼續優化。

3.2 文字rem問題

文字也採用rem的單位主要有什麼問題呢?

  1. 可能會出現通過rem計算,最終呈現到頁面上是 23.335px 這樣的奇葩的字型大小,可能還會因此出現鋸齒、模糊不清等問題;
  2. 對於大屏希望一行顯示的文字多一些,小屏顯示的少一些,而不是一視同仁的全部顯示同樣多的文字。這樣在大屏下顯得文字特別大(下文 3.5 橫豎屏顯示問題 會仔細講)。

對於以上問題,我個人的建議

  1. 對於出現奇葩字型的問題,其實手機上表現並沒那麼明顯,主要原因是現在螢幕顯示效果統一編號,另外html對字型顯示也做優化,所以,如果產品要求不嚴格,可以不用考慮處理;
  2. 對於橫豎屏問題,看情況吧,如果要求不嚴格,也可以不用考慮。

如果一定要解決這個問題,那麼字型就不要使用rem方案了,而是繼續使用px單位。

我們上面提到 大屏 小屏 其實隱含的意思並不是手機螢幕大,而是手機的螢幕解析度不一樣,其實就是dpr不一樣,所以我們針對不同的dpr設定具體的字型就可以了。

比如,我們針對頁面的標題的字型大小就可以如下設定:

.title {
    font-size: 12px;
}
[data-dpr="2"] .title {
    font-size: 24px;
}
[data-dpr="3"] .title {
    font-size: 36px;
}
複製程式碼

3.3 高清圖問題

先來看看 這裡 這篇文章,有講解了為什麼在有些螢幕上要使用 @2x @3x 的高清圖。

再來看看 這裡 講解了具體的高清圖的解決方案。

我這裡簡單歸納下。

3.3.1 對於 <img> 標籤引入的圖片高清解決方案

1、使用 srcset 標籤

<img src="http://g.ald.alicdn.com/bao/uploaded/i1/TB1d6QqGpXXXXbKXXXXXXXXXXXX_!!0-item_pic.jpg_160x160q90.jpg" srcset="http://img01.taobaocdn.com/imgextra/i1/803091114/TB2XhAPaVXXXXXmXXXXXXXXXXXX_!!803091114.jpg 2x, http://gtms04.alicdn.com/tps/i4/TB1wDjWGXXXXXbtXVXX6Cwu2XXX-398-510.jpg_q75.jpg 3x">
複製程式碼

關於 srcset 的說明:猛戳這裡

2、使用js自帶的 Image 非同步載入圖片

<img id="img" data-src1x="xxx@1x.jpg" data-src2x="xxx@2x.jpg" data-src3x="xxx@3x.jpg"/>
複製程式碼
var dpr = window.devicePixelRatio;
if(dpr > 3){
	dpr = 3;
};

var imgSrc = $('#img').data('src'+dpr+'x');
var img = new Image();
img.src = imgSrc;
img.onload = function(imgObj){
	$('#img').remove().prepend(imgObj);//替換img物件
};

複製程式碼

3.3.2 對於背景圖片高清解決方案

1、使用 media query 來處理

/* 普通螢幕(裝置畫素比例小於等於1)使用1倍的圖 */
.css{
    background-image: url(img_1x.png);
}

/* 高清螢幕(裝置畫素比例大於等於2)使用2倍圖  */
@media only screen and (-webkit-min-device-pixel-ratio:2){
    .css{
        background-image: url(img_2x.png);
    }
}

/* 高清螢幕(裝置畫素比例大於等於3)使用3倍圖  */
@media only screen and (-webkit-min-device-pixel-ratio:3){
    .css{
        background-image: url(img_3x.png);
    }
}
複製程式碼

2、使用 image-set 來處理(有相容問題)

.css {
    background-image: url(1x.png); /*不支援image-set的情況下顯示*/
    background: -webkit-image-set(
            url(1x.png) 1x,/* 支援image-set的瀏覽器的[普通螢幕]下 */
            url(2x.png) 2x,/* 支援image-set的瀏覽器的[2倍Retina螢幕] */
            url(3x.png) 3x/* 支援image-set的瀏覽器的[3倍Retina螢幕] */
    );
}

複製程式碼

3.4 1畫素問題

什麼是 1畫素問題 ?

我們說的1畫素,就是指1 CSS畫素。

比如設計師實際了一條線,但是在有些手機上看著明顯很粗,為什麼?

因為這個1px,在有些裝置上(比如:dpr=3),就是用了橫豎都是3的物理畫素矩陣(即:3x3=9 CSS畫素)來顯示這1px,導致在這些裝置上,這條線看上去非常粗!

其實在在中手機上應該是1/3px顯示這條線。

關於 dpr,不理解的,可以看看之前的 這篇 文章。

問題描述清楚了,我們該怎麼處理呢?

方案1:使用css3的 scaleY(0.5) 來解決

比如:div的border-top的1px問題解決。

.div:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  bottom: auto;
  right: auto;
  height: 1px;
  width: 100%;
  background-color: #c8c7cc;
  display: block;
  z-index: 15;
  -webkit-transform-origin: 50% 0%;
          transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
  .div:before {
    -webkit-transform: scaleY(0.5);
            transform: scaleY(0.5);
  }
}
@media only screen and (-webkit-min-device-pixel-ratio: 3) {
  .div:before {
    -webkit-transform: scaleY(0.33);
            transform: scaleY(0.33);
  }
}
複製程式碼

但是,這種方案只能解決直線的問題,涉及到圓角之類的,就無能為力!

方案2:頁面縮放解決問題

我們先來講講頁面縮放能解決1px問題的原理示。

首先大家需要了解一些 viewport 的常識,參考:這裡

假如以下手機的 dpr=2

移動端H5解惑-頁面適配(二)

對於dpr=2的手機裝置,1px就會有 2x2 的物理畫素來渲染,但是當縮放以後其實就變成 1x1 個單位渲染了,看下面示意圖:

移動端H5解惑-頁面適配(二)

所以,我們的思路就是將真個頁面縮小dpr倍,再將頁面的根字型放大dpr倍。這樣頁面雖然變小了,但是由於頁面整體採用rem單位,當根字型放大dpr倍以後,整體都放大了,看上去整體樣式沒什麼變化。

1、將scale設定為1/dpr

假如:dpr = 2

<meta name="viewport" content="width=device-width,initial-scale=0.5">
複製程式碼

2、clientWidth獲取的值會自動擴大dpr倍

比如,以前是360px,當頁面縮小0.5倍,獲取到的值會變為720px。

不知道這個原理的,這篇文章講的還是比較清楚:這裡

var deviceWidth = document.documentElement.clientWidth;
document.documentElement.style.fontSize = deviceWidth + 'px';
複製程式碼

3、css中涉及到1畫素問題的地方使用 px 作為單位

比如:

.box{
	border : 1px solid #ddd;
}
複製程式碼

以上步驟最終整理的結果:

html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>
    <div class="box">
        <div class="logo">Logo</div>
    </div>
</body>
</html>
複製程式碼

js:

//獲取螢幕寬度、dpr值
var deviceWidth = document.documentElement.clientWidth,
    dpr = window.devicePixelRatio || 1;

//設定根字型擴大dpr倍
//由於deviceWidth當頁面縮小dpr倍時,本身獲取的值就增加dpr倍
//所以這裡不需要再乘以dpr了
document.documentElement.style.fontSize = deviceWidth + 'px';

//設定頁面縮放dpr倍
document.getElementsByName('viewport')[0]
    .setAttribute('content','width=device-width;initial-scale=' + 1/dpr)
複製程式碼

scss:

@function calc($val){
    @return $val / 1080;
}

.logo{
	width : calc(180rem);
}

.box{
	border : 1px solid #ddd;
}
複製程式碼

3.5 橫豎屏顯示問題

橫豎屏問題,就是當你橫屏手機、豎屏手機時看到的不一樣的效果問題。

我這裡要說的這個問題,就是設計師會針對橫屏或者豎屏,做不一樣的設計,比如:橫屏時顯示摘要,豎屏時只有標題,這種情況下,我們應該怎麼適配的問題。

關於橫豎屏問題,我將會分2個部分來說明:

  1. 橫豎屏顯示內容不同;
  2. 橫豎屏顯示樣式不同;

3.5.1 橫豎屏顯示內容問題

我們知道橫屏,相當於螢幕變寬了,這時候一行顯示的內容就可以更多。所以,設計師可能會對橫豎屏做2種不同的內容設計,如:

移動端H5解惑-頁面適配(二)

如果設計師本身就設計了2套樣式,那麼我們只需要準備2套css,依據橫豎屏直接顯示對應的樣式,然後html中做好相容即可。

下文會將如何判斷橫豎屏。

3.5.2 橫豎屏顯示樣式問題

這裡有個要說的就是,設計師沒有設計橫屏的樣式,那麼如果我們按照上文提到的方案去處理,那麼就會發現在橫屏模式下字型顯得非常大(因為螢幕寬了),顯示的內容就少了。

這種情況會顯得很不協調,雖然還是等比例顯示的,只不過需要多拖動下滾動條而已,但是你會覺得很怪。尤其是再有彈出框的時候,會更麻煩,彈出框有時候可能還會顯示不完全。。。

比如,下面的樣式:

移動端H5解惑-頁面適配(二)

像這種問題,我們上面提到的依據螢幕寬度自動調整根目錄font-size的大小,就有點不合適了。這樣雖然保證了橫向的比例是嚴格按照設計搞來的,但是顯示上非常醜。

所以,我們一般的做法就是在橫屏的時候將 deviceWidth=deviceHeight

  • 正常豎屏大小:
移動端H5解惑-頁面適配(二)
  • 橫屏時讓width=height
移動端H5解惑-頁面適配(二)
  • 不做橫豎屏特殊寬度處理時
移動端H5解惑-頁面適配(二)

以上3組畫面對比我們得到的效果是:

  1. 在橫屏下如果讓width=height,那麼整體頁面的寬度等於豎屏時看到的寬度,整體佈局不會有變化,只是縱向看到的內容多少發生了變化;
  2. 如果橫屏不做處理,橫屏是width其實就等於豎屏時的height,即700px,這時候整體頁面顯示非常寬,文字比較大。

所以,經過我們的實際對比體驗以後,一致認為橫屏時讓 width=height 體驗比較好。

附上核心程式碼:

var deviceWidth = document.documentElement.clientWidth,
    deviceHeight = document.documentElement.clientHeight

//橫屏狀態
if (window.orientation === 90 || window.orientation === -90) {
    deviceWidth = deviceHeight;
};

//設定根字型大小
document.documentElement.style.fontSize = deviceWidth + 'px';
複製程式碼

3.5.3 附1:JS檢測橫豎屏

js獲取螢幕旋轉方向:window.orientation

  • 0 - 正常方向
  • -90 - 螢幕順時鐘旋轉90度
  • 90 - 螢幕逆時針旋轉90度
  • 180 - 螢幕旋轉180度
window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() {
    if (window.orientation === 180 || window.orientation === 0) { 
        console.log('豎屏狀態!');
    };
    if (window.orientation === 90 || window.orientation === -90 ){ 
        console.log('橫屏狀態!');
    }  
}, false); 
複製程式碼

3.5.4 附2:CSS判斷橫豎屏

  • 寫在同一個CSS中:
@media screen and (orientation: portrait) {
  /*豎屏 css*/
} 
@media screen and (orientation: landscape) {
  /*橫屏 css*/
}
複製程式碼
  • 分開寫在2個CSS中,在link中通過media篩選載入:
<!-- 豎屏 -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">

<!-- 豎屏 -->
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">

複製程式碼

3.6 手機字型縮放問題

手機字型縮放是什麼問題呢?

就是當你在手機 設定 -> 字型設定 中將字型放大或者縮小,使得手機整體系統字型發生了變化,這時候可能就會影響到H5頁面正常的顯示。

經過實際測試,這個問題當前發生的概率不是很大,因為很多手機廠商都已經做了保護措施。但是為了保險起見,我們還是有必要進行檢測,一旦不一樣就要採取措施。

3.6.1 如何檢測手機字型不是預設字型

var deviceWidth = document.documentElement.clientWidth;

//設定根字型大小
var setFz = deviceWidth + 'px';
document.documentElement.style.fontSize = setFz;

//獲取實際html字型大小
var realFz = window.getComputedStyle(document.documentElement)
	.getPropertyValue('font-size') //如:360px

//比較二者是否相同
if(setFz !== realFz){
    //TODO 設定的字型和實際顯示的字型不同
};
複製程式碼

3.6.2 如果手機字型不是預設字型如何處理

比如:你想設定的字型大小為100px,但是實際大小卻為50px,那麼你可以確定其實使用者字型是縮放了0.5,這時候你就需要將你的字型擴大1倍,這樣才能保證實際頁面的字型是100。

所以,按照這個等比例換算以後,我們就需要重新設定頁頁面的font-size。

var deviceWidth = document.documentElement.clientWidth;

//設定根字型大小
var setFz = deviceWidth + 'px';
document.documentElement.style.fontSize = setFz;

//獲取實際html字型大小
var realFz = window.getComputedStyle(document.documentElement)
	.getPropertyValue('font-size') //如:360px

//比較二者是否相同
if(setFz !== realFz){
    //去掉單位px,下面要參與計算
    setFz = parseFloat(setFz);
    realFz = parseFloat(realFz);
    
    //重新計算要設定的新的字型大小
    //公式推導:100 -> 50,x -> 100,求x?
    //即:setFz -> realFz, x -> setFz,求x?
    var x =  setFz * setFz / realFz;
    
    //重新設定html的font-size
    document.documentElement.style.fontSize = x + 'px';
};
複製程式碼

這麼做理論上已經解決了問題,但是還有點瑕疵,問題就是:

  • 如果字型不是預設字型,首先會設定一次font-size,重新計算後再次設定一次font-size,反應到頁面上就是頁面可能會快速晃動一次,因為兩次設定的字型大小不一樣,如果手機配置不高,估計晃動會很明顯。

如何解決這個問題?思路就是:

  1. 給頁面增加一個隱藏元素,設定其字型大小為100px
  2. 獲取這個元素在頁面上的實際字型大小是否為100px
  3. 如果設定的和實際的不等,則計算其比例。

最終程式碼片段如下:

var deviceWidth = document.documentElement.clientWidth;

var setFz = '100px';

//給head增加一個隱藏元素
var h = document.getElementsByTagName('head')[0],
    s = document.createElement('span');
    s.style.fontSize = setFz;
    s.style.display = 'none';
    h.appendChild(s);

//判斷元素真實的字型大小是否100px
//如果不相等則獲取真實的字型換算比例
var realFz = getComputedStyle(s).getPropertyValue('font-size');

if(setFz !== 'realFz'){
    //去掉單位px,下面要參與計算
    setFz = parseFloat(setFz);
    realFz = parseFloat(realFz);
    
    //由於:var x = setFz * setFz / realFz;
    //公式推導:x -> setFz, y -> deviceWidth
    //所以:var y = deviceWidth * x / setFz;
    
    //重置deviceWidth
    deviceWidth = deviceWidth * setFz / realFz;
};

document.documentElement.style.fontSize = deviceWidth + 'px';
複製程式碼

四、最終適配方案(v1.0)

除了上面一步步的分析中間的原理之外,我們可能還需要考慮或者遇到以下問題:

  1. 到底什麼時候動態初始化上面的js指令碼?
  2. 偶爾還可能會遇到進入頁面時 document.documentElement.clientWidth=0 的bug?
  3. 頁面晃動?頁面空白?頁面整體凌亂然後又正常等問題?

所以,在儘可能的解決諸多問題後,最終的指令碼如下:

html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>
    <!-- 正文 -->
</body>
</html>
複製程式碼

js:

/**
 * @DESCRIPTION 移動端頁面適配解決方案 v1.0
 * @AUTHOR      Night
 * @DATE        2018年08月01日
 */
(function(window, document){
    var docEle = document.documentElement,
        dpr    = window.devicePixelRatio || 1,
        scale  = 1 / dpr;
    
    var fontSizeRadio = 1, //手機字型正常比例
        isLandscape   = false;//是否橫屏
    
    ///////////////////////// viewport start //////////////////////////////////
    
    //設定頁面縮放比例並禁止使用者手動縮放
    document.getElementsByName('viewport')[0].setAttribute('content','width=device-width,initial-scale='+scale+',maximum-scale='+scale+',minimum-scale='+scale+',user-scalable=no');
    
	///////////////////////// viewport end //////////////////////////////////
    
    //橫屏狀態檢測
    if (window.orientation === 90 || window.orientation === -90) {
        isLandscape = true;
    };

    ///////////////////// system font-size check start //////////////////////
    
    //試探字型大小,用於檢測系統字型是否正常
    var setFz = '100px';

    //給head增加一個隱藏元素
    var headEle = document.getElementsByTagName('head')[0],
        spanEle = document.createElement('span');
        spanEle.style.fontSize = setFz;
        spanEle.style.display = 'none';
        headEle.appendChild(spanEle);

    //判斷元素真實的字型大小是否setFz
    //如果不相等則獲取真實的字型換算比例
    var realFz = getComputedStyle(headEle).getPropertyValue('font-size');

    if(setFz !== 'realFz'){
        //去掉單位px,下面要參與計算
        setFz = parseFloat(setFz);
        realFz = parseFloat(realFz);

        //獲取字型換算比例
        fontSizeRadio = setFz / realFz;
    };
    
    ///////////////////// system font-size check end //////////////////////
    
    var setBaseFontSize = function(){
        var deviceWidth = docEle.clientWidth,
            deviceHeight= docEle.clientHeight;
        
        if(isLandscape){
            deviceWidth = deviceHeight;
        };
        
        docEle.style.fontSize = deviceWidth * fontSizeRadio + 'px';
    };
    setBaseFontSize();
    
    //頁面發生變化時重置font-size
    //防止多個事件重複執行,增加延遲300ms操作(防抖)
    var tid;
    window.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(setBaseFontSize, 300);
    }, false);
    window.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(setBaseFontSize, 300);
        };
    }, false);
    
})(window, document);
複製程式碼

scss:

//設計稿尺寸大小,假如設計稿寬度750
$baseDesignWidth = 750;

@function calc($val){
    @return $val / $baseDesignWidth;
}

//適配元素採用rem,假如設計稿中元素寬度180
.logo{
	width : calc(180rem);
}

//邊框採用px,假如設計稿邊框寬度1px
.box{
	border : 1px solid #ddd;
}
複製程式碼

五、新頁面適配技術可以考慮(v2.0)

如果不太考慮老的手機型號,可以採用 viewport 單位。

由於我本人也沒有在專案中使用這個方案,所以不過多發表言論,大家有興趣的可以研究下。

具體方案細節參考:

六、後記

講了這麼多,這裡總結下,任何事情弄懂原理最重要!

比如,當你首次看 使用Flexible實現手淘H5頁面的終端適配 這篇文章的時候你會很感慨,感覺很有收穫,但是當你實際開始專案的時候,卻不知道該怎麼下手。

俗話說,臺上一分鐘,臺下十年功。

為了寫本文以及姊妹篇,我個人零零散散的時間加起來不下1個月,一直到(2016年12月2日)才發表了本文的第一版。由於收到一些流言反饋和後續知識的積累,於是決定今天(2018年08月01日)再把它重新整理一遍,以讓大家更清楚這中間的原理。

因為中間涉及的東西太多,只要有一個知識有些不清楚,可能就會卡克!比如這個概念 dips,不同的文章有不同的說法,而且還給你解釋了它跟 dip 的不同,其實就是指 CSS畫素,這些人故意發明一些專業詞彙,搞的你暈頭轉向,所以,當你看了我的這兩篇文章,也許還是一知半解,這很正常,慢慢來,多多練習,相信你會明白的。

如果還有哪裡不清楚,或者本文有錯誤的地方,感謝批評指正。

本文重新編輯於:2018年08月01日

  1. 針對大家的留言以及個人的反覆推敲,重新整理了這遍文章;
  2. 增加針對手機本身字型放大、縮小的解決方案,以及一些新的替代方案思路。

參考

(全文完)

相關文章