Web移動端適配方案

1、前言

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

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

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

2、Flexible方案

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

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

2.1 Flexible的核心思想

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

rem是相對於html元素的font-size來作計算的計算屬性值。
經過設置documentElementfontSize屬性值就能夠統一整個頁面的佈局標準。css3

// 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爲單位時都是按照頁面比例來計算的。web

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

設置viewportwidthdevice-width,改變瀏覽器viewport(佈局視口和視覺視口)的默認寬度爲理想視口寬度,從而使得用戶能夠在理想視口內看到完整的佈局視口的內容。正則表達式

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

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,擁抱新的變化。

3、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中的較大值)

圖片

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

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

圖片
圖片

從圖中能夠看出,目前大部分的主流瀏覽器基本上已經支持了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單位:

圖片

可是若是你的業務不容許,須要你的項目跑在不少更古老的機型或者瀏覽器版本上,那麼就不得不考慮到一些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上直接不顯示(不知道是否是我姿式不對)。

border-image還有一個問題就是沒法作圓角。

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

圖片

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)  
複製代碼

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

• 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 圖片模糊問題

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

完結,撒花。

4、臨了寄語

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

5、參考

• www.w3cplus.com/mobile/vw-l…• www.w3cplus.com/css/fix-1px…• www.w3cplus.com/css/vw-for-…• www.w3cplus.com/mobile/lib-…• blog.51cto.com/zhoulujun/2…• juejin.im/post/5cddf2…• www.cnblogs.com/sunLemon/p/…• segmentfault.com/q/101000001…• blog.noob6.com/2018/06/03/…

相關文章
相關標籤/搜索