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