移动端适配 - 小结
在前端的世界里,移動終端和 PC 的響應式適配是我們常見的適配,在適配的過程中有些都可以起作用,有些有不同,經(jīng)過不斷地實踐總結(jié),我想總結(jié)一下。
1. 視口 viewport
1.1 viewport 基礎
viewport 解釋為中文就是‘視口’的意思,也就是瀏覽器中用于顯示網(wǎng)頁的區(qū)域。在 PC 端,其大小也就是瀏覽器可視區(qū)域的大小,所以我們也不會太關(guān)注此概念;而在移動端,絕大多數(shù)情況下 viewport 都大于瀏覽器可視區(qū),保證 PC 頁面在移動瀏覽器上面的可視性。為提升可視性體驗,針對移動端有了對 viewport 的深入研究。
1.2 viewport 詳解
在移動端有三種類型的 viewport: layoutviewport、visualviewport、idealviewport。具體解釋如下:
- layoutviewport: 大于實際屏幕, 元素的寬度繼承于 layoutviewport,用于保證網(wǎng)站的外觀特性與桌面瀏覽器一樣。layoutviewport 到底多寬,每個瀏覽器不同。iPhone 的 safari 為 980px,通過 document.document.clientWidth 獲取。
- visualviewport: 當前顯示在屏幕上的頁面,即瀏覽器可視區(qū)域的寬度。
- idealviewport: 為瀏覽器定義的可完美適配移動端的理想 viewport,固定不變,可以認為是設備視口寬度。比如 iphone 7 為 375px, iphone 7p 為 414px。
1.3 viewport 設置
我們通過對幾種 viewport 設置可以對網(wǎng)頁的展示進行有效的控制,在移動端我們經(jīng)常會在 head 標簽中看到這段代碼:
<meta name='viewport' content='width=device-width,initial-scale=1,user-scale=no' />通過對 meta 標簽三個 viewport 的設置,最終使頁面完美展示。下面詳細的闡釋其具體含義:
- width 設置的是 layoutviewport 的寬度
- initial-scale 設置頁面的初始縮放值,并且這個初始縮放值是相對于 idealviewport 縮放的,最終得到的結(jié)果不僅會決定 visualviewport,還會影響到 layoutviewport
- user-scalable 是否允許用戶進行縮放的設置
對上面的說明通過公式推導進行進一步的解釋:
// 設定兩個變量: viewport_1 = width; viewport_2 = idealviewport / initial-scale;// 則: layoutviewport = max{viewport_1, viewport_2}; visualviewport = viewport_2;結(jié)合上面對不同 viewport 分析補充如下兩個結(jié)論:
1.4 viewport 舉例
以下是通過改變 meta viewport 的幾個參數(shù)的值來算取不同的 viewport:
| - | - | 980px | 980px | 375px | 否 |
| device-width | 1 | 375px | 375px | 375px | 否 |
| device-width | 2 | 375px | 188px | 375px | 是 |
| device-width | 0.5 | 750px | 750px | 375px | 否 |
| 480px | 1 | 480px | 375px | 375px | 是 |
| 480px | 2 | 480px | 188px | 375px | 是 |
| 480px | 0.5 | 750px | 750px | 375px | 否 |
以上是針對 iphone 6/7/8 的測試數(shù)據(jù),且無論怎么設置 viewport 都具有臨界值,即:75 <= layoutviewport <= 10000,75 <= visualviewport <= 1500。
1.5 為什么要設置 viewport
viewport 的設置不會對 PC 頁面產(chǎn)生影響,但對于移動頁面卻很重要。下面我們舉例來說明:
2. 設備像素比 dpr 與 1px 物理像素
2.1 物理像素(physical pixel)
手機屏幕上顯示的最小單元,該最小單元具有顏色及亮度的屬性可供設置,iphone6、7、8 為:750 * 1334,iphone6+、7+、8+ 為 1242 * 2208
2.2 設備獨立像素(density-indenpendent pixel)
此為邏輯像素,計算機設備中的一個點,css 中設置的像素指的就是該像素。老早在沒有 retina 屏之前,設備獨立像素與物理像素是相等的。
2.3 設備像素比(device pixel ratio)
設備像素比(dpr) = 物理像素/設備獨立像素。如 iphone 6、7、8 的 dpr 為 2,那么一個設備獨立像素便為 4 個物理像素,因此在 css 上設置的 1px 在其屏幕上占據(jù)的是 2個物理像素,0.5px 對應的才是其所能展示的最小單位。這就是 1px 在 retina 屏上變粗的原因,目前有很多辦法來解決這一問題。
?
?
?
2.4 1px的物理像素的解決方案
從第一部分的討論可知 viewport 的 initial-scale 具有縮放頁面的效果。對于 dpr=2 的屏幕,1px壓縮一半便可與1px的設備像素比匹配,這就可以通過將縮放比 initial-scale 設置為 0.5=1/2 而實現(xiàn)。以此類推 dpr=3的屏幕可以將 initial-scale設置為 0.33=1/3 來實現(xiàn)。
3. 設備像素比 dpr 與 rem 的適配方案
結(jié)合 2、3 部分可以實現(xiàn) 1px 的物理像素這一最小屏幕單位,那在此基礎上如可讓設計通常提供的 750px 設計稿來完美的適配到多種機型上,使用 rem 是一種解決方式。
3.1 rem 如何設置
rem 是相對于根元素 html 的 font-size 來做計算。通常在頁面初始化時加載時通過對document.documentElement.style.fontSize 設置來實現(xiàn)。
3.2 rem 適配規(guī)則
通過對 initial-scale = 1/dpr 的設置,已將對屏幕的描述從物理像素轉(zhuǎn)化到了物理像素上了,這將是后續(xù)推導的基礎,且設計稿為 750px。
因此可推導出 rem 的設定方式:
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';下面我們將 750px 下,1rem 代表的像素值用 baseFont 表示,則在 baseFont = 75 的情況下,是分成 10 等份的。因此可以將上面的公式通用話一些:
document.documentElement.style.fontSize = document.documentElement.clientWidth / ( 750 / 75 ) + 'px';整體設置可參考如下代碼:
(function (baseFontSize) {const _baseFontSize = baseFontSize || 75;const ua = navigator.userAgent;const matches = ua.match(/Android[\S\s]+AppleWebkit\/(\d{3})/i);const isIos = navigator.appVersion.match(/(iphone|ipad|ipod)/gi);const dpr = window.devicePixelRatio || 1;if (!isIos && !(matches && matches[1] > 534)) {// 如果非iOS, 非Android4.3以上, dpr設為1;dpr = 1;}const scale = 1 / dpr;const metaEl = document.querySelector('meta[name="viewport"]');if (!metaEl) {metaEl = document.createElement('meta');metaEl.setAttribute('name', 'viewport');window.document.head.appendChild(metaEl);}metaEl.setAttribute('content', 'width=device-width,user-scalable=no,initial-scale=' + scale + ',maximum-scale=' + scale + ',minimum-scale=' + scale);document.documentElement.style.fontSize = document.documentElement.clientWidth / (750 / _baseFontSize) + 'px'; })();同時為了書寫方便可以直接通過 px 布局,然后在打包時利用 pxtorem 庫轉(zhuǎn)化為基于 rem 的布局。
4. 視口單位適配方案
將視口寬度 window.innerWidth 和視口高度 window.innerHeight 等分為 100 份,且將這里的視口理解成 idealviewport 更為貼切,并不會隨著 viewport 的不同設置而改變。
- vw : 1vw 為視口寬度的 1%
- vh : 1vh 為視口高度的 1%
- vmin : vw 和 vh 中的較小值
- vmax : 選取 vw 和 vh 中的較大值
如果設計稿為 750px,那么 1vw = 7.5px,100vw = 750px。其實設計稿按照設么都沒多大關(guān)系,最終轉(zhuǎn)化過來的都是相對單位,上面講的 rem 也是對它的模擬。這里的比例關(guān)系也推薦不要自己換算,使用 pxtoviewport 的庫就可以幫我們轉(zhuǎn)換。當然每種方案都會有其弊端,這里就不展開討論。
5. iphoneX的適配
倍圖其實就是像素尺寸和開發(fā)尺寸的倍率關(guān)系,但這只是外在的表現(xiàn)。倍圖核心的影響因素在于PPI(DPI),了解屏幕密度與各尺寸的關(guān)系有助于我們深度理解倍率的概念:《基礎知識學起來!為設計師量身打造的DPI指南》
iPhone8在本次升級中,屏幕尺寸和分辨率都遺傳了iPhone6以后的優(yōu)良傳統(tǒng);
然而iPhone X 無論是在屏幕尺寸、分辨率、甚至是形狀上都發(fā)生了較大的改變,下面以iPhone 8作為參照物,看看到底iPhone X的適配我們要怎么考慮。
我們看看iPhone X尺寸上的變化:
5.1. ?iPhoneX的適配---安全區(qū)域(safe area)
蘋果對于 iPhone X 的設計布局意見如下:
?
核心內(nèi)容應該處于 Safe area 確保不會被設備圓角(corners),傳感器外殼(sensor housing,齊劉海) 以及底部的 Home Indicator 遮擋。也就是說 我們設計顯示的內(nèi)容應該盡可能的在安全區(qū)域內(nèi);
5.3. ?iPhoneX的適配---適配方案viewport-fit
? ? 5.3.1 ?PhoneX的適配,在iOS 11中采用了viewport-fit的meta標簽作為適配方案;viewport-fit的默認值是auto。
? viewport-fit取值如下:
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? auto | 默認:viewprot-fit:contain;頁面內(nèi)容顯示在safe area內(nèi) |
| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cover | viewport-fit:cover,頁面內(nèi)容充滿屏幕 |
? ? viewport-fit meta標簽設置(cover時)
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> ?? ? 5.3.2 ?css constant()函數(shù) 與safe-area-inset-top &?safe-area-inset-left &?safe-area-inset-right &?safe-area-inset-bottom的介紹
?
?
?
? ?如上圖所示 在iOS 11中的WebKit包含了一個新的CSS函數(shù)constant(),以及一組四個預定義的常量:safe-area-inset-left,?safe-area-inset-right,?safe-area-inset-top和?safe-area-inset-bottom。當合并一起使用時,允許樣式引用每個方面的安全區(qū)域的大小。
? ? 5.3.1當我們設置viewport-fit:contain,也就是默認的時候時;設置safe-area-inset-left,?safe-area-inset-right,?safe-area-inset-top和?safe-area-inset-bottom等參數(shù)時不起作用的。
? ? 5.3.2當我們設置viewport-fit:cover時:設置如下
body {padding-top: constant(safe-area-inset-top); //為導航欄+狀態(tài)欄的高度 88px padding-left: constant(safe-area-inset-left); //如果未豎屏時為0 padding-right: constant(safe-area-inset-right); //如果未豎屏時為0 padding-bottom: constant(safe-area-inset-bottom);//為底下圓弧的高度 34px }?
?5.4. ? iPhoneX的適配---高度統(tǒng)計
? ? viewport-fit:cover + 導航欄
?
5.5.iPhoneX的適配---媒體查詢
注意這里采用的是690px(safe area高度),不是812px;
@media only?screen?and (width:?375px) and (height:?690px){body {background:?blue;}}5.6.iphoneX viewport-fit ?問題總結(jié)
1.關(guān)于iphoneX 頁面使用了漸變色時;如果viewport-fit:cover;
????1.1在設置了背景色單色和漸變色的區(qū)別,如果是單色時會填充整個屏幕,如果設置了漸變色 那么只會更加子元素的高度去渲染;而且頁面的高度只有690px高度,上面使用了padding-top:88px;
body固定為:
<body><div?class="content">this is subElement</div></body>1.單色時:
* {padding: 0;margin: 0; } body {background:green;padding-top: constant(safe-area-inset-top); //88px /*padding-left: constant(safe-area-inset-left);*/ /*padding-right: constant(safe-area-inset-right);*/ /*padding-bottom: constant(safe-area-inset-bottom);*/ }?
2.漸變色
* {padding: 0;margin: 0;}body {background:-webkit-gradient(linear, 0 0, 0 bottom, from(#ffd54f), to(#ffaa22));padding-top: constant(safe-area-inset-top); //88px/*padding-left: constant(safe-area-inset-left);*//*padding-right: constant(safe-area-inset-right);*//*padding-bottom: constant(safe-area-inset-bottom);*/}?
解決使用漸變色 仍舊填充整個屏幕的方法;CSS設置如下
<!DOCTYPE html> <html> <head><meta name="viewport" content="initial-scale=1, viewport-fit=cover"><title>Designing Websites for iPhone X: Respecting the safe areas</title><style> * {padding: 0;margin: 0;}html, body {height: 100%;}body {padding-top: constant(safe-area-inset-top);padding-left: constant(safe-area-inset-left);padding-right: constant(safe-area-inset-right);padding-bottom: constant(safe-area-inset-bottom);}.content {background: -webkit-gradient(linear, 0 0, 0 bottom, from(#ffd54f), to(#ffaa22));width: 100%;height: 724px;} </style> </head> <body> <div class="content">this is subElement</div> </body> </html>?
2.頁面元素使用了固定定位的適配即:{position:fixed;}
????????2.1 子元素頁面固定在底部時;使用viewport-fit:contain時;可以看到bottom:0時只會顯示在安全區(qū)域內(nèi);
<!DOCTYPE html> <html> <head><meta name="viewport" content="initial-scale=1"><!--<meta name="viewport" content="initial-scale=1, viewport-fit=cover">--><title>Designing Websites for iPhone X: Respecting the safe areas</title><style>* {padding: 0;margin: 0;}/*html,body {*//*height: 100%;*//*}*/body {background: grey;/*padding-top: constant(safe-area-inset-top);*//*padding-left: constant(safe-area-inset-left);*//*padding-right: constant(safe-area-inset-right);*//*padding-bottom: constant(safe-area-inset-bottom);*/}.top {width: 100%;height: 44px;background: purple;}.bottom {position: fixed;bottom: 0;left: 0;right: 0;height: 44px;color: black;background: green;}</style> </head> <body><div class="top">this is top</div><div class="bottom">this is bottom</div> </body> </html>?
2.1 子元素頁面固定在底部時;使用viewport-fit:cover時;可以看到bottom:0時只會顯示在安全區(qū)域內(nèi);
?
* {padding: 0;margin: 0;}/*html,body {*//*height: 100%;*//*}*/body {background: grey;padding-top: constant(safe-area-inset-top);/*padding-left: constant(safe-area-inset-left);*//*padding-right: constant(safe-area-inset-right);*//*padding-bottom: constant(safe-area-inset-bottom);*/}.top {width: 100%;height: 44px;background: purple;}.bottom {position: fixed;bottom: 0;left: 0;right: 0;height: 44px;color: black;background: green;}添加html,body ? {width:100%;heigth:100%}
?
圖1:
* {padding: 0;margin: 0;}html,body {height: 100%;}body {background: grey;padding-top: constant(safe-area-inset-top);padding-left: constant(safe-area-inset-left);padding-right: constant(safe-area-inset-right);padding-bottom: constant(safe-area-inset-bottom);}.top {width: 100%;height: 44px;background: purple;}.bottom {position: fixed;bottom: 0;left: 0;right: 0;height: 44px;color: black;background: green;}圖2:
* {padding: 0;margin: 0;}html,body {height: 100%;}body {background: grey;padding-top: constant(safe-area-inset-top);padding-left: constant(safe-area-inset-left);padding-right: constant(safe-area-inset-right);/*padding-bottom: constant(safe-area-inset-bottom);*/}.top {width: 100%;height: 44px;background: purple;}.bottom {position: fixed;bottom: 0;left: 0;right: 0;height: 44px;color: black;background: green;}?
?
2.3 關(guān)于alertView彈框 遮罩層的解決方案
?
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><!--<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">--><meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"><title>alertView</title><script data-res="eebbk">document.documentElement.style.fontSize = window.screen.width / 7.5 + 'px';</script><style>* {margin: 0;padding: 0;}html,body {width: 100%;height: 100%;}body {font-size: 0.32rem;padding-top: constant(safe-area-inset-top);padding-left: constant(safe-area-inset-left);padding-right: constant(safe-area-inset-right);padding-bottom: constant(safe-area-inset-bottom);}.content {text-align: center;}.testBut {margin: 50px auto;width: 100px;height: 44px;border: 1px solid darkgray;outline:none;user-select: none;background-color: yellow;}</style><link href="alertView.css" rel="stylesheet" type="text/css"> </head> <body><section class="content"><button class="testBut" onclick="showLoading()">彈框加載</button></section><script type="text/javascript" src="alertView.js"></script><script>function showLoading() {UIAlertView.show({type:"input",title:"溫馨提示", //標題content:"VIP會員即將到期", //獲取新的isKnow:false});var xx = new UIAlertView();console.log(xx);}</script> </body> </html>?
參考資料:
iPhone X的Web設計
? ?iPhone X適配沒那么復雜,但也不是看上去這么簡單
總結(jié)
在移動端開發(fā)中,理解視口對適配至關(guān)重要。因此本文先從視口展開討論,從而引出 1px、rem 及 vw/vh 這些和適配相關(guān)的主要話題。下面提供的參考文章會在某些點上更加細化,以供參考。
總結(jié)
以上是生活随笔為你收集整理的移动端适配 - 小结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Markdown懒办法排版微信公众号文章
- 下一篇: Wavefront公司的.obj文件格式