「不要再問我XX的問題」系列:
1、 不要再問我this的指向問題了
2、 不要再問我跨域的問題了
移動端適配的問題,通常來講咱們都不會去深究,由於這種東西都是配置一次就不再用管的了,接到設計圖就按照祖傳套路擼就完事了。循序漸進的一定只能成爲活動頁寫手,研究透徹之後,才能成爲一名專業的活動頁寫手嘛。css
文章開始,咱們須要來捋清楚像素、視口以及縮放之間種種藕斷絲連的關係,來抽絲剝繭一波。html
像素咱們寫得多了,不就是px嘛,爲何要拿出來講呢?由於像素還不只僅就是px。android
設備像素也能夠叫物理像素,由設備的屏幕決定,其實就是屏幕中控制顯示的最小單位。git
設備獨立像素是一種能夠被程序所控制的虛擬像素,在Web開發中對應CSS像素。github
設備像素與設備獨立像素之間的關係就是,DPR(設備像素比),設備像素比 = 設備像素 / 設備獨立像素。這條公式成立的前提是,縮放比爲1,緣由下面講到縮放的時候就會知道。根據這種關係,若是設備像素大於設備獨立像素(DPR大於1的設備,咱們常說的高清屏或者Retina屏),就會出現一個設備獨立像素對應多個設備像素的狀況:
segmentfault
遙想從前智能手機剛出的時候,不多網站去特地適配移動端,然而用戶是能夠直接從手機去訪問PC端網站的,因此怎樣顯示好一個網站,不管這個網站是一個PC網站仍是移動端網站,就是亟需解決的問題。因此移動端三個視口布局視口、視覺視口、理想視口橫空出世,成爲各類移動適配方案的基礎。跨域
佈局視口是在html元素之上的容器,咱們的頁面就「裝」在佈局視口中。
想一想咱們常寫的width:100%,這個100%是基於什麼計算出來的呢?去翻資料會看到:若是某些屬性被賦予一個百分值的話,它的計算值是由這個元素的包含塊計算而來的。那html元素的包含塊是什麼呢?沒錯,就是咱們的佈局視口,它是全部CSS百分比推算的根源,若是說CSS是一支畫筆,那麼佈局視口就是那張畫布吧。這張畫布有一個默認尺寸(若是沒有手動去設置meta viewport),通常在768px ~ 1024px間,能夠經過document.documentElement.clientWidth獲取。這樣一來,網頁的佈局就再也不受限於設備的尺寸,即便是小屏幕的移動端設備中也能容得下PC網站。
瀏覽器
視覺視口是指用戶經過設備屏幕看到的區域,能夠經過縮放來改變視覺視口的大小,並經過window.innerWidth獲取。
這裏有必要講一下縮放,縮放改變的是CSS像素的大小,放大時CSS像素增大,則一個CSS像素能夠跨越更多的設備像素,視覺視口會變小。什麼?放大反而視覺視口變小?沒錯,這是由於視覺視口也是經過CSS像素度量,而放大就是使CSS像素放大,假設屏幕上原本須要200個CSS像素才能佔滿屏幕,因爲放大,如今只須要100個CSS像素就能佔滿,因此視覺視口的寬就變成100px。
雖然縮放改變了CSS像素的大小,但移動端的縮放是不會改變佈局視口的,因此縮放並不會影響佈局,不過在PC端是會影響佈局的。最直觀的感覺是,咱們平時在移動端雙指縮放網頁,整個網頁的佈局是沒有變化的,能夠經過拖動來看到不一樣區域的東西,可是在PC端進行縮放,好比閱讀時想文字大一些而對網頁進行放大操做,這時字是放大了,但整個頁面的佈局會有所改變。那麼既然與佈局視口無關那還跟誰有關係呢?答案就是下面準備要講的理想視口,它們之間的計算方式是:縮放係數 = 理想視口寬度 / 視覺視口寬度
sass
理想視口是指網站在移動設備中的理想大小,這個大小就是設備的屏幕大小。
爲何須要理想視口呢?首先,先來看看如今的狀況是怎麼的不理想。咱們在瀏覽一個沒通過移動適配的網站時,因爲佈局視口在768px ~ 1024px之間,整個網站就「畫」在一個這麼大的「畫布」上,但因爲手機屏幕比「畫布」小,因此須要通過縮小才能塞進手機屏幕,結果咱們瀏覽網站的時候雖然看得見全貌,但裏面的東西都變得很小,須要放大一下才能看得清,就是這麼不理想。若是不須要放大就能夠看得清那就很理想了嘛。回想一下上面不理想的解決方案,就是將一個大畫布通過縮小裝進小屏幕裏,假設如今畫布跟屏幕同樣大,就在這個畫布上做畫,豈不是很合適?
因此總結起來,理想視口說白了就是理想的佈局視口,經過<meta name="viewport" content="width=device-width, initial-scale=1">來設置。app
<meta> 元素可提供有關頁面的元信息,不會顯示在頁面上,能夠用來告訴瀏覽器怎樣解析頁面。<meta>能夠設置的東西不少,但這裏只講vieport,它是全部移動適配方案的基礎。
首先meta viewport的設置格式是<meta name="viewport" content="name=value,name=value",其中name的值可設爲:
雖然只有五個值,但仍有一些值得注意的點:
根據公式縮放係數 = 理想視口寬度 / 視覺視口寬度 ,若是設置了initial-scale好比爲0.5,那麼以iPhone6爲例,iPhone6的設備寬度是375px,即理想視口寬度也爲375px,因此視覺視口寬度 = 375px(理想視口寬度)/ 0.5(縮放係數)。很明顯設置了initial-scale就至關於初始化了視覺視口,並且會將佈局視口初始化爲這個視覺視口的值。
上面說到設置了initial-scale至關於初始化了視覺視口和佈局視口,但width用於指定佈局視口的大小,那麼一塊兒設置的話聽誰的呢?
仍是以iPhone6爲例,它的尺寸是667(h) * 375(w),若是設置<meta name="viewport" content="width=400, initial-scale=1">
,執行一下console.log(`佈局視口: ${document.documentElement.clientWidth}; 視覺視口: ${window.innerWidth}`)
會獲得「佈局視口: 400; 視覺視口: 400」。
這時候旋轉一下設備,這時尺寸變成了667(w) * 375(h),再執行一下console.log(`佈局視口: ${document.documentElement.clientWidth}; 視覺視口: ${window.innerWidth}`)
會獲得「佈局視口: 667; 視覺視口: 667」。
結論是:width與initial-scale都會初始化佈局視口,但瀏覽器會取其最大值。
這時候再看回<meta name="viewport" content="width=device-width, initial-scale=1">,明明width=device-width和initial-scale=1都是去初始化佈局視口成理想的佈局視口,只寫其中一個不就完了嘛,爲何要兩個都一塊兒寫呢?由於有的瀏覽器只設置其中一個,不能保證理想視口的尺寸能隨着屏幕的旋轉而正確改變,因此兩個一塊兒寫只是爲了解決兼容性問題。
上面說了不少理論知識,其實就是爲了能有一套方案舒服地還原移動端設計圖,作出一個專爲移動端訪問的頁面。
這裏的圖片問題是指高清/Retina屏下圖片會顯示得比較模糊,這是由於咱們平時使用的圖片大多數是png、jpg這樣格式的圖片,它們稱做是位圖圖像(bitmap),是由一個個像素點構成,縮放會失真。上面講像素的時候說過,這種高清/Retina屏DPR大於一,則一像素橫跨了多個設備像素,而位圖圖像須要一個像素點對應一個設備像素才清晰。因此假設一張100 x 100的圖片放在普通屏上看是清晰的,放到高清/Retina屏上就會顯得比較模糊,那是由於原本100 x 100的圖片在普通屏上圖片像素與設備像素一一對應,而到了高清/Retina屏上一個圖片像素卻要對應多個設備像素,這樣一來看起來圖片就比較模糊。
如圖所示,若是一個圖片像素要對應多個設備像素的話,那這些設備像素只能顯示成跟這個圖片像素差很少的顏色,致使看起來會模糊。
既然知道了問題產生的緣由,那解決方法也很簡單,位圖圖像須要一個像素點對應一個設備像素才清晰嘛,那就原本是100 x 100的圖片在DPR爲1的屏幕上顯示清晰,在DPR爲2的屏幕上顯示模糊,那就在DPR爲2的屏幕上放200 x 200的圖好了,這樣就一一對應了。
「你看看設計圖這根線是很細的,爲何你實現出來那麼粗,看起來很劣質的感受。」
沒道理呀,設計圖量的是1px,css寫的也是1px,怎麼會粗了呢?通常設計師出圖的時候,都會按照一個尺寸做爲標準來出圖,好比按照iPhone6的尺寸出圖,就是一張750px寬的設計圖,這個750px其實就是iPhone6的設備像素,在測量設計圖時量到的1px實際上是1設備像素,而當咱們設置<meta name="viewport" content="width=device-width, initial-scale=1">時,佈局視口等於理想視口等於375px,而且因爲iPhone6的DPR爲2,寫css時的1px對應的是2設備像素,因此看起來會粗一點。
那麼只要寫0.5px就是對應1設備像素了嘛。是的,道理是這麼說,可是不少瀏覽器並不支持0.5px的寫法,致使顯示不出來,但沒關係,網上不少方法解決這個問題的方法就不細說了,這裏只是講清楚1px邊框問題產生的緣由。
由於PC端屏幕通常都會比設計圖尺寸要大,因此只須要居中固定一個內容區用於顯示設計圖的內容,其他多出的地方留白便可。而移動端屏幕有大有小,設計圖通常會以一款機型爲標準來出圖,好比說iPhone6的尺寸,若是不經處理直接量設計圖就開幹會出現什麼問題呢?
(從左到右爲iPhone四、iPhone六、iPhone plus)
能夠看到以iPhone6爲標準出的設計圖測量出來350px x 350px的元素在iPhone6上寫width: 350px;height: 350px;
是剛恰好的,左右的間隙各有10px,但小一點的屏幕iPhone4橫向滾動條都出來了,而plus左右間隙明顯比10px大不少,這樣一來不一樣尺寸的屏幕出來的效果跟設計圖的效果就會有不一樣程度的出入,這並非咱們想要的,咱們想要的是不一樣尺寸的屏幕顯示的效果與設計圖比例是一致的。
既然想要的是不一樣屏幕尺寸顯示的比例與設計圖一致,那麼顯然適配方案就是等比縮放。
(如下代碼都是爲了講述原理,沒有過多的細節考慮與測試,不能用於生產環境)
說到縮放,首先想到的固然是initial-scale。回想一下initial-scale的做用:設置了initial-scale就至關於初始化了視覺視口,並且會將佈局視口初始化爲這個視覺視口的值。那麼咱們是否是能夠以設計圖爲基準等比縮放佈局視口從而適配呢?
<script> const scale = window.screen.width / 750 document.write(`<meta name="viewport" content="initial-scale=${scale}">`) </script>
這種方式進行適配優勢是簡單粗暴,缺點是太簡單粗暴了,由於viewport的設置是影響全局的,這樣一來雖然能夠直接將設計圖量得的尺寸寫到css上,但若是有一些須要地方不須要等比縮放而須要設置固定尺寸,好比要求在不一樣尺寸屏幕上顯示固定大小的文字,或者你引進了一個庫,裏面的有樣式你也不知道人家是按照怎樣的適配方案進行適配的,那麼到了你的項目裏因爲全局的viewport縮放,可能會影響到這個庫的顯示效果。
不一樣於px是固定尺寸單位,rem是相對單位,相對於html標籤字體大小的單位。好比html標籤的font-size爲100px,那麼1rem就等於100px。藉助rem這個相對單位咱們一樣能夠達到等比縮放的效果。
<meta name="viewport" content="width=device-width, initial-scale=1">
document.documentElement.style.fontSize = `${document.documentElement.clientWidth / 7.5}px
`使用這個方案,咱們只對須要等比縮放的元素使用rem,而要求固定尺寸的地方使用px便可,這樣一來相對於viewport方案來講就比較靈活,能夠按需使用而不是一刀切。不過這種方案寫css的時候可能會沒那麼直觀,成本可能會高一點點,可是藉助構建工具或者less/sass能夠解決,畢竟如今應該不多項目不使用這些工具的了吧。
這裏所說的增強版rem方案其實就是手淘的Flexible方案(也相似移動端高清、多屏適配方案),究竟增強了什麼呢?那就是,經過設置viewport進而全局解決1px邊框問題。
既然要經過設置viewport來解決1px邊框問題,那設置這個viewport的方式確定內有乾坤:
if (!dpr && !scale) { var isAndroid = win.navigator.appVersion.match(/android/gi); var isIPhone = win.navigator.appVersion.match(/iphone/gi); var devicePixelRatio = win.devicePixelRatio; if (isIPhone) { if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) { dpr = 3; } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){ dpr = 2; } else { dpr = 1; } } else { // 其餘設備下,仍舊使用1倍的方案 dpr = 1; } scale = 1 / dpr; }
得出的scale用於設置viewport的縮放document.write(`<meta name="viewport" content="initial-scale=${scale}">`)
,這樣一來,對於Retina屏將viewport縮放爲1 / dpr最終產生的效果是,1px css像素嚴格等於1px 設備像素,由此解決了1px邊框問題。那爲何只對iPhone進行縮放呢?請看大漠老師的文章再談Retina下1px的解決方案。
其餘與rem相關的配置與上面的rem方案相似,這裏就再也不展開說了。
這個增強版rem方案最大的優點是解決了1px邊框問題,但由此也進行了viewport的縮放,仍然會面臨着上面說的viewport方案涉及到的一些影響,爲此該方案會經過給html設置data-dpr
document.documentElement.setAttribute('data-dpr', dpr)
從而寫css的時候能夠針對不一樣的dpr固定設置尺寸:
.test { width: 1rem; height: 2rem; font-size: 12px; } [data-dpr="2"] .test { font-size: 13px; } [data-dpr="3"] .test { font-size: 14px; }
vw也是一個相對單位,它相對的是佈局視口,1vw就是1%的佈局視口寬度。其實rem方案就是在模擬vw,來看看使用vw怎麼作。
rem方案有的優點vw也有,並且也不會像rem那麼繞,但就是兼容性不夠rem好,長遠來看vw最後會接棒rem做爲移動適配的主力,由於它生來就幹這個事情呢。
沒有銀彈。全局viewport縮放方案很粗暴?但對於要求不高也不須要兼顧固定尺寸的頁面,上來就全局縮放,拿起設計稿就能夠寫代碼了。要求高又想靈活,還會怕構建的那一點點麻煩嗎?rem方案走起。兼容性不須要考慮,那vw方案直白又優雅不試試看嗎?方案沒有優劣之分只有合適與否。最後,若是有說得不對的地方,還望指正。