整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          20、rem 適配方法如何計算 HTML 跟字號及適配方案?(必會)

          用方案

          1、設(shè)置根 font-size:625%(或其它自定的值,但換算規(guī)則 1rem 不能小于 12px)

          2、通過媒體查詢分別設(shè)置每個屏幕的根 font-size

          3、CSS 直接除以 2 再除以 100 即可換算為 rem

          優(yōu):有一定適用性,換算也較為簡單。

          劣:有兼容性的坑,對不同手機適配不是非常精準;需要設(shè)置多個媒體查詢來適應不同手機,單某款手機尺寸不在設(shè)置范圍之內(nèi),會導致無法適配。

          網(wǎng)易方案

          1、拿到設(shè)計稿除以 100,得到寬度 rem 值

          2、通過給 html 的 style 設(shè)置 font-size,把 1 里面得到的寬度 rem 值代入x document.documentElement.style.fontSize =document.documentElement.clientWidth / x + ‘px‘;

          3、設(shè)計稿 px/100 即可換算為 rem

          優(yōu):通過動態(tài)根 font-size 來做適配,基本無兼容性問題,適配較為精準,換算簡便。

          劣:無 viewport 縮放,且針對 iPhone 的 Retina 屏沒有做適配,導致對一些手機的適配不是很到位。

          手淘方案

          1、拿到設(shè)計稿除以 10,得到 font-size 基準值

          2、引入 flexible

          3、不要設(shè)置 meta 的 viewport 縮放值

          4、設(shè)計稿 px/ font-size 基準值,即可換算為 rem

          優(yōu):通過動態(tài)根 font-size、viewpor、dpr 來做適配,無兼容性問題,適配精準。

          劣:需要根據(jù)設(shè)計稿進行基準值換算,在不使用 sublime text 編輯器插件開發(fā)時,單位計算復雜。

          接:https://www.jianshu.com/p/3b45aa981e77

          下面是一些基礎(chǔ)概念的講解,幫助理解各種適配方案實現(xiàn)。

          像素:

          1、物理像素(設(shè)備像素)

          屏幕的物理像素,又被稱為設(shè)備像素,他是顯示設(shè)備中一個最微小的物理部件。任何設(shè)備屏幕的物理像素出廠時就確定了,且固定不變的。

          2、設(shè)備獨立像素

          設(shè)備獨立像素也稱為密度無關(guān)像素,可以認為是計算機坐標系統(tǒng)中的一個點,這個點代表一個可以由程序使用的虛擬像素(比如說CSS像素),然后由相關(guān)系統(tǒng)轉(zhuǎn)換為物理像素。

          3、設(shè)備像素比

          設(shè)備像素比簡稱為dpr,其定義了物理像素和設(shè)備獨立像素的對應關(guān)系

          設(shè)備像素比 = 物理像素 / 設(shè)備獨立像素
          以iphone6為例:
          iphone6的設(shè)備寬和高為375pt * 667pt,可以理解為設(shè)備的獨立像素,而其設(shè)備像素比為2.固有設(shè)備像素為750pt * 1334pt
          

          通過:window.devicePixelRatio獲得。

          設(shè)備像素比是區(qū)別是否是高清屏的標準,dpr大于1時就為高清屏,一般情況下dpr為整數(shù),但是android有些奇葩機型不為整數(shù)。

          4、css像素

          在CSS、JS中使用的一個長度單位。單位px

          注:在pc端1物理像素等于1px,但是移動端1物理像素不一定等于1px,1物理像素與px的關(guān)系與以下因素有關(guān)。(有些視口概念,可以把下面視口看完了再來看)

          1、屏幕布局視口大小(下面會講到)
          2、屏幕的分辨率(物理像素)
          

          對于一塊屏幕,其物理像素是確定的。視覺視口尺寸是繼承的布局視口的,而視覺視口里寬度即是css的px數(shù)。故在一塊屏上物理像素與px的關(guān)系就是物理像素與布局視口的px數(shù)的關(guān)系。

          比如iphone6,期物理像素為750,如果沒有設(shè)置布局視口時,viewport為980px
          此時:1物理像素長度等于980/750px = 1.3067px的長度
          由于像素都是點陣的,故1物理像素相當于1.3067px * 1.3067px方格。
          當在meta中設(shè)置了如下配置時
          <meta name="viewport" content="width=device-width">
          相當于把布局視口設(shè)置為設(shè)備的寬度(即上面講到的設(shè)備獨立像素), 對于iphone6就是375px。
          此時1物理像素長度等于375/750px = 0.5px的長度,故1物理像素相當于0.5px * 0.5px的方格。
          

          視口:

          1、布局視口:

          在html中一般在meta中的name為viewport字段就是控制的布局視口。布局視口一般都是瀏覽器廠商給的一個值。在手機互聯(lián)網(wǎng)沒有普及前,網(wǎng)絡上絕大部分頁面都是為電腦端瀏覽而做的,根本沒有做移動端的適配。隨著移動端的發(fā)展,在手機上看電腦端的頁面已成為非常普及現(xiàn)象。而電腦端頁面寬度較大,移動端寬度有限,要想看到整個網(wǎng)頁,會有很長的滾動條,看起來非常麻煩。于是瀏覽器廠商為了讓用戶在小屏幕下網(wǎng)頁也能夠顯示地很好,所以把布局視口設(shè)置的很大,一般在768px ~ 1024px 之間,最常用的寬度就是 980。這樣用戶就能看到絕大部分內(nèi)容,并根據(jù)具體內(nèi)容選擇縮放。

          故布局視口是看不見的,瀏覽器廠商設(shè)置的一個固定值,如980px,并將980px的內(nèi)容縮放到手機屏內(nèi)。

          布局視口可以通過:

          document.documentElement.clientWidth(clientHeight) // 布局視口的尺寸。
          

          2、視覺視口:

          瀏覽器可視區(qū)域的大小,即用戶看到的網(wǎng)頁的區(qū)域。(其寬度繼承的布局視口寬度)

          window.innerWidth(innerHeight) // 視覺視口尺寸
          

          3、理想視口:

          布局視口雖然解決了移動端查看pc端網(wǎng)頁的問題,但是完全忽略了手機本身的尺寸。所以蘋果引入了理想視口,它對設(shè)備來說是最理想的布局視口,用戶不需要對頁面進行縮放就能完美的顯示整個頁面。最簡單的做法就是使布局視口寬度改成屏幕的寬度。

          可以通過window.screen.width獲取。

          <meta name="viewport" content="width=device-width">
          

          移動端到底怎么適配不同的屏幕呢?最簡單的方法是設(shè)置如下視口:

          <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
          

          當使用以上方案定義布局視口時,即布局視口等于理想視口(屏幕寬度),屏幕沒有滾動條,不存在高清屏下,字體較小的問題。但是在不同屏幕上,其視覺寬度是不同的,不能簡單的將所有的尺寸都設(shè)置為px,可能會出現(xiàn)滾動條。小尺寸的可以用px,大尺寸的只能用百分比和彈性布局。

          viewport縮放

          對于上面的設(shè)置,再不同的屏幕上,css像素對應的物理像素具數(shù)是不一致的。

          在普通屏幕下,dpr=1時,

          1個css像素長度對應1個物理像素長度,1個css像素對應1個物理像素。

          而在Retina屏幕下,如果dpr=2,

          1個css像素長度對應2個物理像素長度,1css像素對應4個物理像素。

          此時如果css中寫

          border: 1px solid red; // 此時1px 對應的寬度是2物理像素的寬度。
          

          而一般現(xiàn)在移動端設(shè)計稿都是基于iphone設(shè)計的,稿子一般為750px或640px,這正好是iphone6和iphone5的物理像素。在設(shè)計稿中,一般有些邊框效果,這時邊框的線寬為1px,對應的就是1物理像素。而對于iphone5和iphone6,當width=device-width時,css的1px顯示出來的是2個物理像素,所以看起來線就比較粗。怎么解決呢?1px邊框效果其實有很多hack方法,其中一種就是通過縮放viewport。

          initial-scale是將布局視口進行縮放,initial-scale是相對于理想視口的,即initial-scale=1與width=device-width是一樣的效果。initial-scale=0.5等效于width= 2倍的device-width,所以設(shè)置initial-scale和width都可以改變布局視口的大小。

          <meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no"> 
          

          對于iphone6當添加如上設(shè)置后,initial-scale=0.5,即將頁面縮小2倍后等于屏幕寬度。

          布局視口width:
          width / 2 = 375px; width = 750px;
          

          所以此時布局視口為750px,此時1px等于1物理像素。

          適配方案:

          上面講了一些基礎(chǔ)概念,下面講具體適配。

          對于ui設(shè)計師給的一張設(shè)計稿,怎么將其還原到頁面上?對于不同手機屏幕,其dpr不同,屏幕尺寸也不同,考慮到各種情況,有很多適配方案,所以不同的適配方案,實現(xiàn)方法不同,處理復雜度也不同,還原程度也不同。

          方案一

          固定高度,寬度自適應。

          這種方案是目前使用較多的方案,也是相對較簡單的實現(xiàn)方案:

          該方法使用了理想視口:

          <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
          

          垂直方向使用固定的值,水平方向使用彈性布局,元素采用定值、百分比、flex布局等。這種方案相對簡單,還原度也非常低。

          方案二:

          固定布局視口寬度,使用viewport進行縮放

          如:荔枝FM、網(wǎng)易應用

          荔枝的代碼:

          if(/Android (\d+\.\d+)/.test(navigator.userAgent)){
           var version = parseFloat(RegExp.$1);
           if(version>2.3){
           var phoneScale = parseInt(window.screen.width)/640;
           if(/MZ-M571C/.test(navigator.userAgent)){
           document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
           }else if(/M571C/.test(navigator.userAgent)&&/LizhiFM/.test(navigator.userAgent)){
           document.write('<meta name="viewport" content="width=640, minimum-scale = 0.5, maximum-scale= 0.5">');
           }else{
           document.write('<meta name="viewport" content="width=640, minimum-scale = '+ phoneScale +', maximum-scale = '+ phoneScale +', target-densitydpi=device-dpi">');
           }
           }else{
           document.write('<meta name="viewport" content="width=640, target-densitydpi=device-dpi">');
           }
          }else{
           document.write('<meta name="viewport" content="width=640, user-scalable=no, target-densitydpi=device-dpi">');
          }
           
          

          網(wǎng)易應用:

          var win = window,
           width = 640,
           iw = win.innerWidth || width,
           ow = win.outerHeight || iw,
           sw = win.screen.width || iw,
           saw = win.screen.availWidth || iw,
           ih = win.innerHeight || width,
           oh = win.outerHeight || ih,
           ish = win.screen.height || ih,
           sah = win.screen.availHeight || ih,
           w = Math.min(iw, ow, sw, saw, ih, oh, ish, sah),
           ratio = w / width,
           dpr = win.devicePixelRatio;
           if (ratio = Math.min(ratio, dpr), 1 > ratio) {
           var ctt = ",initial-scale=" + ratio + ",maximum-scale=" + ratio,
           metas = document.getElementsByTagName("meta");ctt += "";
           for (var i = 0, meta; i < metas.length; i++) meta = metas[i], "viewport" == meta.name && (meta.content += ctt)
           }
          

          固定布局視口,寬度設(shè)置固定的值,總寬度為640px,根據(jù)屏幕寬度動態(tài)生成viewport。(設(shè)計稿應該是640px的)

          <meta name="viewport" content="width=640, minimum-scale = 0.5625, maximum-scale = 0.5625, target-densitydpi=device-dpi">
          

          這種方式布局如荔枝FM的網(wǎng)頁寬度始終為640px。縮放比例scale為:

          var scale = window.screen.width / 640
          

          設(shè)計稿為640px時,正好可以1:1以px來寫樣式。但是1px所對應的物理像素就不一定是1了。

          (window.screen.width * dpr) / 640 // 1px對應的物理像素
          

          iphone5.png

          iphone6.png

          方案三:

          根據(jù)不同屏幕動態(tài)寫入font-size,以rem作為寬度單位,固定布局視口。

          如網(wǎng)易新聞:

          <meta name="viewport" content="width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
          

          以640px設(shè)計稿和750px的視覺稿,網(wǎng)易這樣處理的:

          var width = document.documentElement.clientWidth; // 屏幕的布局視口寬度
          var rem = width / 7.5; // 750px設(shè)計稿將布局視口分為7.5份
          var rem = width / 6.4; // 640px設(shè)計稿將布局視口分為6.4份
          

          這樣不管是750px設(shè)計稿還是640px設(shè)計稿,1rem 等于設(shè)計稿上的100px。故px轉(zhuǎn)換rem時:

          rem = px * 0.01;
          

          在750px設(shè)計稿上:

          75px 對應 0.75rem, 距離占設(shè)計稿的10%;
          在ipone6上:
          width = document.documentElement.clientWidth = 375px;
          rem = 375px / 7.5 = 50px;
          0.75rem = 37.5px; (37.5/375=10%;占屏幕10%)
           
          在ipone5上:
          width = document.documentElement.clientWidth = 320px;
          rem = 320px / 7.5 = 42.667px;
          0.75rem = 32px; (32/320=10%;占屏幕10%)
          

          故對于設(shè)計稿上任何一個尺寸換成rem后,在任何屏下對應的尺寸占屏幕寬度的百分比相同。故這種布局可以百分比還原設(shè)計圖。

          iphone5-2.png

          iphone6-2.png

          方案四:

          以rem作為寬度單位,動態(tài)寫入viewport和font-size進行縮放。

          根據(jù)設(shè)置的dpr設(shè)置font-size。如:

          document.documentElement.style.fontSize = 50 * dpr;
          // dpr 為設(shè)置的設(shè)備像素比。(注意不是設(shè)備自身的設(shè)備像素比,而是認為設(shè)置的dpr)
          

          這種情況下,dpr = 1時,1rem = 50px;

          dpr = 2時, 1rem = 100px;

          當設(shè)計以iphone6為標準,出750px的設(shè)計稿時,此時dpr=2,故1rem 等于100px,將圖上的尺寸轉(zhuǎn)換為rem非常方便,除以100就行。代碼如下:

          var scale = 1.0;
          var dpr = 1;
          var isAndroid = window.navigator.appVersion.match(/android/gi);
          var isIPhone = window.navigator.appVersion.match(/iphone/gi);
          var devicePixelRatio = window.devicePixelRatio;
          // 此處只簡單對ios做了伸縮處理,安卓沒有做伸縮處理,統(tǒng)一dpr = 1
          if ( isIPhone ) {
           scale /= devicePixelRatio;
           dpr *= devicePixelRatio;
          }
          var viewport = document.getElementById('viewport');
          var content = 'initial-scale=' + scale + ', maximum-scale=' + scale + ',minimum-scale=' + scale + ', width=device-width, user-scalable=no';
          viewport.setAttribute( 'content', content );
          document.documentElement.style.fontSize = 50 * dpr + 'px';
          document.documentElement.setAttribute('data-dpr', dpr);
          

          對于該方案,

          假設(shè)肉眼看到的寬度(視覺寬度):visualWidth,令dpr=1時,其1rem對應的寬度為50.
          dpr = 1 時, 1rem = 50px, initial-scale=1, 縮放為1。
          visualWidth = 50 * 1 = 50;
          dpr = 2 時, 1rem = 100px, initial-scale=0.5, 縮放為0.5。
          visualWidth = 100 * 0.5 = 50;
          dpr = 3 時, 1rem = 150px, initial-scale=0.3333, 縮放為0.3333。
          visualWidth = 150 * 0.3333 = 50;
          

          所以該方案,1rem在所有屏幕上對應的肉眼距離相同,故不同屏幕下,總的rem數(shù)不同,大屏下總的rem數(shù)大于小屏下,如iphone6下,總寬度為7.5rem,iphone5下,總寬度為6.4rem。故此方案不能百分比還原設(shè)計稿,故寫樣式時,對于大塊元素應該用百分比,flex等布局,不能直接用rem。

          關(guān)于這個方案的詳細教程請參考這篇文章傳送門

          iphone5-3.png

          iphone6-3.png

          方案五:

          根據(jù)不同屏幕動態(tài)寫入font-size和viewport,以rem作為寬度單位

          將屏幕分為固定的塊數(shù)10:

          var width = document.documentElement.clientWidth; // 屏幕的布局視口寬度
          var rem = width / 10; // 將布局視口分為10份
          

          這樣在任何屏幕下,總長度都為10rem。1rem對應的值也不固定,與屏幕的布局視口寬度有關(guān)。

          對于動態(tài)生成viewport,他們原理差不多,根據(jù)dpr來設(shè)置縮放。看看淘寶的:

          var devicePixelRatio = window.devicePixelRatio;
          var isIPhone = window.navigator.appVersion.match(/iphone/gi);
          var dpr,scale;
          if (isIPhone) {
           if (devicePixelRatio >=3) {
           dpr = 3;
           } else if (devicePixelRatio >=2) {
           dpr = 2;
           } else {
           dpr = 1;
           }
          } else {
           dpr = 1;
          }
          scale = 1 / dpr;
          

          淘寶只對iphone做了縮放處理,對于android所有dpr=1,scale=1即沒有縮放處理。

          此方案與方案三相似,只是做了viewport縮放,能百分比還原設(shè)計稿。

          iphone5-4.png

          iphone6-4.png

          適配中要解決的問題 :

          移動端適配最主要的是使在不同屏幕下不用縮放頁面就能正常顯示整個頁面。以上方案都完成了這一需求。其次有幾個需求:

          1、解決高清屏下1px的問題,其實有很多hack方法,這里只講了縮放視口。先將布局視口設(shè)置為高清屏的物理像素。這樣css中1px就是1個物理像素,這樣看到的線條才是真正的1px。但是此時視口寬度大于設(shè)備的寬度,就會出現(xiàn)滾動條。故對視口進行縮放,使視口寬度縮放到設(shè)備寬度。

          淘寶團隊在處理安卓端的縮放存在很多問題,所以dpr都做1處理,所以安卓端就沒有解決1px的問題。

          2、在大屏手機中一行看到的段落文字應該比小屏手機的多。

          由于淘寶和網(wǎng)易新聞rem都是百分比,故如果用rem一行顯示的文字個數(shù)應該是相同的。故對于段落文本不能用rem作為單位,應該用px處理,對于不同的dpr下設(shè)置不同的字體。

          .selector { 
           color: red; 
           font-size: 14px; 
          }
          [data-dpr="2"] .selector { 
           font-size: 28px; // 14 * 2
          } 
          [data-dpr="3"] .selector { 
           font-size: 42px; // 14 * 3
          }
          

          對于方案四,不管什么情況下,1rem對應的視覺上的寬度都是一樣的,而對應的大屏、小屏手機其視覺寬度當然不同,故字體設(shè)置為rem單位時,也能滿足大屏手機一行顯示的字體較多這個需求。

          五種方案對比:

          上面四種方案對設(shè)計稿還原程度是有差別的。

          除了方案一和方案四以外,其他方案都是百分比還原設(shè)計稿,大屏下元素的尺寸就大。

          方案一還原設(shè)計稿程度較低,這里不做說明。

          方案二做了百分比適配,部分1px適配,沒有字體適配。

          方案三做了百分比適配,沒有1px適配,有字體大小適配。

          方案四沒有做百分百適配,布局要用百分百和flex布局,做了1px的適配,并且對于段落文字直接可以用rem做單位,不需要做適配。

          方案五做了百分比適配,有1px適配,有字體大小適配。

          項目中遇到的問題:

          在我們項目中方案四和方案五都用過。

          方案五在使用中沒有遇到什么問題,就是剛開始沒有做字體適配都是用的rem,后面加入了字體適配,這種方案設(shè)計師相對輕松些,不用考慮在大小屏幕下的布局效果。

          方案四時沒有跟ui設(shè)計師溝通清楚,導致設(shè)計師在設(shè)計圖上一行排了很多交互元素,在小屏下放不下去,又不能簡單放百分比(元素里的文字放不下)。所以還是要做動態(tài)判斷大小屏,做出相應適配。這個方案可能設(shè)計師需要考慮的多些,盡量減少一行內(nèi)的交互元素,當一行交互元素多時要考慮小屏手機怎么適配。

          其實對于1px的適配在蘋果端很好,在android端各個廠商手機差別太大,適配有很多問題。這是為什么絕大多數(shù)方案里都放棄了android端1px適配。不過最近看到很多網(wǎng)站都用了densitydpi=device-dpi這個安卓的私有屬性來兼容部分安卓機型,這個屬性在新的webkit已經(jīng)被移除了,使用它主要為了兼容低版本的android系統(tǒng)。

          這里大漠老師針對flexible方案進行了改版,兼容了更多的android機型的1px效果。文章傳送門

          他給了個壓縮版的方案,我看了下源碼,把它寫了一遍,不知道有沒有問題,效果是一樣的。

          var dpr, scale, timer, rem;
          var style = document.createElement('style');
          dpr = window.devicePixelRatio || 1;
          scale = 1 / dpr;
          document.documentElement.setAttribute('data-dpr', dpr);
          var metaEl = document.createElement('meta');
          metaEl.setAttribute('name', 'viewport');
          metaEl.setAttribute('content', 'target-densitydpi=device-dpi, initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
          document.documentElement.firstElementChild.appendChild(metaEl);
          document.documentElement.firstElementChild.appendChild(style);
          if (980 === document.documentElement.clientWidth) {
           metaEl.setAttribute('content', 'target-densitydpi=device-dpi,width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1');
          }
          function refreshRem () {
           var c = '}';
           var width = document.documentElement.clientWidth;
           var isPhone = window.navigator.userAgent.match(/Android|BlackBerry|iPhone|iPad|iPod|Opera Mini|IEMobile/i);
           if (!isPhone && width > 1024) {
           width = 640;
           c = 'max-width:' + width + 'px;margin-right:auto!important;margin-left:auto!important;}';
           }
           window.rem = rem = width / 16;
           style.innerHTML = 'html{font-size:' + rem + 'px!important;}body{font-size:' + parseInt(12 * (width / 320)) + 'px;' + c;;
          }
          refreshRem();
          window.addEventListener('resize', function () {
           clearTimeout(timer);
           timer = setTimeout(refreshRem, 300);
          }, false);
          window.addEventListener('pageshow', function (e) {
           if (e.persisted) {
           clearTimeout(timer);
           timer = setTimeout(refreshRem, 300);
           }
          }, false);
          

          這些方案只是針對絕大部分機型,項目中可能有些特殊機型有特殊問題,需要特殊對待。比如在這篇文章中作者使用flexible在小米max和榮耀8中有問題,需要特殊hack。傳送門,我沒有這種手機,也沒有對此做驗證。

          對于上面的五種方案,方案五看似是適配最好的,但是當項目中引入第三方插件時可能要一一適配,比如:引入一個富文本,里面設(shè)置字體大小的一般都是px,你需要將其一一轉(zhuǎn)換成rem。而對于方案二,可以直接用px做單位來百分百還原設(shè)計稿,引入的插件時也不用適配。所以說,具體項目中用哪個方案,其實不光是前端的選擇,還要跟設(shè)計師討論下,滿足設(shè)計需求,選擇最適合項目的方案。

          移動端雖然整體來說大部分瀏覽器內(nèi)核都是 webkit,而且大部分都支持 css3 的所有語法。但是,由于手機屏幕尺寸不一樣,分辨率不一樣,或者你需要考慮橫豎屏的問題,或者考慮到各式各樣的移動端兼容性問題。這時候你也就不得不解決在不同手機上,不同情況下的展示效果,所以就需要一個開箱即用并且行之有效的移動端適配方案。

          一、基本知識點

          工欲善其事必先利其器,在具體介紹適配方案前,在本章我們會學習下適配相關(guān)的知識點,便于后續(xù)適配方案的直接上手接收。

          1.1、響應式設(shè)計 - 像素

          像素單位有設(shè)備像素、邏輯像素、CSS 像素 3 種。

          1.1.1、設(shè)備像素、設(shè)備分辨率

          設(shè)備像素(device pixels)也叫物理像素,指的是顯示器上的真實像素,每個像素的大小是屏幕固有的屬性,屏幕出廠以后就不會再改變。
          設(shè)備分辨率描述的就是這個顯示器的寬和高分別是多少個設(shè)備像素,例如常見的顯示器的分辨率為 1920 * 1080。
          設(shè)備像素和設(shè)備分辨率是由操作系統(tǒng)來管理的,瀏覽器不知道、也不必知道設(shè)備分辨率的大小,它主要根據(jù)邏輯分別率(下一小節(jié)介紹)來計算的。

          1.1.2、設(shè)備獨立像素、邏輯分辨率

          設(shè)備獨立像素(device independent pixels)是操作系統(tǒng)定義的一種像素單位,應用程序?qū)⒃O(shè)備獨立像素告訴操作系統(tǒng),操作系統(tǒng)再將設(shè)備獨立像素轉(zhuǎn)化為設(shè)備像素,從而控制屏幕上真正的物理像素點。
          為什么需要在應用程序與設(shè)備像素之間定義這么一種單位呢?為什么應用程序不應該直接使用設(shè)備像素?
          例如原先在 1280×720 設(shè)備分辨率的顯示屏中,顯示高度為 12 個設(shè)備像素的字體,現(xiàn)在放到設(shè)備分辨率為 2560 ×1440 的顯示屏中,如果要想得到原先的大小,則需要 24 個設(shè)備像素,如果應用程序直接使用設(shè)備像素,那么編寫應用程序則將變得非常困難,需要編寫應用程序邏輯:字體在一些屏幕上高度為 12 個設(shè)備像素,在另一些屏幕上卻需要 24 個設(shè)備像素。
          因此操作系統(tǒng)定義了一個單位:設(shè)備獨立像素。操作系統(tǒng)保證:用設(shè)備獨立像素定義的尺寸,不管屏幕的參數(shù)如何,都能以合適的大小顯示(這也是設(shè)備獨立像素名字的由來)。操作系統(tǒng)是如何做到的呢?對于那些像素密度高的屏幕,將多個設(shè)備像素劃分為一個邏輯像素。至于將多少設(shè)備像素劃分為一個邏輯像素,這由操作系統(tǒng)決定。
          對于上面的例子:“原本高度為 12 個設(shè)備像素的字體,現(xiàn)在高度為 24 個設(shè)備像素才能得到相同的大小”,操作系統(tǒng)會將一個邏輯像素定義為 2*2個 真實像素,從而設(shè)備獨立像素尺寸不需要改變,而且不管在新、舊設(shè)備上,顯示的尺寸大致相同。

          設(shè)備獨立像素與設(shè)備像素之間的比例是多少,顯示器廠商和操作系統(tǒng)廠商會通過調(diào)查研究來得出最利于觀看的比例。普遍規(guī)律是,屏幕的像素密度越高,就需要更多的設(shè)備像素來顯示一個設(shè)備獨立像素。

          ?

          邏輯分辨率用屏幕的 寬高 來表示(單位:設(shè)備獨立像素),我們通過操作系統(tǒng)的分辨率設(shè)置來改變設(shè)備獨立像素的大小。例如屏幕的設(shè)備分辨率是19201200(單位:設(shè)備像素),我們可以在當前的分辨率下設(shè)置邏輯分辨率是1280*800(單位:設(shè)備獨立像素)。那么橫、縱方向的設(shè)備像素數(shù)量恰好是設(shè)備獨立像素的1.5倍。這也意味著,設(shè)備獨立像素的邊長是設(shè)備像素邊長的1.5倍。?

          1.1.3、CSS 像素

          在 CSS 中使用的 px 都是指 css 像素,比如 width: 128px。css 像素的大小是很容易變化的,當我們縮放頁面的時候,元素的 css 像素數(shù)量不會改變,改變的只是每個 css 像素的大小。也就是說 width: 128px 的元素在縮放200% 以后,寬度依然是 128 個 css 像素,只不過每個 css 像素的寬度和高度變?yōu)樵瓉淼膬杀丁H绻驹貙挾葹?128 個設(shè)備獨立像素,那么縮放 200% 以后元素寬度為 256 個設(shè)備獨立像素。?

          (1)css 像素與設(shè)備獨立像素的關(guān)系

          • 縮放比例就是 css 像素邊長/設(shè)備獨立像素邊長;
          • 在縮放比例為 100% 的情況下,1 個 css 像素大小等于 1 個設(shè)備獨立像素;
          • 在縮放比例為 200% 的情況下,1 個 css 像素大小等于 (2 * 2) 個設(shè)備獨立像素;

          (2)css 像素與設(shè)備像素的關(guān)系

          window.devicePixelRatio 設(shè)備像素比,devicePixelRatio = (在相同長度的直線上)設(shè)備像素的數(shù)量 / CSS 像素的數(shù)量。這個比例也等價于 CSS 像素邊長/設(shè)備像素邊長。如 devicePixelRatio = 2,表示在相同長度的直線上,設(shè)備像素的數(shù)量是 CSS 像素數(shù)量的 2 倍,因此 CSS 像素的邊長是設(shè)備像素的 2 倍。縮放會導致 CSS 像素邊長的改變,從而導致 window.devicePixelRatio 的改變!


          1.2、響應式設(shè)計 - viewport

          viewport 表示瀏覽器的可視區(qū)域,也就是瀏覽器中用來顯示網(wǎng)頁的那部分區(qū)域。存在三種 viewport 分別為 layout viewport、visual viewport 以及 ideal viewport,我們接下來分別介紹三種。

          1.2.1、layout viewport

          layout viewport 為布局視口,即網(wǎng)頁布局的區(qū)域,它是 html 元素的父容器,只要不在 css 中修改 元素的寬度, 元素的寬度就會撐滿 layout viewport 的寬度。
          很多時候瀏覽器窗口沒有辦法顯示出 layout viewport 的全貌,但是它確實是已經(jīng)被加載出來了,這個時候滾動條就出現(xiàn)了,你需要通過滾動條來瀏覽 layout viewport 其他的部分。
          layout viewport 用 css 像素來衡量尺寸,在縮放、調(diào)整瀏覽器窗口的時候不會改變。縮放、調(diào)整瀏覽器窗口改變的只是 visual viewport。?

          在桌面瀏覽器中,縮放100% 的時候,Layout Viewport 寬度等于內(nèi)容窗口的寬度。(你幾乎不會在電腦上見過橫向滾動條,除非你調(diào)整縮放)
          但是在移動端,縮放為 100% 的時候,Layout Viewport 不一定等于內(nèi)容窗口的大小。當你用手機瀏覽瀏覽寬大的網(wǎng)頁(這些網(wǎng)頁沒有采用響應式設(shè)計)的時候,你只能一次瀏覽網(wǎng)頁的一個部分,然后通過手指滑動瀏覽其他部分。這就說明整個網(wǎng)頁(Layout Viewport)已經(jīng)加載出來了,只不過你要一部分一部分地看。

          ?

          1.2.2、visual viewport

          visual viewport 為視覺視口,就是顯示在屏幕上的網(wǎng)頁區(qū)域,它往往只顯示 layout viewport 的一部分。
          visual viewport 就像一臺攝像機,layout viewport 就像一張紙,攝像機對準紙的哪個部分,你就能看見哪個部分。你可以改變攝像機的拍攝區(qū)域大小(調(diào)整瀏覽器窗口大小),也可以調(diào)整攝像機的距離(調(diào)整縮放比例),這些方法都可以改變 visual viewport,但是 layout viewport 始終不變。

          1.2.3、ideal viewport

          ideal viewport 為理想視口,不同的設(shè)備有自己不同的 ideal viewport,ideal viewport 的寬度等于移動設(shè)備的屏幕寬度,所以其是最適合移動設(shè)備的 viewport。只要在 css 中把某一元素的寬度設(shè)為 ideal viewport 的寬度(單位用 px ),那么這個元素的寬度就是設(shè)備屏幕的寬度了,也就是寬度為100% 的效果。 ideal viewport 的意義在于,無論在何種分辨率的屏幕下,那些針對ideal viewport 而設(shè)計的網(wǎng)站,不需要用戶手動縮放,也不需要出現(xiàn)橫向滾動條,都可以完美的呈現(xiàn)給用戶。

          1.2.4、利用 meta 標簽對 viewport 進行控制

          移動設(shè)備默認的 viewport 是 layout viewport,也就是那個比屏幕要寬的 viewport,但在進行移動設(shè)備網(wǎng)站的開發(fā)時,我們需要的是 ideal viewport。那么怎么才能得到 ideal viewport 呢?
          我們在開發(fā) h5 頁面時,最經(jīng)常見的標簽如下所示

          <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
          

          該 meta 標簽的作用是讓當前 viewport 的寬度等于設(shè)備的寬度,同時不允許用戶手動縮放。如果你不這樣的設(shè)定的話,那就會使用那個比屏幕寬的默認 viewport(layout viewport),也就是說會出現(xiàn)橫向滾動條。
          相關(guān)的屬性意義如下所示

          width

          設(shè)置 layout viewport 的寬度,為一個正整數(shù),或字符串 "width-device"

          height

          設(shè)置頁面的初始縮放值,為一個數(shù)字,可以帶小數(shù)

          initial-scale

          允許用戶的最小縮放值,為一個數(shù)字,可以帶小數(shù)

          minimum-scale

          允許用戶的最大縮放值,為一個數(shù)字,可以帶小數(shù)

          maximum-scale

          設(shè)置 layout viewport 的高度,這個屬性對我們并不重要,很少使用

          user-scalable

          是否允許用戶進行縮放,值為"no"或"yes", no 代表不允許,yes 代表允許

          二、方案選擇

          在前端滾滾潮流的歷史發(fā)展中的不同時期分別出現(xiàn)了一些極具代表性的適配方案,以下分別進行簡單介紹。

          2.1、使用 css 的媒體查詢 @media

          基于 css 的媒體查詢屬性 @media 分別為不同屏幕尺寸的移動設(shè)備編寫不同尺寸的 css 屬性,示例如下所示。雖然此方法能在一定程度上解決移動設(shè)備適配的問題,但我們也可以看出其存在以下問題,所以其已幾乎被歷史潮流淘汰。

          • 頁面上所有的元素都得在不同的 @media 中定義一遍不同的尺寸,這個代價有點高;
          • 如果再多一種屏幕尺寸,就得多寫一個 @media 查詢塊;
          @media only screen and (min-width: 375px) {
            .logo {
              width : 62.5px;
            }
          }
          
          @media only screen and (min-width: 360px) {
            .logo {
              width : 60px;
            }
          }
          
          @media only screen and (min-width: 320px) {
            .logo {
              width : 53.3333px;
            }
          }
          

          2.2、使用 rem 單位

          rem(font size of the root element)是指相對于根元素的字體大小的單位,如果我們設(shè)置 html 的 font-size 為 16px,則如果需要設(shè)置元素字體大小為 16px,則寫為 1rem。但是其還是必須得借助 @media 屬性來為不同大小的設(shè)備設(shè)置不同的 font-size,相對上一種方案,可以減少重復編寫相同屬性的代價,簡單示例如下所示。
          我們也能看到該方案存在以下問題:

          • 不同的尺寸需要寫多個 @media;
          • 所有涉及到使用 rem 的地方,全部都需要調(diào)用方法 calc() ,這個也挺麻煩的;
          @media only screen and (min-width: 375px) {
            html {
              font-size : 375px;
            }
          }
          
          @media only screen and (min-width: 360px) {
            html {
              font-size : 360px;
            }
          }
          
          @media only screen and (min-width: 320px) {
            html {
              font-size : 320px;
            }
          }
          
          //定義方法:calc
          @function calc($val){
              @return $val / 1080;
          }
          
          .logo{
          	width : calc(180rem);
          }
          

          2.3、flexible 適配方案

          在 rem 方案上進行改進,我們可以使用 js 動態(tài)來設(shè)置根字體,這種方案的典型代表就是 flexible 適配方案。

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

          它的核心代碼如下所示

          // set 1rem = viewWidth / 10
          function setRemUnit () {
              var rem = docEl.clientWidth / 10
              docEl.style.fontSize = rem + 'px'
          }
          setRemUnit();
          

          上面的代碼中,將 html 節(jié)點的 font-size 設(shè)置為頁面 clientWidth(布局視口)的 1/10,即 1rem 就等于頁面布局視口的 1/10,這就意味著我們后面使用的 rem 都是按照頁面比例來計算的。

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

          設(shè)置 viewport 的 width 為 device-width,改變?yōu)g覽器 viewport(布局視口和視覺視口)的默認寬度為理想視口寬度,從而使得用戶可以在理想視口內(nèi)看到完整的布局視口的內(nèi)容。
          等比設(shè)置 viewport 的 initial-scale、maximum-scale、minimum-scale 的值,從而實現(xiàn) 1 物理像素=1 css像素,以適配高倍屏的顯示效果(就是在這個地方規(guī)避了大家熟知的“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.3.3、flexible 的缺陷

          不可否認 flexible 在兼容性不友好的某個時期還是極大幫助來成千上萬的開發(fā)者,但是該方案自身是存在一些問題的。

          • 由于其縮放的緣故,video 標簽的視頻頻播放器的樣式在不同 dpr 的設(shè)備上展示差異很大;
          • 如果你去研究過 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 {
            // 其他設(shè)備下,仍舊使用1倍的方案
            dpr = 1;
          }
          
          • 不再兼容 @media 的響應式布局,因為 @media 語法中涉及到的尺寸查詢語句,查詢的尺寸依據(jù)是當前設(shè)備的物理像素,和 flexible 的布局理論(即針對不同 dpr 設(shè)備等比縮放視口的 scale 值,從而同時改變布局視口和視覺視口大小)相悖,因此響應式布局在“等比縮放視口大小”的情境下是無法正常工作的;

          其實 flexible 方案是在 模擬 viewport 功能,只是隨著瀏覽器的發(fā)展及兼容性增強,viewport 已經(jīng)能兼容絕大部分主流瀏覽器,并且 flexible 方案自身存在的問題,所有其也已幾乎退出歷史潮流。引用 lib-flexible 的 github 主頁的原話:

          由于 viewport 單位得到眾多瀏覽器的兼容,lib-flexible 這個過渡方案已經(jīng)可以放棄使用,不管是現(xiàn)在的版本還是以前的版本,都存有一定的問題。建議大家開始使用 viewport 來替代此方案。

          2.4、viewport 適配方案

          由于 viewport 單位得到眾多瀏覽器的兼容,所以目前基于 viewport 的移動端適配方案被各大廠團隊所采用。

          vw 作為布局單位,從底層根本上解決了不同尺寸屏幕的適配問題,因為每個屏幕的百分比是固定的、可預測、可控制的。 viewport 相關(guān)概念如下:

          • vw:是 viewport's width 的簡寫,1vw 等于 window.innerWidth 的 1%;
          • vh:和 vw 類似,是 viewport's height 的簡寫,1vh 等于 window.innerHeihgt 的 1%;
          • vmin:vmin 的值是當前 vw 和 vh 中較小的值;
          • vmax:vmax 的值是當前 vw 和 vh 中較大的值;

          假設(shè)我們拿到的視覺稿寬度為 750px,視覺稿中某個字體大小為 75px,則我們的 css 屬性只要如下這么寫,不需要額外的去用 js 進行設(shè)置,也不需要去縮放屏幕等;

          .logo {
            font-size: 10vw; // 1vw = 750px * 1% = 7.5px
          }
          


          2.4.1、設(shè)置 meta 標簽

          在 html 頭部設(shè)置 mata 標簽如下所示,讓當前 viewport 的寬度等于設(shè)備的寬度,同時不允許用戶手動縮放。

          <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
          

          ?

          2.4.2、px 自動轉(zhuǎn)換為 vw

          設(shè)計師一般給寬度大小為 375px 或 750px 的視覺稿,我們采用 vw 方案的話,需要將對應的元素大小單位 px 轉(zhuǎn)換為 vw 單位,這是一項影響開發(fā)效率(需要手動計算將 px 轉(zhuǎn)換為 vw)且不利于后續(xù)代碼維護(css 代碼中一堆 vw 單位,不如 px 看的直觀)的事情;好在社區(qū)提供了 postcss-px-to-viewport 插件,來將 px 自動轉(zhuǎn)換為 vw,相關(guān)配置步驟如下:

          (1) 安裝插件

          npm install postcss-px-to-viewport --save-dev
          

          (2)webpack 配置

          官網(wǎng)是使用 glup 進行配置,但是我們項目模版中是使用 webpack 進行 postcss 插件以及相關(guān)樣式插件的配置,所以我們就使用 webpack 進行配置使用,不需要額外引入 gulp 編譯;webpack 相關(guān)配置如下,且每個屬性表示的意義進行了備注:

          module.exports = {
            plugins: {
              // ...
              'postcss-px-to-viewport': {
                // options
                unitToConvert: 'px',    // 需要轉(zhuǎn)換的單位,默認為"px"
                viewportWidth: 750,     // 設(shè)計稿的視窗寬度
                unitPrecision: 5,       // 單位轉(zhuǎn)換后保留的精度
                propList: ['*', '!font-size'],        // 能轉(zhuǎn)化為 vw 的屬性列表
                viewportUnit: 'vw',     // 希望使用的視窗單位
                fontViewportUnit: 'vw', // 字體使用的視窗單位
                selectorBlackList: [],  // 需要忽略的 CSS 選擇器,不會轉(zhuǎn)為視窗單位,使用原有的 px 等單位
                minPixelValue: 1,       // 設(shè)置最小的轉(zhuǎn)換數(shù)值,如果為 1 的話,只有大于 1 的值會被轉(zhuǎn)換
                mediaQuery: false,      // 媒體查詢里的單位是否需要轉(zhuǎn)換單位
                replace: true,          // 是否直接更換屬性值,而不添加備用屬性
                exclude: undefined,     // 忽略某些文件夾下的文件或特定文件,例如 'node_modules' 下的文件
                include: /\/src\//,     // 如果設(shè)置了include,那將只有匹配到的文件才會被轉(zhuǎn)換
                landscape: false,       // 是否添加根據(jù) landscapeWidth 生成的媒體查詢條件
                landscapeUnit: 'vw',    // 橫屏時使用的單位
                landscapeWidth: 1125,   // 橫屏時使用的視窗寬度
              },
            },
          };
          

          相關(guān)配置屬性,通過注釋一目了然其作用,其中需要強調(diào)的點為 propList 屬性,我們配置了 font-size 不進行轉(zhuǎn)換 vw,也就是說在不同手機屏幕尺寸下的字體大小是一樣的。 其中 font-size 是否需要根據(jù)屏幕大小做適配,或者怎么做,一直是個爭論不休的話題;考慮到我們移動端沒有平板的需求,且咨詢過團隊業(yè)務設(shè)計師的意見,所以對模版進行以上默認配置;當然如果你的視覺要求你的項目要做字體大小適配,修改 propList 屬性的配置即可。

          (3)效果展示 我們在項目代碼中,進行如下 css 編碼:

          .hello {
            color: #333;
            font-size: 28px;
          }
          

          啟動項目,我們可以看到瀏覽器渲染的頁面中,postcss-px-to-viewport 已經(jīng)幫我們做進行了 px -> vw 的轉(zhuǎn)換;如下所示:

          2.4.3、標注不需要轉(zhuǎn)換的屬性

          在項目中,如果設(shè)計師要求某一場景不做自適配,需為固定的寬高或大小,這時我們就需要利用 postcss-px-to-viewport 插件的 Ignoring 特性,對不需要轉(zhuǎn)換的 css 屬性進行標注,示例如下所示:

          • /* px-to-viewport-ignore-next */ —> 下一行不進行轉(zhuǎn)換.
          • /* px-to-viewport-ignore */ —> 當前行不進行轉(zhuǎn)換
          /* example input: */
          .class {
            /* px-to-viewport-ignore-next */
            width: 10px;
            padding: 10px;
            height: 10px; /* px-to-viewport-ignore */
          }
          
          /* example output: */
          .class {
            width: 10px; 
            padding: 3.125vw;
            height: 10px;
          }
          


          2.4.4、Retina 屏預留坑位

          考慮 Retina 屏場景,可能對圖片的高清程度、1px 等場景有需求,所以我們預留判斷 Retina 屏坑位。 相關(guān)方案如下:在入口的 html 頁面進行 dpr 判斷,以及 data-dpr 的設(shè)置;然后在項目的 css 文件中就可以根據(jù) data-dpr 的值根據(jù)不同的 dpr 寫不同的樣式類;

          (1)index.html 文件

          // index.html 文件
          const dpr = devicePixelRatio >= 3? 3: devicePixelRatio >= 2? 2: 1;
          document.documentElement.setAttribute('data-dpr', dpr);
          

          (2)樣式文件

          [data-dpr="1"] .hello {
            background-image: url(image@1x.jpg);
          
          [data-dpr="2"] .hello {
            background-image: url(image@2x.jpg);
          }
            
          [data-dpr="3"] .hello {
            background-image: url(image@3x.jpg);
          }
          


          三、若干特定場景最佳實踐

          3.1、行內(nèi)樣式的場景

          場景:當你需要寫行內(nèi)樣式的代碼(style)時,postcss-px-to-viewport 插件 無法進行 px 單位無法轉(zhuǎn)換,需要自己手動計算好 vw;

          最佳實踐:通過添加、修改、刪除 className 的方式進行處理此類場景,不直接操作行內(nèi)樣式,這更符合將 js 和 css 隔離開的更佳實踐。?

          3.2、1px 的問題

          retina 屏下 1px 問題是個常談的問題,相比較普通屏,retina 屏的 1px 線會顯得比較粗,設(shè)計美感欠缺;在視覺設(shè)計師眼里的 1px 是指設(shè)備像素 1px,而如果我們直接寫 css 的大小 1px,那在 dpr = 2 時,則等于 2px 設(shè)備像素,dpr = 3 時,等于 3px 設(shè)備像素。所以對于要求處理 1px 的場景,我們要進行特殊處理。
          以下介紹常用的幾種方法

          3.2.1、transform: scale(0.5)

          可以使用 transform: scale(0.5) 進行 X、Y 軸的縮放,如下示例所示

          .class1 {
            height: 1px; 
            transform: scaleY(0.5);
          }
          

          優(yōu)點是編寫簡單,但是如果實現(xiàn)上下左右四條邊框會比較難搞,并且如果有嵌套存在的話,會對包含的元素產(chǎn)生影響,所以結(jié)合 :before 和 :after 來使用。?

          3.2.2、transform: scale(0.5) + :before / :after (推薦)

          此種方式能解決例如 標簽上下左右邊框 1px 的場景,以及有嵌套元素存在的場景,比較通用,示例如下所示

          .calss1 {
            position: relative;
            &::after {
              content:"";
              position: absolute;
              bottom:0px;
              left:0px;
              right:0px;
              border-top:1px solid #666;
              transform: scaleY(0.5);
            }
          }
          

          3.2.3、box-shadow

          利用 css 對陰影處理來模擬邊框,示例如下所示,底部一條線,缺點是存在陰影不好看。

            .class1 {
              box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
            }
          

          3.2.4、其它

          還有如下等方式處理 1px 問題,但不推薦,了解即可

          • viewport: 將頁面進行縮小處理;
          • border-image:切個 1px 圖片來模擬;
          • background-image:切個 1px 圖片來模擬;
          • linear-gradient:通過線性漸變,來實現(xiàn)移動端 1px 的線;
          • svg:基于矢量圖形(svg) 在不同設(shè)備屏幕特性下具有伸縮性。

          3.3、圖片高清的問題

          圖片高清的問題:

          • 適用普通屏的圖片在 retina 屏中,圖片展示就會顯得模糊;
          • 適用 retina 屏的圖片在普通屏中,圖片展示就會缺少色差、沒有銳利度,并且浪費帶寬;

          所以如果對性能、美觀要求很高的場景,需要根據(jù) dpr 區(qū)分使用對應的圖片,我們在文章 viewport 適配方案中針對 retina 屏預留了 dpr 方案,相關(guān) css 寫法如下:

          [data-dpr="1"] .hello {
            background-image: url(image@1x.jpg);
          
          [data-dpr="2"] .hello {
            background-image: url(image@2x.jpg);
          }
            
          [data-dpr="3"] .hello {
            background-image: url(image@3x.jpg);
          }
          

          ?

          四、iPhoneX 適配方案

          iPhoneX 取消了物理按鍵,改成底部小黑條,這一改動導致網(wǎng)頁出現(xiàn)了比較尷尬的屏幕適配問題。對于網(wǎng)頁而言,頂部(劉海部位)的適配問題瀏覽器已經(jīng)做了處理,所以我們只需要關(guān)注底部與小黑條的適配問題即可(即常見的吸底導航、返回頂部等各種相對底部 fixed 定位的元素)。 比如一些需要貼在底部的按鈕,和呼起的tabBar和底部彈出框,在iphoneX上就會出現(xiàn)被小黑條遮擋內(nèi)容,或者頁面上出現(xiàn)白色空隙的問題。處理前后截圖如下所示

          4.1、適配之前需要了解的幾個新知識

          4.1.1、安全區(qū)域

          安全區(qū)域指的是一個可視窗口范圍,處于安全區(qū)域的內(nèi)容不受圓角(corners)、齊劉海(sensor housing)、小黑條(Home Indicator)影響,如下圖藍色區(qū)域:

          也就是說,我們要做好適配,必須保證頁面可視、可操作區(qū)域是在安全區(qū)域內(nèi)。 更詳細說明,參考文檔:Human Interface Guidelines - iPhoneX

          4.1.2、viewport-fit

          iOS11 新增特性,蘋果公司為了適配 iPhoneX 對現(xiàn)有 viewport meta 標簽的一個擴展,用于設(shè)置網(wǎng)頁在可視窗口的布局方式,可設(shè)置三個值。?

          • contain: 可視窗口完全包含網(wǎng)頁內(nèi)容(左圖)
          • cover:網(wǎng)頁內(nèi)容完全覆蓋可視窗口(右圖)
          • auto:默認值,跟 contain 表現(xiàn)一致

          需要注意:網(wǎng)頁默認不添加擴展的表現(xiàn)是 viewport-fit=contain,需要適配 iPhoneX 必須設(shè)置 viewport-fit=cover,這是適配的關(guān)鍵步驟。更詳細說明,參考文檔:viewport-fit-descriptor

          4.1.3、env() 和 constant()

          iOS11 新增特性,Webkit 的一個 CSS 函數(shù),用于設(shè)定安全區(qū)域與邊界的距離,有四個預定義的變量:

          • safe-area-inset-left:安全區(qū)域距離左邊邊界距離
          • safe-area-inset-right:安全區(qū)域距離右邊邊界距離
          • safe-area-inset-top:安全區(qū)域距離頂部邊界距離
          • safe-area-inset-bottom:安全區(qū)域距離底部邊界距離

          這里我們只需要關(guān)注 safe-area-inset-bottom 這個變量,因為它對應的就是小黑條的高度(橫豎屏時值不一樣)。

          注意:當 viewport-fit=contain 時 env() 是不起作用的,必須要配合 viewport-fit=cover 使用。對于不支持 env() 的瀏覽器,瀏覽器將會忽略它。

          需要注意的是之前使用的 constant() 在 iOS11.2 之后就不能使用的,但我們還是需要做向后兼容,像這樣:

          padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
          padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */
          

          注意:env() 跟 constant() 需要同時存在,而且順序不能換。 更詳細說明,參考文檔:Designing Websites for iPhone X


          4.2、適配步驟

          4.2.1、設(shè)置網(wǎng)頁在可視窗口的布局方式

          新增 viweport-fit 屬性,使得頁面內(nèi)容完全覆蓋整個窗口,前面也有提到過,只有設(shè)置了 viewport-fit=cover,才能使用 env()

          	<meta name="viewport" content="width=device-width, viewport-fit=cover">
          

          4.2.2、fixed 完全吸底元素場景的適配

          可以通過加內(nèi)邊距 padding 擴展高度:

          {
            padding-bottom: constant(safe-area-inset-bottom);
            padding-bottom: env(safe-area-inset-bottom);
          }
          

          或者通過計算函數(shù) calc 覆蓋原來高度:

          {
            height: calc(60px(假設(shè)值) + constant(safe-area-inset-bottom));
            height: calc(60px(假設(shè)值) + env(safe-area-inset-bottom));
          }
          

          注意,這個方案需要吸底條必須是有背景色的,因為擴展的部分背景是跟隨外容器的,否則出現(xiàn)鏤空情況。

          還有一種方案就是,可以通過新增一個新的元素(空的顏色塊,主要用于小黑條高度的占位),然后吸底元素可以不改變高度只需要調(diào)整位置,像這樣:

          {
            margin-bottom: constant(safe-area-inset-bottom);
            margin-bottom: env(safe-area-inset-bottom);
          }
          

          空的顏色塊:

          {
            position: fixed;
            bottom: 0;
            width: 100%;
            height: constant(safe-area-inset-bottom);
            height: env(safe-area-inset-bottom);
            background-color: #fff;
          }
          

          4.2.3、fixed 非完全吸底元素場景的適配

          像這種只是位置需要對應向上調(diào)整,可以僅通過下外邊距 margin-bottom 來處理

          {
            margin-bottom: constant(safe-area-inset-bottom);
            margin-bottom: env(safe-area-inset-bottom);
          }
          

          或者,你也可以通過計算函數(shù) calc 覆蓋原來 bottom 值:

          {
            bottom: calc(50px(假設(shè)值) + constant(safe-area-inset-bottom));
            bottom: calc(50px(假設(shè)值) + env(safe-area-inset-bottom));
          }
          

          五、VW 兼容方案

          Android 4.4 之下和 iOS 8 以下的版本有一定的兼容性問題(ps:幾乎絕跡,大家可以統(tǒng)計下你們的用戶使用的系統(tǒng)版本占比),但是社區(qū)提供了兼容性解決方案,其為 viewport 的 buggyfill:Viewport Units Buggyfill,可以訪問其 github 官網(wǎng)查看。
          我們也做了對應的實踐,但是考慮到性能,我們項目模版中不會進行引入,有興趣的同學可以查看以下實踐總結(jié);

          5.1、Viewport Units Buggyfill 引入

          viewport-units-buggyfill 主要有兩個 JavaScript 文件:viewport-units-buggyfill.js 和 viewport-units-buggyfill.hacks.js。你只需要在你的 HTML 文件中引入這兩個文件,比如在 react 項目中的 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>
          

          第二步,在HTML文件中調(diào)用 viewport-units-buggyfill,比如:

          <script>
              window.onload = function () {
                  window.viewportUnitsBuggyfill.init({
                      hacks: window.viewportUnitsBuggyfillHacks
                  });
              }
          </script>
          

          但是為保證 Viewport Units Buggyfill 起作用,我們必須在我們樣式文件中用到了viewport 的單位(vw、vh、vmin 或 vmax )地方添加 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);';
          }
          

          5.2、postcss-viewport-units 引入

          在 1 步驟中,我們?nèi)巳庖?content 屬性,效率是非常低下的,好在社區(qū)提供了 postcss-viewport-units 插件,幫我們自動處理 content:

          5.2.1、postcss-viewport-units 安裝配置

          我們執(zhí)行以下命令,進行 postcss-viewport-units 插件的安裝:

          tnpm i postcss-viewport-units --save-dev
          

          在我們的項目配置文件 webpack.config.js 中進行對應的插件引入配置:

          {
            loader: 'postcss-loader',
            options: {
              ident: 'postcss',
              plugins: () => [
                // 我們加的配置
                require('postcss-viewport-units'),
              ],
              sourceMap: isProductionEnv,
            },
          },
          

          5.2.2、效果展示

          我們在項目代碼中,進行如下編碼:

          .hello {
            color: #333;
            font-size: 28px;
          }
          

          展示的頁面中,postcss-viewport-units 已經(jīng)幫我們添加了 content 屬性;如下所示:


          主站蜘蛛池模板: 不卡一区二区在线| 极品人妻少妇一区二区三区| 国产成人一区二区三区在线| 久久精品一区二区东京热| 尤物精品视频一区二区三区 | www一区二区www免费| 久久久久一区二区三区| 偷拍精品视频一区二区三区| 性盈盈影院免费视频观看在线一区| 无码国产精品一区二区免费16| 中文字幕一区二区三区四区| 免费无码一区二区| 国产一区二区三区在线观看免费 | 视频一区视频二区制服丝袜| 麻豆AV无码精品一区二区| 91大神在线精品视频一区| 精品少妇一区二区三区视频| 国产成人精品无码一区二区三区| 一区二区三区91| 亚洲av无码天堂一区二区三区| 日韩一区二区三区视频| 国产一区二区精品在线观看| 日韩在线视频一区| 日韩AV在线不卡一区二区三区| 精品3d动漫视频一区在线观看| 国产一区二区三区小向美奈子| 午夜视频一区二区三区| 国产精品va一区二区三区| 黑人一区二区三区中文字幕| 夜夜爽一区二区三区精品| chinese国产一区二区| 国模丽丽啪啪一区二区| 风间由美性色一区二区三区 | 色狠狠色噜噜Av天堂一区| 本免费AV无码专区一区| 亚洲AV无码一区东京热| 91精品一区二区综合在线| 亚洲欧美日韩一区二区三区 | 又硬又粗又大一区二区三区视频| 久久精品岛国av一区二区无码| 国产精品自拍一区|