博客 / 詳情

返回

Web移動端最強適配方案總結,沒想到這麼好用!

一、前言

在過去的幾年時間裏,移動端web野蠻生長,智能機的Android陣營和IOS陣營分庭抗禮,隨之產生了多個系統版本(系統版本多樣);五花八門的屏幕尺寸、屏幕展示技術(如大名鼎鼎的Retina技術屏)層出不窮(屏幕尺寸、技術多樣),還是CSS的W3C標準在各式各樣的移動端瀏覽器上落實得也是七零八落(瀏覽器兼容多樣)。

細看下來移動端Web開發工作面臨着很多的多樣性,可想而知在這樣的不確定性下去開發一個完善的項目會有多大的阻力,因此,移動端Web亟需一個完善成熟的適配方案來磨平這些多樣性之間的差異和不足,提供一個相對穩定、可控的開發環境。

本文只介紹CSS樣式佈局的適配方案,至於HTML5和JavaScript的適配方案,其實現在已經有了一些成熟的解決方案,如Babel,各種polyfill等,並且搭配Webpack使用更香。

二、Flexible方案

Flexible方案主要是藉助JavaScript控制viewport的能力,使用rem模擬vw的特性從而達到適配目的的一套解決方案。

Flexible方案的實現涉及並使用到了很多PC端開發很少接觸到的概念,其實無論是怎麼樣的適配方案都是建立在梳理和管理這些概念之上的,因此,這些概念對我們理解和探究移動端適配的深層原理尤為重要(具體概念講述請見《深入淺出移動端適配》)。

2.1 Flexible的核心思想

2.1.1 使用rem模擬vw特性適配多種屏幕尺寸

rem是相對於html元素的font-size來做計算的計算屬性值。
通過設置documentElementfontSize屬性值就可以統一整個頁面的佈局標準。

// set 1rem = viewWidth / 10
function setRemUnit () {
    var rem = docEl.clientWidth / 10
    // docEl為document.documentElement,即html元素
    docEl.style.fontSize = rem + 'px'
}
setRemUnit();

如上代碼所示,Flexible將整個頁面的寬度切成了10份,然後將計算出來的頁面寬度的1/10設置為html節點的fontSize,也就意味着,之後我們在當前頁面的html節點的子節點上應用rem為單位時都是按照頁面比例來計算的。

2.1.2 控制viewport的width和scale值適配高倍屏顯示

設置viewportwidthdevice-width,改變瀏覽器viewport(佈局視口和視覺視口)的默認寬度為理想視口寬度,從而使得用户可以在理想視口內看到完整的佈局視口的內容。

等比設置viewportinitial-scalemaximum-scaleminimum-scale的值,從而實現1物理像素=1css像素,以適配高倍屏的顯示效果(就是在這個地方規避了大家熟知的“1px問題”)

var metaEL= doc.querySelector('meta[name="viewport"]');
var dpr = window.devicePixelRatio;
var scale = 1 / dpr
metaEl.setAttribute('content', 'width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no'); 

2.2 Flexible配合的周邊工具

2.2.1 PostCSS-px2rem

Flexible使用了rem作為統一頁面佈局標準的佈局單位,且把頁面寬度等分為了10份,那麼我們在書寫css代碼時就需要去計算當前的px單位在當前設計稿上對應的rem值應該是多少。
以iPhone6為例:佈局視口為375px,則1rem = 37.5px,這時設計稿上給定一個元素的寬為75px(設備獨立像素),我們只需要將它設置為75 / 37.5 = 2rem即可。

當然,以上的工作方式顯然是低效且不可接受的,我們可以藉助PostCSS的pxtorem插件來幫我們完成這個計算過程:

plugins: {
    ...,
    'postcss-pxtorem': {
        // 750設計標準
        rootValue: 75,
        // 轉換成的rem後,保留小數點後幾位
        unitPrecision: 5,
        /**
        * 將會被轉換的css屬性列表,
        * 設置為*表示全部,['*','*position*','!letter-spacing','!font*']
        * *position* 表示所有包含 position 的屬性
        * !letter-spacing 表示非 letter-spacing 屬性
        * !font* 表示非font-size font-weight ... 等的屬性
        * */
        propList: ['*', '!letter-spacing'],
        // 不會被轉換的class選擇器名,支持正則
        selectorBlackList: ['.rem-'],
        replace: true,
        // 允許在媒體查詢中轉換`px`
        mediaQuery: false,
        // 小於1px的將不會被轉換
        minPixelValue: 1
    }
}

以上代碼是基於Vue Cli3.x的Webpack項目,只需要配置在當前項目根目錄的postcss.config.js中即可,除了Webpack配置之外,還可以使用其他的配置方式,詳細介紹可以點擊這裏進行了解。

postcss-pxtorem可以幫我們把我們需要轉的px值計算轉換為對應的rem值,如:

.name-item {
    font-size: 40px;
  line-height: 56px;
  margin-left: 144px;
  border-top: 1PX solid #eeeeee;
  color: #333333;
}

轉換後是這個樣子:

.name-item {
    font-size: .53333rem;
  line-height: .74667rem;
  font-weight: 700;
  margin-left: 1.92rem;
  border-top: 1px solid #eee;
  color: #333;
}

2.3 Flexible的缺陷

2.3.1 對iframe的使用不兼容。

iframe中展示的內容依然使用的是css像素,在高倍屏下會出問題,如我們在使用iframe引用一個騰訊視頻的視頻播放資源時,該視頻播放器的播放按鈕在不同dpr的設備上展示差異很大:

從圖中我們可以看出播放按鈕在dpr = 2的設備上展示的大小要比在dpr = 3的設備上要大很多,如果你去仔細測量的話,會發現剛好是其1.5倍,如果你讀過了深入淺出移動端適配,那麼很容易就理解為什麼了,我們這裏不做深究。

2.3.2 對高倍屏的安卓手機沒做處理

如果你去研究過lib-flexible的源碼,那你一定知道lib-flexible對安卓手機的特殊處理,即:一律按dpr = 1處理。

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;
}

那麼,Flexible為什麼不對安卓的高倍屏做適配處理呢?我想Flexible這樣做應該是有苦衷的:長久以來,安卓手機的dpr五花八門,從14甚至到5,更甚者1.752.63.5這樣的dpr值也層出不窮。所以Flexible在權衡之下直接簡單粗暴的把安卓手一律按dpr = 1處理,也算是快刀斬亂麻了。

當然,我們也可以手動去修改lib-flexible的源碼去彌補上這個缺憾,但我們也只可能針對那些dpr為整數的安卓設備做適配,對於那些比較奇葩的dpr直接忽略即可。然而,天知道安卓手機的dpr最大整數值是多少呢?天知道(三星S8的dpr就是4

2.3.3 不兼容響應式佈局

響應式佈局,其實質性做法就是結合css3的媒體查詢@media對一些不同尺寸閾值做特定的佈局設計,如對768px以下屏幕的使用緊湊型佈局,對769px992px的屏幕做圖文混排型佈局,對大於992px的屏幕做富元素、多元素佈局等。

.main-content {
    max-width: 70em
}
@media screen and (min-width: 0) {
    .main-content {
        margin:0 6.4935064935%
    }
}
@media screen and (min-width: 45em) {
    .main-content {
        margin:0 5.1282051282%
    }
}
@media screen and (min-width: 70em) {
    .main-content {
        margin:0 5.1282051282%
    }
}

其中,@media語法中涉及到的尺寸查詢語句,查詢的尺寸依據是當前設備的物理像素,和Flexible的佈局理論(即針對不同dpr設備等比縮放視口的scale值,從而同時改變佈局視口和視覺視口大小)相悖,因此響應式佈局在“等比縮放視口大小”的情境下是無法正常工作的。

2.3.4 無法正確響應系統字體大小

根據Flexible的實現理論,我們都知道它是通過設置的html元素的font-size大小,從而確保頁面內所有元素在使用rem為單位進行樣式設置時都是相對於html元素的font-size值。

然而,在微信環境(或其他可設置字體大小的Web瀏覽器中,如Safari)下,設置微信的字體大小(調大)後再打開使用Flexible技術適配的Web頁面,你會發現頁面佈局錯亂了,所有使用rem設置大小的元素都變大了,此時htmlfont-size還是原來的大小,但是元素就是變大了,這是為什麼呢?

事實上,雖然Flexible幫我們使用<meta/>標籤設置了width=device-widthuser-scalable=no以及對應的scale縮放值以保證我們的元素大小在高倍屏下(dpr >= 2 )正常展示,但是在調整Web瀏覽器的字體大小後,我們的"視口"也響應的等比縮小了,即視覺視口(window.innerWidth),豁然開朗,並不是我們的元素變大了,而是我們的視覺視口變小了!

基於我們已經掌握的視口相關知識,其根本原因是我們在調整Web瀏覽器的字體大小時,也響應的調整了視口的scale值,因此才導致了視覺視口的變小。

知道了Bug產生的原因,那我們有辦法解決嗎?答案是在Flexible方案下毫無辦法,而在接下來要講到的Viewport方案中則可以完美解決。Flexible承載的歷史使命已經完成了,讓我們放下Flexible,擁抱新的變化。

三、Viewport方案

Viewport方案中主要使用的是css3中CSS Values and Units Module Level 3(候選推薦)新增的<length>單位vwvhvmaxvmin。定義中,它們都是相對單位,其相對的參考系都是"視覺視口":

unit relative to(參考單位)
'vw' 1% of viewport's width(視覺視口寬度的1%)
'vh' 1% of viewport's height(視覺視口高度的1%)
'vmax' 1% of viewport's larger dimension(vw和vh中的較大值)
'vmin' 1% of viewport's smaller dimension(vw和vh中的較大值)

image

vminvmax是根據Viewport中長度偏大的那個維度值計算出來的,如果window.innerHeight > window.innerWidthvmin取值為window.innerWidth / 100vmax取值為window.innerHeight / 100

可能會有同學擔心Viewport方案的瀏覽器兼容性問題,我們可以使用caniuse來查看下viewport單位在各主流瀏覽器版本上的兼容情況:

image

image

從圖中可以看出,目前大部分的主流瀏覽器基本上已經支持了viewport單位,其中有一些淡綠色的瀏覽器版本表示為部分支持,其主要內容為無法兼容vmaxvmin的用法;而“Know issues”一欄中所列的一些已知問題大多也是針對用户縮放viewport大小或者IOS 7 Safari所特有的一些buggy behavior,而對於這些我們是可以控制的。

事實上,我們的適配方案,與其稱為“viewport適配方案”不如叫“vw適配方案”,因為在我們的適配方案中,我們只需要使用到vw這一個相對單位即可,並且其兼容性是最好的,其他單位基本上使用不到。

對於那些只存在IOS 7 Safari及老版本才會出現的一些問題,大可不必多慮,畢竟現在已經9102年了,而IOS 7是“2013年9月18日正式推出,2013年9月19日凌晨1點開放免費下載更新”的,年代久遠,加之iPhone的不更新系統就給你來個限速變卡的騷操作,這種遠古系統再出現的概率幾乎為0。

3.1 Viewport方案的核心思想

3.1.1 使用vw作為元素的佈局單位

vw作為佈局單位,從底層根本上解決了不同尺寸屏幕的適配問題,因為每個屏幕的百分比是固定的、可預測、可控制的。

從我們的實際開發工作出發,我們現在都是統一使用的iPhone6的視覺設計稿(即寬度為750px),那麼100vw=750px,即1vw = 7.5px。那麼如果設計稿上某一元素的寬度為value像素,那麼其對應的vw值則可以通過vw = value / 7.5來計算得到。

需要注意的是,雖然vw無痛解決了我們之前遇到的很多問題,但是它並不是萬能的,通過查找資料、博客和測試實踐,以下場景我們可以放心使用vw來適配我們的頁面:

• 容器適配,可以使用vw
• 文本適配,可以使用vw
• 大於1px的邊框、圓角、陰影都可以使用vw
• 內邊距和外邊距都可以使用vw

3.1.2 降級處理不兼容

在我們已知的大部分主流瀏覽器中,都是天然支持vw單位的,但不排除有某些瀏覽器的某些版本存在不兼容的情況,如果業務需要,我們可以通過如下兩種方式做降級處理:

• CSS Houdini:通過CSS Houdini針對vw做處理,調用CSS Typed DOM Level1提供的CSSUnitValue API;
• CSS Polifill:通過相應的Polyfill做響應的處理,目前針對vw單位的Polyfill主要有:vminpoly、Viewport Units Buggyfill、vunits.js和Modernizr。大漠老師比較推薦的是Viewport Units Buggyfill

3.2 Viewport方案配合的周邊工具

3.2.1 postcss-px-to-viewport

postcss-px-to-viewport插件的作用和postcss-pxtorem的作用類似,主要用來把px單位轉換為vwvhvmin或者vmax這樣的視窗單位(推薦轉換為vw,其他單位多多少少都有一些兼容性問題),也是viewport適配方案的核心插件之一。

結合webpack項目進行配置時,只需要將其配置在項目根目錄下的postcss.config.js中即可,其基本配置項如下:

plugins: {
'postcss-px-to-viewport': {
    unitToConvert: 'px',   // 需要轉換的單位
    viewportWidth: 750,    // 視口寬度,等同於設計稿寬度
    unitPrecision: 5,      // 精確到小數點後幾位
    /**
    * 將會被轉換的css屬性列表,
    * 設置為 * 表示全部,如:['*']
    * 在屬性的前面或後面設置*,如:['*position*'],*position* 表示所有包含 position 的屬性,如 background-position-y
    * 設置為 !xx 表示不匹配xx的那些屬性,如:['!letter-spacing'] 表示除了letter-spacing 屬性之外的其他屬性
    * 還可以同時使用 ! 和 * ,如['!font*'] 表示除了font-size、 font-weight ...這些之外屬性之外的其他屬性名頭部是‘font’的屬性
    * */
    propList: ['*'],
    viewportUnit: 'vw',    // 需要轉換成為的單位
    fontViewportUnit: 'vw',// 需要轉換稱為的字體單位
    /**
    * 需要忽略的選擇器,即這些選擇器對應的屬性值不做單位轉換
    * 設置為字符串,轉換器在做轉換時會忽略那些選擇器中包含該字符串的選擇器,如:['body']會匹配到 .body-class,也就意味着.body-class對應的樣式設置不會被轉換
    * 設置為正則表達式,在做轉換前會先校驗選擇器是否匹配該正則,如果匹配,則不進行轉換,如[/^body$/]會匹配到 body 但是不會匹配到 .body
    */
    selectorBlackList: [],
    minPixelValue: 1,      // 最小的像素單位值
    mediaQuery: false,     // 是否轉換媒體查詢中設置的屬性值
    replace: true,                 // 替換包含vw的規則,而不是添加回退
    /**
    * 忽略一些文件,如'node_modules'
    * 設置為正則表達式,將會忽略匹配該正則的所有文件
    * 如果設置為數組,那麼該數組內的元素都必須是正則表達式
    */
    exclude: [],
    landscape: false,      // 是否自動加入 @media (orientation: landscape),其中的屬性值是通過橫屏寬度來轉換的
    landscapeUnit: 'vw',   // 橫屏單位
    landscapeWidth: 1334   // 橫屏寬度
}

目前出視覺設計稿,我們都是使用750px寬度的,那麼100vw = 750px,即1vw = 7.5px。那麼我們可以根據設計圖上的px值直接轉換成對應的vw值。在實際擼碼過程,不需要進行任何的計算,直接在代碼中寫px即可,postcss-px-to-viewport會自動幫我們把px計算轉換為對應的vw值,比如:

.name-item {
    font-size: 40px;
  line-height: 56px;
  margin-left: 144px;
  border-top: 1PX solid #eeeeee;
  color: #333333;
}

轉換後:

.name-item {
    font-size: 5.33333vw;
  line-height: 7.46667vw;
  margin-left: 19.2vw;
  border-top: 1px solid #eee;
  color: #333;
}  

當然,postcss-px-to-viewport的功能不止於此,它還可以在selectorBlackList選項中設置一些關鍵詞或正則,來避免對這些指定的選擇器做轉換,如selectorBlackList:['.ignore', '.hairlines']

<div class="box ignore"></div>
寫CSS的時候: 
.ignore {
    margin: 10px;
    background-color: red;
}
.box {
    width: 180px;
    height: 300px;
}
.hairlines {
    border-bottom: 0.5px solid red;
}

轉化之後:

.box {
    width: 24vw;
    height: 40vw;
}
.ignore {
    margin: 10px; /*.box元素中帶有.ignore類名,在這個類名寫的`px`不會被轉換*/
    background-color: red;
}
.hairlines {
    border-bottom: 0.5px solid red;
}
3.2.2 Viewport Units Buggyfill

這個js庫是為了兼容那些不兼容vwvhvmaxvmin這些viewport單位的瀏覽器所使用的,在該方案開始我們已經明確過,現如今大部分機型的大部分瀏覽器都已經兼容了viewport單位,大漠老師在17年左右對Top30的熱門機型進行了測試,其中只有如下幾款機型沒有完全支持viewport單位:

image

但是如果你的業務不允許,需要你的項目跑在很多更古老的機型或者瀏覽器版本上,那麼就不得不考慮到一些hack手段,那麼這個js庫就是你的首選方案了。

3.2.2.1 使用方法
1\. 引入JavaScript文件

viewport-units-buggyfill主要有兩個JavaScript文件:viewport-units-buggyfill.jsviewport-units-buggyfill.hacks.js。你只需要在你的HTML文件中引入這兩個文件。比如在Vue項目中的index.html引入它們:

<script src="//g.alicdn.com/fdilab/lib3rd/viewport-units-buggyfill/0.6.2/??viewport-units-buggyfill.hacks.min.js,viewport-units-buggyfill.min.js"></script>
2\. 在HTML文件中調用viewport-units-buggyfill

在html文件中引入polyfill的位置之後,需要手動調用下 viewport-units-buggyfill:

<script>
  window.onload = function () {
    window.viewportUnitsBuggyfill.init({
      hacks: window.viewportUnitsBuggyfillHacks
    });
}
</script>  
3\. 結合使用postcss-viewport-units

具體的使用。在你的CSS中,只要使用到了viewport的單位地方,需要在樣式中添加content

.my-viewport-units-using-thingie {
  width: 50vmin;
  height: 50vmax;
  top: calc(50vh - 100px);
  left: calc(50vw - 100px);
  /* hack to engage viewport-units-buggyfill */
  content: 'viewport-units-buggyfill; width: 50vmin; height: 50vmax; top: calc(50vh - 100px); left: calc(50vw - 100px);';
}  

這可能會令你感到噁心,而且我們不可能每次寫vw都去人肉的計算。特別是在我們的這個場景中,我們使用了postcss-px-to-viewport這個插件來轉換vw,更無法讓我們人肉的去添加content內容。

這個時候就需要前面提到的postcss-viewport-units插件。這個插件將讓你無需關注content的內容,插件會自動幫你處理。比如插件處理後的代碼:

.test {
    padding: 3.2vw;
    margin: 3.2vw auto;
    background-color: #eee;
    text-align: center;
    font-size: 3.73333vw;
    color: #333;
    content: "viewport-units-buggyfill; padding: 3.2vw; margin: 3.2vw auto; font-size: 3.73333vw";
}  

配置這個插件也很簡單,只需要和配置postcss-px-to-viewport一樣,配置在項目根目錄的postcss.config.js中即可:

plugins: {
  'postcss-viewport-units': {}
} 
3.2.2.2 副作用

在我們使用了Viewport Units Buggyfill後,正如你看到的,它會在佔用content屬性,因此會或多或少的造成一些副作用。如img元素和偽元素的使用::before::after

對於img,在部分瀏覽器中,content的寫入會造成圖片無法正常展示,這時候需要全局添加樣式覆蓋:

img {
    content: normal !important;
} 

對於::before等偽元素,就算是在裏面使用了vw單位,Viewport Units Buggyfill對其並不會起作用,如:

// 編譯前
.after {
    content: 'after content';
    display: block;
    width: 100px;
    height: 20px;
    background: green;
}
// 編譯後
.after[data-v-469af010] {
    content: "after content";
    display: block;
    width: 13.333vw;
    height: 2.667vw;
    background: green;
} 

3.3 Viewport方案的缺陷

採用vw來做適配在處理一些細節之處還是存在一定的缺陷的。 比如當容器使用vwmargin採用px時,很容易造成整體寬度超過100vw,從而影響佈局效果。當然我們也是可以避免的,例如使用padding代替margin,結合calc()`函數使用等等...

另外一點,px轉換成vw不一定能完全整除,因此有一定的像素差。

3.3.1 高倍屏適配

通讀整套適配方案,你會發現viewport適配方案單單是使用了vw去適配不同尺寸屏幕的大小問題,而並沒有解決高倍屏展示的問題,如老生常談的1px問題、圖片展示模糊等問題。

3.3.1.1 1px問題

其實網上關於1px這些關於解決高倍屏展示問題的方案有很多,如大漠老師的再談Retina下1px的解決方案,周陸軍的Retina真實還原1px邊框的解決方案,方法總比問題多。

結合上面一些方案,我這裏也整理了幾套被各位大佬所推薦的解決方案並測試了下效果:

• 結合postcss-write-svg和border-imagebackground-image解決1px問題

border-image方案雖然很好用,但是在一些低端機型和ios設備上有兼容問題。主要表現為在一些低端安卓機型,如魅藍note1中展示4個邊框時,下側和右側邊框缺失;在iPhone5siPhone6siPhone6s Plus上直接不顯示(不知道是不是我姿勢不對)。

image

border-image還有一個問題就是無法做圓角。

background-image方案,在以上機型上都能比較好的展現,但是在背景圖方案中需要提供2像素的圖片,如:

image

fineLine(color = #e8e8e8, position = bottom)
  if position == top || position == bottom
    background-repeat: repeat-x
        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAAXNSR…hZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY5g5c+Z/BhAAABRcAsvqBShzAAAAAElFTkSuQmCC)
    if position == top
      background-position: 0 0
    else
      background-position: 0 100%
  else
    background-repeat: repeat-y
        background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAAXNSR…hZcwAADsMAAA7DAcdvqGQAAAAQSURBVBhXY5g5c+Z/BhAAABRcAsvqBShzAAAAAElFTkSuQmCC)
    if position == left
      background-position: 0 0
    else
      background-position: 100% 0

當然,我們也可以藉助postcss-write-svg的能力,自己編寫一個可以繪出上圖中兩種類型的base64圖片出來:

// 畫出來的圖片如圖一(上下)
@svg squareLR {
    width: 1px;
    height: 2px;
    @rect {
        fill: var(--color, black);
        width: 100%;
        height: 50%;
    }
}
// 畫出來的圖片如圖二(左右)
@svg squareTB {
    width: 2px;
    height: 1px;
    @rect {
        fill: var(--color, black);
        width: 50%;
        height: 100%;
    }
}
// 順便還可以優化下我們的mixin寫法  
fineLine(color = #e8e8e8, position = bottom)
  if position == top || position == bottom
    background-repeat: repeat-x
        background-image: svg(squareLR param(--color color))
    if position == top
      background-position: 0 0
    else
      background-position: 0 100%
  else
    background-repeat: repeat-y
        background-image: svg(squareTB param(--color color))
    if position == left
      background-position: 0 0
    else
      background-position: 100% 0

除此之外,我們還有漸變背景圖片方案。在漸變背景圖片方案中,我們只需要維護一份mixin代碼就可以實現我們想要的效果:

bgLine($color = #efefef, $direction = all)
  background-repeat: no-repeat
  if $direction == all
    border: none
    padding: 1px
    background-image:
      -webkit-linear-gradient(top, transparent 50%, $color 50%),
      -webkit-linear-gradient(right, transparent 50%, $color 50%),
      -webkit-linear-gradient(bottom, transparent 50%, $color 50%),
      -webkit-linear-gradient(left, transparent 50%, $color 50%)
    background-image:
      linear-gradient(to top, transparent 50%, $color 50%),
      linear-gradient(to right, transparent 50%, $color 50%),
      linear-gradient(to bottom, transparent 50%, $color 50%),
      linear-gradient(to left,transparent 50%, $color 50%)
    background-size:
      100% 1px,
      1px 100%,
      100% 1px,
      1px 100%
    background-position:
      top center,
      right center,
      bottom center,
      left center
  else
    background-position: $direction center
    background-image: -webkit-linear-gradient($direction, transparent 50%, $color 50%);
    background-image: linear-gradient(to $direction, transparent 50%, $color 50%);
    if $direction == left || $direction == right
      background-size: 1px 100%
    if $direction == top || $direction == bottom
      background-size: 100% 1px
.test
    width 400px
  padding 24px
  margin 24px
  bgLine(red, all)  

image

但是漸變色背景圖方案依然有她的不足,如無法設置邊框圓角、需要維護比較繁瑣的漸變色控制代碼(雖然一萬年可能就動一次)等問題,不過依然是值得一試的適配方案。

0.5px方案

0.5px方案在IOS8之後有很好的支持,我所能蒐羅到的iPhone設備都很清晰的顯示了我們想要到的細線(但是對於iPhone 6s PlusiPhone XiPhone Xs等3倍屏的IOS設備其實並不是真實的1物理像素,而是1.5物理像素,不過影響不大)。

但是在安卓設備上缺喜憂參半,經過我的測試,在Android5.1之後的版本,各設備基本上已經兼容了0.5px的正常顯示,但是不排除有一些低於Android5.1版本的設備不能正常展示,那麼就以為這要用js代碼去做一定的hack,並要涉及到Flexible適配方案去做兼容,這簡直就是技術的倒退,不能忍的。

所以,在各種場景的綜合權衡下,並不推薦在viewport適配方案的項目中使用該策略去做1px問題的兼容。

• 偽元素 + transform scale方案

偽元素 + transform scale的方法相比以上幾種方案是比較簡潔、可控好理解的方式,並且這種方式也支持設置圓角。在騰訊、京東的大部分移動端產品中大都採用的這種適配方案(阿里的移動端產品,如手機版淘寶、手機版天貓等並未對1px做適配處理,amazing!it's understandable~ 比較任性吧)。

其方案的思路也很好理解,大家一看便知:

border-1px($color = #ccc, $radius = 2PX, $direction = all)
  position: relative
  &::after
    content: ""
    pointer-events: none
    display: block
    position: absolute
    border-radius: $radius
    box-sizing border-box
    width 100%
    height 100%
    left: 0
    top: 0
    transform-origin: 0 0
    if $direction == all
      border: 1PX solid $color
    else
      border-{$direction}: 1PX solid $color
    @media only screen and (-webkit-min-device-pixel-ratio:2)
      width: 200%
      height: 200%
      border-radius: $radius * 2
      transform: scale(.5)
    @media only screen and (-webkit-min-device-pixel-ratio:3)
      width: 300%
      height: 300%
      border-radius: $radius * 3
      transform: scale(.333)
3.3.1.2 圖片模糊問題

在高倍屏下產生圖片模糊的問題以及其對應的解決方案,在深入淺出移動端適配已經向大家解釋和介紹過了,此處略過。

完結,撒花。

四、臨了寄語

現在牆內所能查到的移動端適配方案比較零散,而且有一些信息也是不合時宜的、甚至已經過時失真。深入淺出移動端適配和本篇文章的目的就是對那些網上能蒐羅到的移動端適配方案做一個總結和實踐驗證,也算是一個學習的過程。 這兩篇文章中的一些觀點和想法或者代碼實現可能多多少少會有一些不足之處,歡迎大家批評指正。

作者:小小Mac
鏈接:https://juejin.im/post/689404...
來源:掘金

最後,照舊安利一波我們的公眾號:「終端研發部」,目前每天都會推薦一篇優質的技術相關的文章,主要分享java相關的技術與面試技巧,我們的目標是: 知道是什麼,為什麼,打好基礎,做好每一點!這個主創技術公眾號超級值得大家關注。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.