整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          一文搞定移動端適配

          一文搞定移動端適配

          者 | 好學習吧丶

          責編 | 郭芮

          手機市場日漸豐富的同時,給我們前端開發人員帶來的 “網頁內容自適應屏幕尺寸進行顯示的問題” 也日漸凸顯出來,接下來我們就要細說移動端適配的前世今生及方案。

          為什么要移動端適配?

          一般情況下設計稿的設計師按照 375 的尺寸設計,然而,在現在移動終端(就是手機)快速更新的時代,每個品牌的手機都有著不同的物理分辨率,這樣就會導致,每臺設備的邏輯分辨率也不盡相同,此時 375 的設計稿,如果想要還原那基本是不可能了,因為如果一個左右布局,左邊如果寫死,右邊自適應的話,每個設備的右邊所展示的內容大小就不盡相同,這時移動端適配就顯得尤其重要。

          既然要了解前世今生,我們就從幾個概念說起,先上一張圖。

          下面我們一個個解析。

          1.1 屏幕尺寸

          屏幕尺寸是以屏幕對角線的長度來計量,計量單位為英寸。

          如圖所示兩個對角線的長度就是這個屏幕的尺寸:

          1.2 像素

          我們看到上圖 320x480 叫分辨率,而這個所謂的分辨率說白了就是橫向320個像素縱向480個像素組成

          1.2.1 什么叫像素呢?

          像素(Pel, pixel, pictureelement),為組成一幅圖像的全部亮度和色度的最小圖像單元。電視的圖像是由按一定間隔排列的亮度不同的像點構成的,形成像點的單位也就是像素,組成圖像的最小單位就是像素。從計算機技術的角度來解釋,像素是硬件和軟件所能控制的最小單位。它指顯示屏的畫面上表示出來的最小單位,不是圖畫上的最小單位。一幅圖像通常包含成千上萬個像素,每個像素都有自己的顏色信息,它們緊密地組合在一起。由于人眼的錯覺,這些組合在一起的像素被當成一幅完整的圖像。當修改圖像的某區域,實際上是在修改該區域內的像素。對這些像素修改的好與壞將決定最終圖片的質量。單位面積內的像素越多,圖像的效果就越好。彩色電視圖像是由成千個像素點所組成的,而且每個像素都是由紅綠藍三種顏色并排組成的。(注意每個像素的大小是不固定的,他是根據設備的分辨率決定的,知識點,后面要考)

          1.2.2 什么叫分辨率呢?

          屏幕分辨率是指縱橫向上的像素點數,單位是 px。屏幕分辨率確定計算機屏幕上顯示多少信息的設置,以水平和垂直像素來衡量。就相同大小的屏幕而言,當屏幕分辨率低時(例如 640 x 480),在屏幕上顯示的像素少,單個像素尺寸比較大。屏幕分辨率高時(例如 1600 x 1200),在屏幕上顯示的像素多,單個像素尺寸比較小。

          知道什么叫做分辨率后,有人就會奇怪,我記得蘋果的蘋果官網上的蘋果 6 的分辨率為 750x1334 啊,但是設計稿上蘋果 6 的分辨率為 375x667 啊,而且各個設備的分辨率都比實際分辨率小很多,這就牽扯到一些歷史原因了

          1.2.3 設備物理分辨率(設備像素)

          相信我們所有前端開發者,都是見證了手機這個移動設備發展的過程。從藍屏手機,到彩屏手機,到諾基亞研發出來觸屏手機,再到智能手機一步步發展下來,我們的我們的手越來越清晰,越來越大,所以我們的屏幕發展也越來越迅速。

          上圖可以清楚的看到,不同分辨率所帶來的的差距。

          從最初的顆粒感相當大的屏幕,到 720p 再到 1080p,甚至于現在各家旗艦手機的 2k 屏幕,我們的物理分辨率在變得原來越大。這樣就暴露出來一個問題,我們如果手機分辨率翻倍,我們的圖像不就要被縮小一倍,我們難道要在每個設備上就出個設計稿,每個設備的分辨不盡相同啊,其實你擔憂的問題,我們的喬幫主在很多年前就想到了。這就是我們的邏輯分辨率

          1.2.4 邏輯分辨率(設備獨立像素)

          如下圖所示,雖然設備物理分辨不同,但是他的這個邏輯分辨率卻都差不多,這就要感謝喬幫主了。

          喬布斯在 iPhone4 的發布會上首次提出了 Retina Display(視網膜屏幕)的概念,在 iPhone4 使用的視網膜屏幕中,把 2x2 個像素當 1 個像素使用,這樣讓屏幕看起來更精致,但是元素的大小卻不會改變。從此以后高分辨率的設備,多了一個邏輯像素。這些設備邏輯像素的差別雖然不會跨度很大,但是仍然有點差別,于是便誕生了移動端頁面需要適配這個問題,既然邏輯像素由物理像素得來,那他們就會有一個像素比值。

          1.2.5 設備像素比

          設備像素比 device pixel ratio 簡稱 dpr,即物理像素和設備獨立像素的比值。為什么要知道設備像素比呢?因為這個像素比會產生一個非常經典的問題,1 像素邊框的問題。

          1、1px 邊框問題

          當我們 css 里寫的 1px 的時候,由于它是邏輯像素,導致我們的邏輯像素根據這個設備像素比(dpr)去映射到設備上就為 2px,或者 3px,由于每個設備的屏幕尺寸不一樣,就導致每個物理像素渲染出來的大小也不同(記得上面的知識點嗎,設備的像素大小是不固定的),這樣如果在尺寸比較大的設備上,1px 渲染出來的樣子相當的粗礦,這就是經典的一像素邊框問題。

          2、如何解決?

          核心思路,就是在 web 中,瀏覽器為我們提供了 window.devicePixelRatio 來幫助我們獲取 dpr。在 css 中,可以使用媒體查詢 min-device-pixel-ratio,區分 dpr:我們根據這個像素比,來算出他對應應該有的大小,但是暴露個非常大的兼容問題。

          其中 Chrome 把 0.5px 四舍五入變成了 1px,而 firefox/safari 能夠畫出半個像素的邊,并且 Chrome 會把小于 0.5px 的當成 0,而 Firefox 會把不小于 0.55px 當成 1px,Safari 是把不小于 0.75px 當成 1px,進一步在手機上觀察 iOS 的 Chrome 會畫出 0.5px 的邊,而安卓(5.0)原生瀏覽器是不行的。所以直接設置 0.5px 不同瀏覽器的差異比較大,并且我們看到不同系統的不同瀏覽器對小數點的 px 有不同的處理。所以如果我們把單位設置成小數的 px 包括寬高等,其實不太可靠,因為不同瀏覽器表現不一樣。

          至于其他解決一像素邊框問題網上有一堆答案,在這里我推薦一種非常好用,并且沒有副作用的解決方案。

          transform: scale(0.5) 方案

          div { height:1px; background:#000; -webkit-transform: scaleY(0.5); -webkit-transform-origin:0 0; overflow: hidden;}

          css 根據設備像素比媒體查詢后的解決方案

          /* 2倍屏 */@media only screen and (-webkit-min-device-pixel-ratio: 2.0) { .border-bottom::after { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); }}/* 3倍屏 */@media only screen and (-webkit-min-device-pixel-ratio: 3.0) { .border-bottom::after { -webkit-transform: scaleY(0.33); transform: scaleY(0.33); }}

          如此,完美的解決一像素看著粗的問題。

          擴展補充

          CSS 最新的規范中正在計劃通過標準的屬性實現一像素邊框,通過給border-width屬性添加hairline關鍵字屬性來實現,具體如下鏈接[1]。之所以叫hairline,是因為一像素邊框就跟頭發絲一樣。

          練習使用方案時,也要多多關注最新發展喲。

          如何適配?

          2.1 viewport

          視口(viewport)代表當前可見的計算機圖形區域。在 Web 瀏覽器術語中,通常與瀏覽器窗口相同,但不包括瀏覽器的 UI, 菜單欄等——即指你正在瀏覽的文檔的那一部分。

          那么在移動端如何配置視口呢?簡單的一個 meta 標簽即可!

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

          他們分別什么含義呢?

          我們在移動端視口要想視覺效果和體驗好,那么我們的視口寬度必須無限接近理想視口。

          理想視口:一般來講,這個視口其實不是真是存在的,它對設備來說是一個最理想布局視口尺寸,在用戶不進行手動縮放的情況下,可以將頁面理想地展示。那么所謂的理想寬度就是瀏覽器(屏幕)的寬度了。

          于是上述的 meta 設置,就是我們的理想設置,他規定了我們的視口寬度為屏幕寬度,初始縮放比例為 1,就是初始時候我們的視覺視口就是理想視口!

          其中 user-scalable 設置為 no 可以解決移動端點擊事件延遲問題(拓展)

          2.2 適配方法

          2.2.1 rem 適配

          rem 是 CSS3 新增的一個相對單位,這個單位引起了廣泛關注。這個單位與 em 有什么區別呢?區別在于使用 rem 為元素設定字體大小時,仍然是相對大小,但相對的只是 HTML 根元素。這個單位可謂集相對大小和絕對大小的優點于一身,通過它既可以做到只修改根元素就成比例地調整所有字體大小,又可以避免字體大小逐層復合的連鎖反應。目前,除了 IE8 及更早版本外,所有瀏覽器均已支持 rem。對于不支持它的瀏覽器,應對方法也很簡單,就是多寫一個絕對單位的聲明。這些瀏覽器會忽略用 rem 設定的字體大小。

          舉個例子:

          //假設我給根元素的大小設置為14px
          html{
          font-size:14px
          }
          //那么我底下的p標簽如果想要也是14像素
          p{
          font-size:1rem
          }
          //如此即可

          rem 的布局,不得不提 flexible,flexible 方案是阿里早期開源的一個移動端適配解決方案,引用 flexible 后,我們在頁面上統一使用 rem 來布局。

          原理非常簡單:

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

          rem 是相對于 html 節點的 font-size 來做計算的。所以在頁面初始話的時候給根元素設置一個 font-size,接下來的元素就根據 rem 來布局,這樣就可以保證在頁面大小變化時,布局可以自適應。

          如此我們只需要給設計稿的 px 轉換成對應的 rem 單位即可。

          當然,這個方案只是個過渡方案——為什么說是過渡方案?

          因為當年 viewport 在低版本安卓設備上還有兼容問題,而 vw,vh 還沒能實現所有瀏覽器兼容,所以 flexible 方案用 rem 來模擬 vmin 來實現在不同設備等比縮放的“過度”方案,之所以說是過度方案,是因為這個他這個根據設備大小去判斷頁面的方案是根據屏幕大小去百分百還原設計稿,從而讓人看到的大小效果是一樣的,但是 蘋果 5 和蘋果 6p 雖然看到的設計稿還原是一樣的,但是他在一個合適距離上看到的效果能一樣嗎,本質上,用戶使用更大的屏幕,是想看到更多的內容,而不是更大的字。

          so,這個用縮放來解決問題的方案是個過渡方案,注定被時代所淘汰。

          2.2.2 vw,vh 布局

          vh、vw 方案即將視覺視口寬度 window.innerWidth 和視覺視口高度 window.innerHeight 等分為 100 份。

          vh 和 vw 方案和 rem 類似也是相當麻煩需要做單位轉化,而且 px 轉換成 vw 不一定能完全整除,因此有一定的像素差。

          不過在工程化的今天,webpack 解析 css 的時候用 postcss-loader 有個 postcss-px-to-viewport 能自動實現 px 到 vw 的轉化。

          {
          loader: 'postcss-loader',
          options: {
          plugins:=>[
          require('autoprefixer')({
          browsers: ['last 5 versions']
          }),
          require('postcss-px-to-viewport')({
          viewportWidth: 375, //視口寬度(數字)
          viewportHeight: 1334, //視口高度(數字)
          unitPrecision: 3, //設置的保留小數位數(數字)
          viewportUnit: 'vw', //設置要轉換的單位(字符串)
          selectorBlackList: ['.ignore', '.hairlines'], //不需要進行轉換的類名(數組)
          minPixelValue: 1, //設置要替換的最小像素值(數字)
          mediaQuery: false //允許在媒體查詢中轉換px(true/false)
          })
          ]
          }

          2.2.3 px 為主,vx 和 vxxx(vw/vh/vmax/vmin)為輔,搭配一些 flex(推薦)

          之所以推薦使用此種方案,是由于我們要去考慮用戶的需求,用戶之所以去買大屏手機,不是為了看到更大的字,而是為了看到更多的內容,這樣直接使用 px 是最明智的方案,使用 vw,rem 等布局手段無可厚非,但是,flex 這種彈性布局大行其道的今天,如果如果還用這種傳統的思維去想問題顯然是有兩個原因(個人認為 px 是最好的,可能有大佬,能用 vw,或者 rem 寫出精妙的布局,也說不準)。

          • 為了偷懶,不愿意去做每個手機的適配

          • 不愿意去學習新的布局方式,讓 flex 等先進的布局和你擦肩而過

          2.3 移動端適配流程

          • 在 head 設置 width=device-width 的 viewport‘

          • 在 css 中使用 px

          • 在適當的場景使用 flex 布局,或者配合 vw 進行自適應

          • 在跨設備類型的時候(pc <-> 手機 <-> 平板)使用媒體查詢

          • 在跨設備類型如果交互差異太大的情況,考慮分開項目開發

          寫在最后

          疫情期間有了跳槽的想法,問到移動端布局方面,雖然勉強能回答上來,但是總是支支吾吾,不是很了解,故而,發下宏愿,梳理移動端適配,幫助后來人后來者居上!

          原文作者:好學習吧丶 整理:歪碼行空公眾號

          原文鏈接:https://juejin.im/post/5e6caf55e51d4526ff026a71

          聲明:本文為投稿,版權歸其個人所有。

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

          一、基本知識點

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

          1.1、響應式設計 - 像素

          像素單位有設備像素、邏輯像素、CSS 像素 3 種。

          1.1.1、設備像素、設備分辨率

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

          1.1.2、設備獨立像素、邏輯分辨率

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

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

          ?

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

          1.1.3、CSS 像素

          在 CSS 中使用的 px 都是指 css 像素,比如 width: 128px。css 像素的大小是很容易變化的,當我們縮放頁面的時候,元素的 css 像素數量不會改變,改變的只是每個 css 像素的大小。也就是說 width: 128px 的元素在縮放200% 以后,寬度依然是 128 個 css 像素,只不過每個 css 像素的寬度和高度變為原來的兩倍。如果原本元素寬度為 128 個設備獨立像素,那么縮放 200% 以后元素寬度為 256 個設備獨立像素。?

          (1)css 像素與設備獨立像素的關系

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

          (2)css 像素與設備像素的關系

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


          1.2、響應式設計 - viewport

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

          1.2.1、layout viewport

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

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

          ?

          1.2.2、visual viewport

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

          1.2.3、ideal viewport

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

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

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

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

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

          width

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

          height

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

          initial-scale

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

          minimum-scale

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

          maximum-scale

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

          user-scalable

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

          二、方案選擇

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

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

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

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

          • 不同的尺寸需要寫多個 @media;
          • 所有涉及到使用 rem 的地方,全部都需要調用方法 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 動態來設置根字體,這種方案的典型代表就是 flexible 適配方案。

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

          它的核心代碼如下所示

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

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

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

          設置 viewport 的 width 為 device-width,改變瀏覽器 viewport(布局視口和視覺視口)的默認寬度為理想視口寬度,從而使得用戶可以在理想視口內看到完整的布局視口的內容。
          等比設置 viewport 的 initial-scale、maximum-scale、minimum-scale 的值,從而實現 1 物理像素=1 css像素,以適配高倍屏的顯示效果(就是在這個地方規避了大家熟知的“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 在兼容性不友好的某個時期還是極大幫助來成千上萬的開發者,但是該方案自身是存在一些問題的。

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

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

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

          2.4、viewport 適配方案

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

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

          • 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 中較大的值;

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

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


          2.4.1、設置 meta 標簽

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

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

          ?

          2.4.2、px 自動轉換為 vw

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

          (1) 安裝插件

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

          (2)webpack 配置

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

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

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

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

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

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

          2.4.3、標注不需要轉換的屬性

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

          • /* px-to-viewport-ignore-next */ —> 下一行不進行轉換.
          • /* px-to-viewport-ignore */ —> 當前行不進行轉換
          /* 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 屏坑位。 相關方案如下:在入口的 html 頁面進行 dpr 判斷,以及 data-dpr 的設置;然后在項目的 css 文件中就可以根據 data-dpr 的值根據不同的 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、行內樣式的場景

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

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

          3.2、1px 的問題

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

          3.2.1、transform: scale(0.5)

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

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

          優點是編寫簡單,但是如果實現上下左右四條邊框會比較難搞,并且如果有嵌套存在的話,會對包含的元素產生影響,所以結合 :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:通過線性漸變,來實現移動端 1px 的線;
          • svg:基于矢量圖形(svg) 在不同設備屏幕特性下具有伸縮性。

          3.3、圖片高清的問題

          圖片高清的問題:

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

          所以如果對性能、美觀要求很高的場景,需要根據 dpr 區分使用對應的圖片,我們在文章 viewport 適配方案中針對 retina 屏預留了 dpr 方案,相關 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 取消了物理按鍵,改成底部小黑條,這一改動導致網頁出現了比較尷尬的屏幕適配問題。對于網頁而言,頂部(劉海部位)的適配問題瀏覽器已經做了處理,所以我們只需要關注底部與小黑條的適配問題即可(即常見的吸底導航、返回頂部等各種相對底部 fixed 定位的元素)。 比如一些需要貼在底部的按鈕,和呼起的tabBar和底部彈出框,在iphoneX上就會出現被小黑條遮擋內容,或者頁面上出現白色空隙的問題。處理前后截圖如下所示

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

          4.1.1、安全區域

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

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

          4.1.2、viewport-fit

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

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

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

          4.1.3、env() 和 constant()

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

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

          這里我們只需要關注 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、設置網頁在可視窗口的布局方式

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

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

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

          可以通過加內邊距 padding 擴展高度:

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

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

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

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

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

          {
            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 非完全吸底元素場景的適配

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

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

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

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

          五、VW 兼容方案

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

          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文件中調用 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 步驟中,我們人肉引入 content 屬性,效率是非常低下的,好在社區提供了 postcss-viewport-units 插件,幫我們自動處理 content:

          5.2.1、postcss-viewport-units 安裝配置

          我們執行以下命令,進行 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 已經幫我們添加了 content 屬性;如下所示:

          前端開發中,移動端不同設備的屏幕適配一直是個繞不開的技術話題。目前比較流行的方案是類似淘寶的flexible。其原理是使用js動態計算html的font-size,利用rem來實現不同寬度的適配。使用js方案雖然比較成熟,但也有它的一些缺點,比如性能損耗,由于js的阻塞加載和動態計算,頁面不免會出現卡頓和閃屏的現象,影響用戶體驗。今天我們不使用js,完全使用css來實現適配,來看看是怎么實現的吧!

          移動端屏幕適配

          方案一:使用meta標簽

          在html的head中插入下面的meta標簽:

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

          沒錯,是兩個viewport標簽。width=device-width寫在上面,width=375寫在下面,375就是以哪個設備寬度為基準,現在大部分設計稿都是以iphone6的375寬度為基準做2倍圖。加了上面兩個mata標簽,后面的css就可以完全使用px為單位直接使用,整個頁面會自動按設備寬度進行等比例縮放??聪旅娴难菔拘Ч?/p>

          <script src="https://lf3-cdn-tos.bytescm.com/obj/cdn-static-resource/tt_player/tt.player.js?v=20160723"></script>


          方案二:使用css的calc計算屬性

          在css中定義html的font-size為:calc(100vw/3.75),calc、vw能兼容ios8+和android4.4+,可放心使用,如下:

          html {
            font-size: calc(100vw/3.75);
            -webkit-text-size-adjust: 100%;
          }

          然后在css中,就可以將所有的px單位除以100,得到rem單位了。比如:

          .row>div {
            float: left;
            width: .82rem;
            height: .82rem;
            text-align: center;
            line-height: .82rem;
            margin-left: .05rem;
            background-color: #f0f0f0;
          }

          上面的rem單位轉換,建議大家可以使用px2rem這個插件完成,webpack、vscode都能支持。設置時將rootFontSize 設為100即可。

          設置px2rem參數

          在vscode中,可以使用ctrl+shift+p,選擇px2rem就可以將當前頁面的px全部轉換為rem。

          px2rem在vscode中的使用方法


          當然,rem和px可以相互共存,比如我標題欄就想要44px高,這樣就不會縮放了??聪旅娴难菔拘Ч?/p>

          純css實現移動端適配

          總結:兩種方案如何選擇

          方案一,直接使用html的mata實現整個頁面的縮放,比較適合那些宣傳單頁或全屏游戲交互類,無法實現px與rem共存的情況。

          方案二,利用了rem來縮放,可實現與px共存,在借助px2rem的情況下,能高效方便的實現適配。

          綜合考慮,小編建議使用方案二。你,學會了嗎?


          主站蜘蛛池模板: 免费精品一区二区三区第35| 亚洲国产日韩一区高清在线| 久久精品道一区二区三区| 免费观看一区二区三区| 欧洲无码一区二区三区在线观看 | 国产精品一区二区久久精品| 人妻少妇精品视频三区二区一区 | 国产三级一区二区三区| 成人一区二区免费视频| 麻豆AV一区二区三区| 国产日韩精品一区二区在线观看 | 色窝窝无码一区二区三区 | 偷拍精品视频一区二区三区| 国产亚洲3p无码一区二区| 亚洲另类无码一区二区三区| 日韩一区二区三区在线观看| 免费观看一区二区三区| 国产午夜精品一区二区三区极品 | 欧美激情国产精品视频一区二区 | 国产精品一区二区电影| 亚洲乱码日产一区三区| 亚洲AV日韩AV一区二区三曲| 国产一区二区视频在线播放| 亚洲狠狠久久综合一区77777| 精品国产区一区二区三区在线观看| 中文字幕一区视频一线| 精品一区二区ww| 文中字幕一区二区三区视频播放| 国产精品日韩一区二区三区| 精品视频一区二区三区免费| 久久久久人妻精品一区三寸| 国产在线aaa片一区二区99| 国产精品视频免费一区二区| 国偷自产av一区二区三区| 日韩免费无码一区二区视频| 台湾无码一区二区| 中文字幕VA一区二区三区| 国产精品福利一区| 亚洲综合无码一区二区痴汉| 国产一区二区三区久久| 亚洲午夜精品第一区二区8050|