從flexible.js引入高德地圖談起的移動端適配


曾幾何時,前端還僅僅是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標准的新布局方式。

而到底用不用這套方案,作為前端的我們也是見仁見智了!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM