1. 程式人生 > 實用技巧 >移動端web佈局:適配

移動端web佈局:適配

  • viewport與移動裝置適配的核心問題是什麼?
  • meta標籤與移動適配的相關內容
  • 移動web適配

一、viewport與移動裝置適配的核心問題是什麼?

在前面一篇部落格(移動端web佈局:畫素與成像的基本原理)中詳細的介紹了畫素相關的原理,只是對螢幕清晰度做了一些演示,但在移動web中關於畫素的問題主要還是體現在佈局上,前面說過手機螢幕的參照畫素是iPhone3的畫素,如果所有手機螢幕都按照這個參照畫素的畫素比來顯示內容,會帶來一個什麼問題?

當在螢幕是iPhone4物理畫素為640*960px與iPhone6物理畫素750*1334px中顯示同一個網頁時會發什麼?

同樣的畫素比,卻在不同的螢幕寬度中來顯示,必然會出現一個的問題就是如果按照iPhone4給出合適的邏輯畫素,在iPhone6中就必然無法被填滿;如果按照iPhone6給出合適的邏輯畫素,在iPhone4中就必然超出螢幕寬度出現滾動條。

那為了適配這兩個終端的螢幕我們是需要做兩套樣式嗎?

顯然這不符合我們的開發需求,這樣這會產生大量的重複工作,降低工作效率。現實中我們都是一套樣式讓它自動適配各個終端的螢幕。這是如何做到的呢?

要解決這個適配的問題方法有幾種:

這裡我們先來了解viewport這個mate標籤屬性。

在瞭解viewport之前,建議先閱讀一下關於BOM的這篇部落格《BOM:瀏覽器物件模型之瀏覽器剖析入門》,表面上看這些內容沒什麼直接關係,但請細品。

1.1手機瀏覽器是以什麼畫素展示樣式的?現在iPhone6.7.8模型上測試下面這段程式碼:

 1 <!doctype html>
 2 <html
> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <style> 7 *{ 8 margin: 0; 9 } 10 div{ 11 width: 740px; 12 height: 100px; 13 border: 5px solid green; 14 } 15 </
style> 16 </head> 17 <body> 18 <div></div> 19 </body> 20 </html>

你會驚奇的發現這樣的結果:

引數中寫的是div的元素寬度包括邊框是750px,但是iPhone6的螢幕寬度是375px,然而這個750px的元素還沒填滿375px寬的視窗。

如果從畫素的縮放比角度來說,iPhone6的畫素縮放比(DPR)是2,從之前解析過的物理畫素、邏輯畫素、畫素縮放比的關係來理解,div由750px轉換成375不奇怪,那是為什麼這個div的實際展示寬度還小於375呢?下面我們繼續來看下面這個資料:

2.當我們將滑鼠懸停在控制檯的html標籤上,瀏覽器顯示html標籤的寬度是980,這又是為什麼?帶著這些疑問,我們來看viewport的屬性:

width:視口的寬度;
height:視口的高度;
user-scalable:是否允許使用者進行頁面縮放;
initial-scale:頁面初始縮放值;
minimum-scale:頁面最小能夠縮放的比例;
maximum-scale:頁面最大能夠縮放的比例;

2.6viewport的屬性與屬性值:

 1 //viewport屬性及引數的標準設定
 2 //標準設定(後面適配都是參照這個設定的基礎進行)
 3 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 4 1.width: 取值/正整數、device-width、帶px單位的畫素值。 不建議設定數字和像數值,安卓部分裝置不支援
 5 2.height: 與widht同理,但一般不設定
 6 3.initial-scale:  按比例縮放邏輯畫素,值為一個大於0的數值,可以帶小數
 7 4.minimum-scale:  邏輯畫素最小縮放比例,取值與initial-scale一致
 8 5.maximum-scale:  邏輯畫素最大縮放比例,取值與initial-scale一致
 9 6.user-scalable:  是否允許使用者進行頁面縮放,值為no或yes,一般設定為no
10 
11 注意:
12 1.user-scalable有時候會失效,比如部分裝置由放大文字的工具,就有可能失效,但可以通過事件處理這個問題。以及ios10中取no。
13 2.最好按照標準將所有屬性及引數寫全,比如不寫全的情況下,可能出現iphone5中的黑邊問題,等其他bug
14 
15 適配:
16 後面所有適配中都基本是改變initial-scale的取值來完成,百分比適配除外。

這裡我們主要要搞清楚width與innitial-scale這兩個屬性所表達的含義:

注:裝置獨立畫素===裝置的css邏輯畫素寬度初始值;裝置畫素===裝置的物理畫素;

width:表示瀏覽器的可視邏輯寬度,也就是瀏覽器的視窗內容區的橫向寬度,這個寬度是CSS邏輯畫素。設定寬度指為device-width是,則表示瀏覽器視口寬度就等於裝置初始邏輯畫素。

initial-scale:表示的是基於裝置的獨立畫素的伸縮比,如果設定初始值設定為1.0則頁面的CSS邏輯畫素寬度等於裝置獨立畫素,但這個數值可以取大於0的任何值,當取值大於1.0時則表示基於裝置獨立畫素縮小CSS邏輯畫素寬度,當取值小於1.0時則表示基於裝置獨立畫素放大CSS邏輯畫素寬度。

initial-scale除了可以設定裝置獨立畫素以外,還可以修改瀏覽器的視口邏輯寬度,這個種情況是在邏輯畫素大於width的畫素設定時,為什麼需要這樣的設定邏輯,請看圖解:

如果此時不放大瀏覽器的視口寬度,那CSS邏輯畫素的多出來的350px用到什麼地方去呢?

2.7以iPhone6為例計算視口寬度以及CSS邏輯畫素值:

2.7.1content="width=device-width, initial-scale=1.0"

視口寬度:375px; CSS邏輯畫素寬度375px; 此時CSS邏輯畫素375px對應物理畫素750px,則一個邏輯畫素表示兩個物理畫素; 當元素寬度大於375px時,會出現橫向滾動條;

裝置螢幕最大可以顯示375px的邏輯畫素內容。

2.7.2content="width=device-width, initial-scale=1.5"

視口寬度375px;  CSS邏輯畫素寬度375/1.5=250px; 此時CSS邏輯畫素250px對應物理畫素750px,則一個邏輯畫素表示三個物理畫素; 當元素值寬度大於250px時,會出現橫向滾動條;

裝置螢幕最大可以顯示250px的邏輯畫素內容,這時候視口超出邏輯畫素也就超出了螢幕,載入頁面就會出現橫向滾動條。

2.7.3content="width=device-width, initial-scale=0.5"

視口寬度750px;  CSS邏輯畫素寬度375/0.5=750px; 此時CSS邏輯畫素750px對應物理畫素750px,則一個邏輯畫素表示一個物理畫素; 當元素寬度大於750px時,會出現橫向滾動條;

裝置螢幕最大可以顯示750px的邏輯畫素內容,只有內容CSS畫素超出了750px時才會出現橫向滾動條。

注:這裡有一個很關鍵的問題,就是viewport的width屬性表示的是瀏覽器內容區的預設寬度,而CSS邏輯畫素寬度表示的是該頁面上多少個邏輯畫素可以填滿裝置的螢幕。這裡的CSS邏輯畫素不是裝置的邏輯畫素,而是由裝置的邏輯畫素與initial-scale的縮放比值計算而來。

展示2.7.2的測試程式碼(iPhone6):

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.5, maximum-scale=1.5, minimum-scale=1.5">
 6     <title>Document</title>
 7     <style>
 8         *{
 9             margin: 0;
10         }
11         .box div{
12             height: 100px;
13             float: left;
14         }
15         .box div:nth-child(1){
16             width: 250px;
17             background-color: red;
18         }
19         .box div:nth-child(2){
20             width: 50px;
21             background-color: yellow;
22         }
23         .box div:nth-child(3){
24             width: 75px;
25             background-color: blue;
26         }
27         .box div:nth-child(4){
28             background-color: green;
29         }
30 
31     </style>
32 </head>
33 <body>
34     <div class="box">
35         <div></div>
36         <div></div>
37         <div></div>
38     </div>
39 </body>
40 </html>
View Code

二、meta標籤與移動適配的相關內容

2.1移動web真機測試方案一:

在vs code或者webStorm中安裝live server外掛,讓後使用這個外掛啟動html頁面,然後再將IP換成本機IP,windows換成ipv4的ip,這時候將電腦與手機連結到同一個wifi下,然後在手機端的瀏覽器輸入改好ip的連結就可以在手機端真機測試了。(如果嫌手動輸入麻煩,就是可以百度一個二維碼轉換器,將連結地址換成二維碼,微信掃一掃連結生成的二維碼,然後在微信調整瀏覽器開啟網頁)

2.2移動web真機測試方案二:

由於前面一個方案外掛開啟的連結我的手機一致無法獲取頁面,可能是埠的問題,然後我就採用node+express做了一個最簡單的靜態頁面服務,專案安裝、檔案結構和程式碼如下:

npm init -y
npm install body-parser express --save

檔案結構:

 1 //工作區間
 2 node_modules
 3 page--靜態資源根目錄
 4 --index.html
 5 index.js
 6 package.json
 7 package-lock.json
 8 
 9 //index.js程式碼
10 
11 let express=require("express");
12 let app = express();
13 app.use(express.static("page"));
14 app.listen(12306);
15 
16 //電腦本地可以使用http://127.0.0.1:12306/index.html
17 //手機可以使用:http://本機ip或ipv4地址/index.html(同樣是在同一個wifi下)

2.3通過mate標籤在移動web端的一些小應用(關鍵有點炫的就是配置桌面圖示,瞬間將web轉成原生應用的感覺,但別飄,它還是個web頁面):

 1 禁止識別電話與郵箱(但是郵箱沒效果)
 2 <meta name="format-detection" content="telephone=no,email=no" />
 3 設定新增到主屏後的標題(ios)
 4 <meta name="apple-mobile-web-app-title" content="標題">
 5 新增到主屏幕後,全屏顯示,刪除蘋果預設的工具欄和選單欄(無用)
 6 <meta name="apple-mobile-web-app-capable" content="yes" />
 7 放在桌面上的logo
 8 <link rel="apple-touch-icon-precomposed" href="iphone_logo.png" />
 9 啟動時候的畫面(無用)
10 <link rel="apple-touch-startup-image" href="logo_startup.png" />
11 設定x5核心瀏覽器只能豎屏瀏覽(只有UC有效)
12 <meta name="x5-orientation" content="portrait" />
13 設定x5核心瀏覽器全屏瀏覽
14 <meta name="x5-fullscreen" content="true" />
15 設定UC瀏覽器只能豎屏瀏覽
16 <meta name="screen-orientation" content="portrait">
17 設定UC瀏覽器全屏瀏覽
18 <meta name="full-screen" content="yes">
19 如果想遮蔽所有瀏覽器的橫屏的話,需要在後面陀螺儀相關內容,在陀螺儀相關部落格中補充

三、移動web適配

移動適配的內容:字型、寬高、間距、影象(圖示、圖片)

適配目標:在不同尺寸的移動裝置上,頁面相對性的達到合理的展示(自適應)或者保持統一效果的等比例縮放(看起來差不多)。

一些移動裝置的資料鏈接:

https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions
https://material.io/tools/devices/
http://screensiz.es/phone

3.1百分比適配:

根據父級算百分比,需要配合其他佈局使用。只能設定寬度,高度一般採用固定和內容撐開的方式,而是父子級之間的畫素關聯性特別大,佈局較為固定,不適合更新頻率高的專案。

採用百分比佈局的專案有m.360.cn移動端和m.lagou.com/拉勾網移動端等。

示例為模擬360主頁部分:

主要適配包括viewport視窗寬度為預設裝置邏輯畫素寬度,頁面CSS畫素與裝置邏輯畫素比為1:1。

然後,佈局元素採用寬度百分比適配,導航高度固定,內容高度採用自適應。

  1 <!doctype html>
  2 <html lang="en">
  3 <head>
  4     <meta charset="UTF-8">
  5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  6     <title>Document</title>
  7     <style>
  8         *{
  9             margin: 0;
 10             padding: 0;
 11         }
 12         a{
 13             text-decoration: none; color: #2b2b2b;
 14         }
 15         header{
 16             padding: 0 20px;
 17             height: 48px;
 18             line-height: 48px;
 19             background-color: #23ac38;
 20         }
 21         header a:nth-child(1){
 22             float: left;
 23         }
 24         header a:nth-child(2){
 25             float: right;
 26         }
 27         img{
 28             vertical-align: middle;
 29         }
 30         header a:nth-child(1) img{
 31             height: 32px;
 32         }
 33         header a:nth-child(2) img{
 34             height: 16px;
 35         }
 36         .lanternSlide img{
 37             width: 100%;
 38         }
 39         nav ul{
 40             width: 100%;
 41             padding: 30px 0;
 42         }
 43         nav ul li{
 44             width: 25%;
 45             float: left;
 46             list-style: none;
 47         }
 48         nav ul li a{
 49             display: block;
 50             border-right: 1px solid #e5e5e5;
 51         }
 52         nav ul li img{
 53             width: 50%;
 54             padding:0 25%;
 55         }
 56         nav ul li p{
 57             padding-top: 5px;
 58             width: 100%;
 59             height: 28px;
 60             line-height: 28px;
 61             text-align: center;
 62             font-size: 14px;
 63         }
 64     </style>
 65 </head>
 66 <body>
 67     <header>
 68         <a  href="http://m.360.com">
 69             <img src="//p2.ssl.qhimg.com/t01893213e448dbbfa2.png" alt="">
 70         </a>
 71         <a href="#">
 72             <img src="//p1.ssl.qhmsg.com/t010fa93a99715aad32.png" alt="">
 73         </a>
 74     </header>
 75     <div class="lanternSlide">
 76         <img src="//p1.ssl.qhmsg.com/t01dd057241efdad2ad.png" alt="some pic">
 77     </div>
 78     <nav>
 79         <ul>
 80             <li>
 81                 <a href="#">
 82                     <img src="//p0.ssl.qhimg.com/t01f222be1309c2fd7f.png" alt="">
 83                     <p>全部產品</p>
 84                 </a>
 85             </li>
 86             <li>
 87                 <a href="#">
 88                     <img src="//p4.ssl.qhimg.com/t01d7427e1f6239b1b1.png" alt="">
 89                     <p>360搜尋</p>
 90                 </a>
 91             </li>
 92             <li>
 93                 <a href="#">
 94                     <img src="//p2.ssl.qhimg.com/t0199146ed535ecd65e.png" alt="">
 95                     <p>360商城</p>
 96                 </a>
 97             </li>
 98             <li>
 99                 <a href="#">
100                     <img src="//p1.ssl.qhimg.com/t0192c6a682125e6cc9.png" alt="">
101                     <p>360遊戲</p>
102                 </a>
103             </li>
104         </ul>
105     </nav>
106 </body>
107 </html>
模擬360移動端主頁部分

3.2viewport適配:

根據裝置邏輯畫素縮放比、瀏覽器視口、瀏覽器CSS畫素寬度三者的關係,將所有頁面的CSS畫素設定為一致的。也就是說在實際開發程式碼中,一套程式碼在任何移動裝置中表現一致。

關鍵思路:CSS畫素寬度相同,所以需要指定一個錨定的裝置邏輯畫素為CSS畫素寬度,然後通過計算獲得目標裝置邏輯畫素與錨定畫素的比值,動態配置給viewport的initial-scale的值即可。

注意:

  由於是計算比值,就可以存在不能整除,最終適配可能會與實際存在一些偏差。

  不能設定viewport的width值,存在相容Bug風險,雖然現在的裝置基本基本都沒有遇到這個問題,但不能絕對排除風險。

  雖然這種存在一些問題,但這個適配方式基本可用,一般也不會出問題。就是在設計稿要按照錨定畫素來做,但為了適配高清晰大屏裝置圖片素材需要另外按照高清晰裝置尺寸來做,不能從原設計稿上直接切。

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view">
 6     <title>Document</title>
 7     <style>
 8         *{
 9             margin: 0;
10             padding: 0;
11         }
12         .box div{
13             width: 93.75px;
14             height: 100px;
15             float: left;
16         }
17         .box div:nth-child(1){
18             background-color: red;
19         }
20         .box div:nth-child(2){
21             background-color: yellow;
22         }
23         .box div:nth-child(3){
24             background-color: green;
25         }
26         .box div:nth-child(4){
27             background-color: blue;
28         }
29     </style>
30     <script>
31         (function(){
32             let curWidth = document.documentElement.clientWidth;
33                 curWidth = window.innerWidth;
34                 curWidth = window.screen.width;
35             let targetWidth = 375;
36             let scale = curWidth / 375 ;
37             let view = document.getElementById("view");
38             view.content = "user-scalable=no, initial-scale="+ scale +", maximum-scale="+ scale +", minimum-scale="+ scale ;
39         })();
40     </script>
41 </head>
42 <body>
43     <!-- 這裡使用了一張375px寬度的圖片測試  -->
44     <img src="image/375.jpg" alt="">
45     <div class="box">
46         <div></div>
47         <div></div>
48         <div></div>
49         <div></div>
50     </div>
51 </body>
52 </html>
viewport適配

3.3DPR:

適配原理:將瀏覽器CSS畫素與裝置的物理畫素呈1:1的狀態。

這個適配算不上是一個完整的適配,因為每個裝置的物理畫素都不相同,這只是作為一個參考適配,為rem適配做準備。

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view">
 6     <title>Document</title>
 7     <style>
 8         *{
 9             margin: 0;
10             padding: 0;
11         }
12         .img{
13             width: 100%;
14         }
15         .img img{
16             width: 100%;
17         }
18         .box div{
19             width:  25%;
20             height: 100px;
21             float: left;
22         }
23         .box div:nth-child(1){
24             background-color: red;
25         }
26         .box div:nth-child(2){
27             background-color: yellow;
28         }
29         .box div:nth-child(3){
30             background-color: green;
31         }
32         .box div:nth-child(4){
33             background-color: blue;
34         }
35     </style>
36     <script>
37         (function(){
38             //基於css3屬性選擇器獲取meta標籤
39             let meta = document.querySelector('meta[name="viewport"]');
40             let scale = 1/window.devicePixelRatio;      //拿到裝置的畫素縮放比,並取其反比值
41             if(!meta){
42                 //當viewport的meta標籤不存在時,建立標籤
43                 meta = document.createElement("meta");
44                 meta.name = "viewport";
45                 meta.content = "width="+ html.clientWidth +", user-scalable=no, initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale ;
46             }else{
47                 meta.setAttribute("content",  "width=device-width, user-scalable=no, initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale);
48             }
49         })();
50     </script>
51 </head>
52 <body>
53     <!-- 這裡使用了一張375px寬度的圖片測試  -->
54     <div class="img"><img src="image/750.jpg" alt=""></div>
55     <div class="box">
56         <div></div>
57         <div></div>
58         <div></div>
59         <div></div>
60     </div>
61 </body>
62 </html>
DPR適配

3.4REM適配:

適配原理:將CSS邏輯畫素寬度分割成若干分,然後講一份的寬度設定為html的字型寬度,然後再用rem作為佈局的畫素寬度。

 1 //核心程式碼演示
 2 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view">
 3 <script>
 4     (function(){
 5         let html = window.document.documentElement;
 6         let width = html.clientWidth;
 7         //假設將css邏輯畫素切割成16等份
 8         html.style.fontSize = width / 16 + "px";
 9     })();
10 </script>
11     

3.5REM基於設計稿寬度作為基準做適配:

設計為了圖片不會失真最好的策略就是將設計稿與裝置的物理畫素相匹配,那我們在開發時為了百分百還原設計稿,最好的方式就是將CSS畫素與設計稿畫素對應起來,前面介紹了DPR適配方案,但是DPR的適配方案有一個非常大的麻煩就是每個裝置的CSS畫素都是對應裝置的物理畫素,這樣就不能使用一套程式碼完成樣式設計。

那麼換一個思路,將設計稿的畫素與CSS畫素關聯起來:

 1 //核心程式碼 designWidth為設計稿寬度
 2 <script>
 3     (function(doc, win, designWidth){
 4         const html = doc.documentElement;
 5         const refreshRem = () => {
 6             const clientWidth = html.clientWidth;
 7             html.style.fontSize = 100 * (clientWidth / designWidth) + 'px';
 8         }
 9         doc.addEventListener('DOMContentLoaded', refreshRem);
10     })(document, window, 750);
11 </script>

html字型寬度 = CSS邏輯畫素寬度 / 設計稿寬度 * 100px;

元素CSS邏輯畫素(rem) = 元素設計寬度 / 設計稿寬度 * CSS邏輯畫素寬度 /(CSS邏輯畫素 / 設計稿寬度 * 100);

            = 元素設計寬度 / 設計稿寬度 * CSS邏輯畫素寬度 / CSS邏輯畫素寬度 * 設計稿寬度 / 100;

            = 元素設計寬度 /100;

所以這時候可以實現CSS畫素與設計稿畫素呈現1:100的關係。

這裡需要提醒一下,示例中我使用了ES6的變數和函式申明語法,在ios10不相容,所以為了相容需要改成ES5語法。

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view">
 6     <title>Document</title>
 7     <style>
 8         *{
 9             margin: 0;
10             padding: 0;
11         }
12         .img{
13             width: 100%;
14         }
15         .img img{
16             width: 100%;
17         }
18         .box div{
19             width:  1.66rem;
20             height: 1.66rem;
21             float: left;
22         }
23         .box div:nth-child(1){
24             background-color: red;
25         }
26         .box div:nth-child(2){
27             background-color: yellow;
28         }
29         .box div:nth-child(3){
30             background-color: green;
31         }
32     </style>
33     <script>
34         (function(doc, win, designWidth){
35             const html = doc.documentElement;
36             const refreshRem = () => {
37                 const clientWidth = html.clientWidth;
38                 html.style.fontSize = 100 * (clientWidth / designWidth) + 'px';
39             }
40             doc.addEventListener('DOMContentLoaded', refreshRem);
41         })(document, window, 500);
42     </script>
43 </head>
44 <body>
45     <div class="img"><img src="image/750.jpg" alt=""></div>
46     <div class="box">
47         <div></div>
48         <div></div>
49         <div></div>
50     </div>
51 </body>
52 </html>
設計稿500px的寬度示例
 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view">
 6     <title>Document</title>
 7     <style>
 8         *{
 9             margin: 0;
10             padding: 0;
11         }
12         .img{
13             width: 100%;
14         }
15         .img img{
16             width: 100%;
17         }
18         .box div{
19             width:  2.16rem;
20             height: 2.16rem;
21             float: left;
22         }
23         .box div:nth-child(1){
24             background-color: red;
25         }
26         .box div:nth-child(2){
27             background-color: yellow;
28         }
29         .box div:nth-child(3){
30             background-color: green;
31         }
32         .box div:nth-child(4){
33             background-color: yellow;
34         }
35         .box div:nth-child(5){
36             background-color: green;
37         }
38     </style>
39     <script>
40         (function(doc, win, designWidth){
41             const html = doc.documentElement;
42             const refreshRem = () => {
43                 const clientWidth = html.clientWidth;
44                 html.style.fontSize = 100 * (clientWidth / designWidth) + 'px';
45             }
46             doc.addEventListener('DOMContentLoaded', refreshRem);
47         })(document, window, 1080);
48     </script>
49 </head>
50 <body>
51     <div class="img"><img src="image/750.jpg" alt=""></div>
52     <div class="box">
53         <div></div>
54         <div></div>
55         <div></div>
56         <div></div>
57         <div></div>
58     </div>
59 </body>
60 </html>
設計稿1080px的寬度示例

3.6通過媒體查詢設定根節點字型寬度:

這種根子節點字型寬度設計並不是一個連續縮放,而是每個區間的字型寬度相同,超出一個區間以後再進行縮放,需要針對每種CSS畫素寬度進行設定,並非一種理想的適配方案,但在一定範圍內也可以做到適配的效果。

  1 html {
  2     font-size: 50px
  3 }
  4 
  5 body {
  6     font-size: 24px
  7 }
  8 
  9 @media screen and (min-width: 320px) {
 10     html {
 11         font-size:21.33px
 12     }
 13 
 14     body {
 15         font-size: 12px
 16     }
 17 }
 18 
 19 @media screen and (min-width: 360px) {
 20     html {
 21         font-size:24px
 22     }
 23 
 24     body {
 25         font-size: 12px
 26     }
 27 }
 28 
 29 @media screen and (min-width: 375px) {
 30     html {
 31         font-size:25px
 32     }
 33 
 34     body {
 35         font-size: 12px
 36     }
 37 }
 38 
 39 @media screen and (min-width: 384px) {
 40     html {
 41         font-size:25.6px
 42     }
 43 
 44     body {
 45         font-size: 14px
 46     }
 47 }
 48 
 49 @media screen and (min-width: 400px) {
 50     html {
 51         font-size:26.67px
 52     }
 53 
 54     body {
 55         font-size: 14px
 56     }
 57 }
 58 
 59 @media screen and (min-width: 414px) {
 60     html {
 61         font-size:27.6px
 62     }
 63 
 64     body {
 65         font-size: 14px
 66     }
 67 }
 68 
 69 @media screen and (min-width: 424px) {
 70     html {
 71         font-size:28.27px
 72     }
 73 
 74     body {
 75         font-size: 14px
 76     }
 77 }
 78 
 79 @media screen and (min-width: 480px) {
 80     html {
 81         font-size:32px
 82     }
 83 
 84     body {
 85         font-size: 15.36px
 86     }
 87 }
 88 
 89 @media screen and (min-width: 540px) {
 90     html {
 91         font-size:36px
 92     }
 93 
 94     body {
 95         font-size: 17.28px
 96     }
 97 }
 98 
 99 @media screen and (min-width: 720px) {
100     html {
101         font-size:48px
102     }
103 
104     body {
105         font-size: 23.04px
106     }
107 }
108 
109 @media screen and (min-width: 750px) {
110     html {
111         font-size:50px
112     }
113 
114     body {
115         font-size: 24px
116     }
117 }
View Code

3.7號稱移動端適配終極解決方案hotcss的應用:

下載地址:https://github.com/imochen/hotcss

使用的基本介紹(如果有scss使用經驗及scss編輯器外掛配置經驗的可以跳過):

//工具與外掛安裝及使用
1.將下載的壓縮包解壓
2.從解壓的src資料夾中將hotcss.js和px2rem.scss拿出來備用(如果使用的是less就將px2rem.scss換成px2rem.less)
3.給編輯器安裝scss自動編譯外掛:https://www.cnblogs.com/ZheOneAndOnly/p/13763219.html

//我的工作區間設計(node服務環境我就不寫了,如果有需要參看前面的示例)
page
    css
        hotCss.scss //專案的scss檔案(自己些的樣式程式碼,通過編輯器的自動編譯外掛,會在css資料夾自動生成一個同名的css檔案)
        px2rem.scss//hotcss工具檔案(前面準備的)
    html
        hotCss.html //專案的結構檔案(自己寫的程式碼)
    image             //圖片資料夾
    js
        hotcss.js      //hotcss工具檔案(前面準備的)

hotcss的示例程式碼:(這裡我就不把全部的測試程式碼粘貼出來了,多了反而看著懵,能說明hotcss怎麼用並能體現出它的原理就可以了)

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Document</title>
 6     <link rel="stylesheet" href="/css/hotCss.css">
 7     <script src="/js/hotcss.js"></script>
 8 </head>
 9 <body>
10     <div></div>
11     <div></div>
12     <div></div>
13     <div></div>
14 </body>
15 </html>
測試的hotCss.html檔案
 1 @import "px2rem.scss";
 2 $designWidth: 750;
 3 
 4 body{
 5     margin: 0;
 6 }
 7 
 8 body div{
 9     float: left;
10     width: px2rem(187.5);
11     height: px2rem(100);
12 }
13 
14 body div:nth-child(1){
15     background-color: red;
16 }
17 
18 body div:nth-child(2){
19     background-color: yellow;
20 }
21 
22 body div:nth-child(3){
23     background-color: green;
24 }
25 
26 body div:nth-child(4){
27     background-color: blueviolet;
28 }
測試的hotCss.scss檔案

hotcss的原理剖析(適配的核心程式碼在hotcss.js第83~97行mresize方法):

這個適配的核心,就是將視口的css畫素設定為裝置的物理畫素,並將裝置的寬度分為20份,即320/20=16,每份16的基礎畫素。

然後,通過裝置的實際視口寬度,放大或縮小這個寬度,即innerWidth*20/320,最後配置給根元素的字型畫素,然後再元素上使用rem來表達元素寬度。

rem由px2rem按照元素的設計實際寬度,對應的設計總寬度1/20的比例。(這個難度在於各種資料的關係,實際上應用的是小學的數學知識就可以搞定)

//然後在實際開發時,hotcss.js還需要修改一個引數,避免出現一些異常
//第14行
maxWidth = 540,
//預設視口最大寬度為540,這是適配iphone5的引數,
//這個引數根據設計圖對應的裝置畫素,也就是設計圖的畫素寬度

//然後在在你的scss中最上部新增這段程式碼
@import "px2rem.scss";   //引入px2rem.scss
$designWidth: 540;        //配置設計寬度,即也是對應裝置的物理畫素

3.8最新的標準適配方案:vw適配

vw是相對於視口寬度的1/100的寬度單位,對應的vh也就是相對於視口高度的1/100的高度單位。並且會隨著螢幕的豎屏與橫屏自動切換縮放頁面。

也就是vw是將螢幕寬度切成100,通過前面hotcss直接就能想到通過scss的函式和變數語法,可以直接將設計畫素轉換成vw畫素,請看示例:

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport"
 6           content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8     <title>Document</title>
 9     <link rel="stylesheet" href="/css/index.css">
10 </head>
11 <body>
12     <div>a</div>
13     <div>b</div>
14     <div>c</div>
15     <div>d</div>
16 </body>
17 </html>
測試用的html程式碼
 1 $designWidth:750;/*配置設計畫素 */
 2 @function px100vw($px){ /* 將設計畫素轉換成每單位vw的rem */
 3   @return $px*100/$designWidth +vw;
 4 }
 5 
 6 *{
 7   margin: 0;
 8   padding: 0;
 9 }
10 div{
11   float: left;
12   width: px100vw(187.5);
13   height: px100vw(187.5);
14 }
15 div:nth-child(1){
16   background-color: red;
17 }
18 div:nth-child(2){
19   background-color: yellow;
20 }
21 div:nth-child(3){
22   background-color: green;
23 }
24 div:nth-child(4){
25   background-color: blue;
26 }
使用scss配合vw畫素的scss程式碼

再來看看對應的rem適配程式碼:

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport"
 6           content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8     <title>Document</title>
 9     <link rel="stylesheet" href="/css/index.css">
10     <script>
11         (function(win, doc){
12             let tid = null;
13             let mreSize = function() {
14                 let html = doc.documentElement;
15                 html.style.fontSize = (html.clientWidth / 100) + 'px';
16                 console.log(html.clientWidth);
17             }
18             mreSize();
19             //當視口發生變化時調整根節點字型大小,這裡需要做一下防抖處理
20             win.addEventListener('resize', function () {
21                 clearTimeout(tid);
22                 tid = setTimeout(mreSize,33);
23             },false);
24             //當頁面載入完成以後呼叫一次,防止一些bug
25             win.addEventListener('load',mreSize,false);
26             //防止怪異現象再呼叫一次
27             tid = setTimeout(mreSize,333);
28         })(window,document);
29 
30     </script>
31 </head>
32 <body>
33     <div>a</div>
34     <div>b</div>
35     <div>c</div>
36     <div>d</div>
37 </body>
38 </html>
對應測試的rem適配結構程式碼
 1 $designWidth:750;/*配置設計畫素 */
 2 @function px100rem($px){ /* 將設計畫素轉換成每單位vw的rem */
 3   @return $px*100/$designWidth +rem;
 4 }
 5 
 6 *{
 7   margin: 0;
 8   padding: 0;
 9 }
10 div{
11   float: left;
12   width: px100rem(187.5);
13   height: px100rem(187.5);
14 }
15 div:nth-child(1){
16   background-color: red;
17 }
18 div:nth-child(2){
19   background-color: yellow;
20 }
21 div:nth-child(3){
22   background-color: green;
23 }
24 div:nth-child(4){
25   background-color: blue;
26 }
對應測試的rem適配scss程式碼

上面兩個示例的思路都是保持css元素畫素與設計畫素一致,然後藉助css的擴充套件語言scss或者less的變數和函式來轉換成vw或者rem畫素,這讓程式設計時更方便,不再需要計算css的元素畫素,但不可避免的需要藉助一些工具和外掛實現,甚至rem還需要藉助js來實現,那可不可以不借助工具和外掛同時也儘量保持設計畫素與css的元素畫素一致呢?

當然還是可行的,這時候就需要vw和rem配合來解決,這種是一種可以不借助css的擴充套件語言和js就可以實現的方案:

首先,回顧一下viewport的適配原理,不記得可回到前面再看一下,然後再來看下面的推導公式。

 1 設計畫素 * ? = css畫素
 2 ?= css畫素/設計畫素
 3 即:
 4 設計畫素放大多少被等於css畫素,以iphone6為例,設計稿畫素寬度=750,css畫素=375
 5  375 / 750 = 0.5
 6 計算得到設計畫素放大0.5倍 = css畫素 
 7 每一個設計畫素的0.5被 = 每一個css畫素
 8 即畫素比
 9 
10 
11 1vw = css畫素 / 100
12 1vw = 設計畫素 * 畫素比 / 100
13 設計畫素 = 1vw /(畫素比 / 100)
14 
15 假設使用vw表示根節點字型畫素
16 即:一個設計畫素實際佔比多少個根節點字型畫素?
17 畫素比/1vw實際表示的css畫素
18 即:iphone6為例
19 fontsezi = 0.5 / 3.75 vw
20 
21 iphone6 p為例
22 fontsize = 414/1080 / 4.14 vw
23 
24 由於這個數值會非常小,並且在使用rem時區分rem值與px值,可以放大畫素比100倍,然後rem值縮小一百倍,即可的一下公式:
25 fontsize = css畫素寬度 / 設計畫素寬度 * 100 / css畫素 * 100 vw
26 元素實際畫素 = 設計畫素 / 100 rem

如果對推導過程不是很明白也不要緊,直接拿最後兩行的公式去用就可以了,下面這裡是一個簡單的示例:

 1 <!doctype html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <meta name="viewport"
 6           content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
 7     <meta http-equiv="X-UA-Compatible" content="ie=edge">
 8     <title>Document</title>
 9     <link rel="stylesheet" href="/css/index.css">
10 </head>
11 <body>
12     <div></div>
13     <div></div>
14     <div></div>
15     <div></div>
16 </body>
17 </html>
iphone6的設計標準測試html程式碼
 1 *{
 2     margin: 0;
 3     padding: 0;
 4 }
 5 html{
 6     font-size: 13.33333333333333333vw; /* css畫素寬度 / 設計畫素寬度 * 100 / css畫素 * 100 vw */
 7 }
 8 
 9 div{
10     float: left;
11     width: 1.875rem;
12     height: 1.875rem;
13 }
14 
15 div:nth-child(1){
16     background-color: red;
17 }
18 div:nth-child(2){
19     background-color: yellow;
20 }
21 div:nth-child(3){
22     background-color: green;
23 }
24 div:nth-child(4){
25     background-color: blue;
26 }
iphone的設計標準測試css程式碼

使用vw+rem適配就完全避開了js的調整,規避了JS操作DOM的效能損耗,使用vw系統自帶的螢幕自適應功能。