曾幾何時,前端還僅僅是PC端的。隨着移動時代的興起,h5及css3的推陳出新。前端的領域慢慢的由傳統的pc端轉入了移動端,這也導致了前端這一職業在風口的一段時間出盡了風頭。
從開始的惶恐和無從下手,慢慢的到了有了統一的技術方案去落地實現。
從手寫不同尺寸的媒體查詢css到以手淘的flexible.js來進行移動端的適配,雖然過程曲折,不過效果也是十分的顯著,因為有了成熟的體系以后。什么東西就有據可尋,適配也就沒那么困難了。
但是,因為這次引入了高德地圖,所以在適配上出現了一點意料之外的問題。
首先,我要說下視口這個東西,因為手淘的這個方案是嚴重依賴視口這個概念的。
1.視口
1.1視口的分類
ppk將視口分為三大類:布局視口,可視視口,理想視口
那視口是什么呢?通俗點說就是就是瀏覽器上(也可能是一個app中的webview)用來顯示網頁的那部分區域,但viewport又不局限於瀏覽器可視區域的大小,它可能比瀏覽器的可視區域要大,因為為了正常的顯示PC端的網頁,瀏覽器會將自己的layout viewport設置為一個較大的值,結果就是會出現左右的滾動條。當然viewport(visual viewport)也可能比瀏覽器的可視區域要小,比如有的手機自帶的瀏覽器會有一個自帶的黑色小邊距。

布局視口和可視視口我們作基本了解即可。在實際工作中,我們需要接觸和處理的更多是ideal viewport。
而我們前端一直孜孜以求的移動端的適配。其實就是為了讓用戶的瀏覽器中呈現的是我們的理想視口
ideal viewport並沒有一個固定的尺寸,不同的設備擁有有不同的ideal viewport。
早期移動端開發,對於終端設備適配問題只屬於Android系列,有320pt的,有360pt的,有384pt的等等。但隨着iPhone6,iPhone6+的出現,從此終端適配問題不再是Android系列了,也從這個時候讓移動端適配全面進入到“雜屏”時代。有320pt的,有375pt(iphone x)的,有414pt(plus)的等等。
http://viewportsizes.com里面收集了眾多設備的理想寬度。
1.2 如何影響視口?
既然viewport這么重要,那我們怎么去控制他為我所用呢?這個時候,就輪到meta標簽出場了。
先來一段我們在開發的時候最常用的一句話。
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
用下面的表格來解釋一下content里面的屬性
| width | 設置layout viewport 的寬度,為一個正整數,或字符串"width-device" |
| height | 設置layout viewport 的高度,這個屬性對我們並不重要,幾乎不使用 |
| initial-scale | 設置頁面的初始縮放值,相對於ideal viewport進行縮放,為一個數字,可以帶小數 |
| minimum-scale | 允許用戶的最小縮放值,為一個數字,可以帶小數 |
| maximum-scale | 允許用戶的最大縮放值,為一個數字,可以帶小數 |
| user-scalable | 是否允許用戶進行縮放,值為"no"或"yes", no 代表不允許,yes代表允許 |
<meta name="viewport" content="width=device-width">
width=device-width,通過這個特殊值。我們將layout viewport => ideal viewport
<meta name="viewport" content="initial-scale=1.0">
通過設置初始的縮放比率,我們也可以將layout viewport => ideal viewport
所以上面兩種方式是殊途同歸的。那么,為什么我們還要將兩個都寫上去呢?
答案:initial-scale=1.0是為了處理在 iphone、ipad上,無論是豎屏還是橫屏,寬度都是豎屏時ideal viewport的寬度,
width=device-width是為了處理在windows phone 上的IE 無論是豎屏還是橫屏都把寬度設為豎屏時ideal viewport的寬度
<meta name="viewport" content="width=400, initial-scale=1.0">
如果出現了上面的這種怎么辦呢?
這個時候瀏覽器會取它們兩個中較大的那個值。例如,當width=400,ideal viewport的寬度為320時,取的是400;當width=400, ideal viewport的寬度為480時,取的是ideal viewport的寬度。
總結起來就是“誰大誰先行“
2.引入高德后頁面的表現
在vue的spa項目中引入高德以后,我們發現在不同的dpr下,地圖的顯示效果差距非常大。

在dpr=3的時候,也就是plus的機型上,地圖顯得格外的小。幾乎用肉眼是無法看清上面的字體。所以,這樣說來,基於flexible的適配方法肯定是有問題的了。
而出現這個問題的原因就是我們的viewport被縮放了。
if (!dpr && !scale) { var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { // iOS下,對於2和3的屏,用2倍的方案,其余的用1倍方案 if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其他設備下,仍舊使用1倍的方案 dpr = 1; } scale = 1 / dpr; }
通過上面的代碼計算出了viewport縮放的比率。當處於iphone 6+plus的時候,scale = 0.333333333333....
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
最后寫到頁面上面的結果就是:
<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
所以,iphone plus是414pt,通過flexible將viewport縮小了0.33333333333,我們將414/0.3333333333=1242.0000001242
而正好高德地圖通過canvas繪制的畫布的寬度也就是1242。
3.如何解決這個問題
通過我的總結,處理這個問題的方法大致有三種
3.1 通過vue-router的路由守衛進行處理
beforeMount() { this.$nextTick(() = >{ const dpr = document.documentElement.getAttribute('data-dpr')
if (dpr > 1) { window.tempViewport = document.querySelector('meta[name="viewport"]').getAttribute('content');
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no');
window.tempDpr = dpr;
document.documentElement.setAttribute('data-dpr', 1); } }) }, beforeRouteLeave(to, from, next) { if (window.tempViewport) { document.querySelector('meta[name="viewport"]').setAttribute('content', window.tempViewport);
delete window.tempViewport; } if (window.tempDpr) { document.documentElement.setAttribute('data-dpr', window.tempDpr);
delete window.tempDpr; } next() },
不過這樣的方式不是很好,因為頁面在過渡的時候會出現一瞬間樣式的變形。而且如果在當前有地圖的頁面有其他結構的話,其他結構也會錯亂。
Tips:如果不是SPA的應用,而且整屏頁面是地圖占滿的情況下,這個方案還是可行的。
3.2 通過css scale屬性
這個方法在我試驗了以后,也存在問題。雖然地圖的大小是正常了,但是在地圖上進行點標記的時候,會出現地圖位置的偏移。
Tips:如果僅僅是展示,而並沒有任何交互的情況下,這個方式也是可行的。
3.3 通過設置dpr = 1 (推薦)
通過設置dpr=1,強制flexible布局對viewport不進行縮放。
<meta name="flexible" content="initial-dpr=1" />
這樣,最后寫到頁面上的meta標簽就是這樣的。
<meta name="viewport" content="initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
既然viewport沒有縮放了。高德地圖通過canvas繪制的地圖也就是按照我們的ideal viewport來進行處理了。
不過這種方式會產生另外兩個副作用:
- 通過縮放來處理的"1px問題"這里需要重新去處理了
- 通過dpr設置的不同dpr下的文本字號大小,可能會出現13px這樣很奇葩的尺寸了
第一個是適配中一個很經典的問題,我會放到隨后去講。而第二個問題只能暫時這樣去處理了。
4.結尾
大漠對於這個問題的解釋是:flexible已經完成自己的使命,該功成身退了。他推薦使用vw,vh標准的新布局方式。
而到底用不用這套方案,作為前端的我們也是見仁見智了!
