工做之後,大部分的業務工做都是基於移動端H5的,開發過程當中學習了不少東西,遇到過許多問題,諸如rem\em\css px\device px等,本文純屬我的的概括總結,若有問題,請指出親噴~css
本文主要是講解移動端的響應式佈局, 可是在真正進入以前, 先了解一些概念。html
一般在PC端上面,咱們並不須要考慮設備像素和css像素
之間的差異,以目前的pc來看,1個設備像素一般等於1個css像素
。可使用screen.width/height
來獲取咱們屏幕的寬高設備像素。前端
screen.width // 1920
screen.height // 1080
複製代碼
若是你給一個元素的寬度爲width: 192px;
那麼你的屏幕上(假設你的屏幕寬度像素爲1920)能夠在一行上顯示10個該元素。原理則是由於咱們的PC中1個設備像素等於1個css像素。git
當用戶放大或者縮小屏幕時(按住ctrl+滾動鼠標輪,也就是改變zoom值
),則有所不一樣。此時,咱們的設備像素仍然沒有改變,仍是1920*1080,css像素的數量也沒有改變,可是css像素大小變了
。 假設放大到200%, 那麼1個css像素就等於兩個設備像素
, 以此類推。github
如下是引用ppk大神
的三張圖片, 下面深藍色爲設備像素, 上面淺藍色爲css像素web
正常狀況下:
api
縮小時:
瀏覽器
放大時:
bash
screen.width/heihgt
取的是屏幕的寬高,單位是是css像素。ide
window.innerWidth/innerHeight
取的是網頁區域的寬高, 單位是css像素。
當你改變zoom值時, screen不會改變, innerWidth/height會改變
。
viewport
是一個限制html
元素的功能, 能夠理解爲html
元素的上一層元素。聽起來有點難以理解, 下面講一個例子:
假設, 你給某個div
元素設置了width:50%
的樣式後, 當你縮小放大瀏覽器的時候,你會發現div元素老是佔據了50%的寬度,咱們知道,寬度百分比是依賴它的包裹元素
(假設是body), 那麼問題就回到了body的寬度身上。一般在沒有設置寬度的狀況下,全部塊級元素都佔用其父級寬度的100%
。因此body和html元素同樣寬。那麼html元素有多寬呢,默認狀況下它和瀏覽器窗口同樣寬
,這也就是爲何div老是佔據瀏覽器寬度的50%,而html元素則是受限於viewport(和viewport佔據同樣的寬度),換句話說,viewport徹底等於瀏覽器窗口,並且它不是HTML語言元素,因此你沒法經過使用css對其進行影響
。
咱們能夠經過document.documentElement.clientWidth/clientHeight
來獲取viewport的寬高, 它的單位是css像素
。
上面二者都可以獲取網頁區域大小, 可是他們之間仍是有區別的。 前者不包含滾動條, 後者包含
。
咱們能夠經過document.documentElement.offsetWidth/offsetHeight
來獲取html元素的寬高, 它的單位是css像素
。
event的三對屬性:
媒體查詢:
@media all and (max-width: 400px)
複製代碼
@media all and (max-device-width: 400px)
複製代碼
在默認狀況下,通常來說,移動設備上的viewport都是要大於瀏覽器可視區域的,這是由於考慮到移動設備的分辨率相對於桌面電腦來講都比較小,因此爲了能在移動設備上正常顯示那些傳統的爲桌面瀏覽器設計的網站,移動設備上的瀏覽器都會把本身默認的viewport設爲980px或1024px(也多是其它值,這個是由設備本身決定的),但帶來的後果就是瀏覽器會出現橫向滾動條,由於瀏覽器可視區域的寬度是比這個默認的viewport的寬度要小的。下圖列出了一些設備上瀏覽器的默認viewport的寬度。
如下是關於各瀏覽器的viewport
前面介紹了viewport的概念, 可是在移動端的時候, viewport並不那麼容易理解, ppk在移動端提出了三個viewport
的概念。
Layout viewport
也就是佈局viewport,即默認的瀏覽器viewport , 而且能夠經過document.documentElement.clientWidth
來獲取。
圖片引用自深刻理解viewport
visual viewport
layout viewport 的寬度是默認的瀏覽器viewport,因此咱們還須要一個viewport來表明可視區域的大小,ppk把這個viewport叫作 visual viewport。ppk說visual viewport的寬度能夠經過window.innerWidth
來獲取, 然而我獲取的時候發現獲得的值是網頁區域的值。
圖片引用自深刻理解viewport
ideal viewport
有了兩個viewport並不ok, 由於咱們既不想讓用戶滾動滾動條來瀏覽咱們的網頁,也不想用戶盯着縮小了的pc網頁瀏覽, 因此有了第三個viewport。 所謂的ideal viewport則是當layout viewport等於屏幕的寬度, 如ip6,它的ideal viewport就是375px
。
開發過h5的應該都知道, 咱們常常會把下面這句代碼複製到head標籤中:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
複製代碼
它的做用其實就是設置了ideal viewport
。如下是它的6個屬性:
key | value |
---|---|
width | 設置layout viewport 的寬度,爲一個正整數,或字符串"width-device" |
initial-scale | 設置頁面的初始縮放值,爲一個數字,能夠帶小數 |
minimum-scale | 容許用戶的最小縮放值,爲一個數字,能夠帶小數 |
maximum-scale | 容許用戶的最大縮放值,爲一個數字,能夠帶小數 |
height | 設置layout viewport 的高度,這個屬性對咱們並不重要,不多使用 |
user-scalable | 是否容許用戶進行縮放,值爲"no"或"yes", no 表明不容許,yes表明容許 |
那麼若是咱們想設置ideal viewport, 只須要把width設置成width-device或者把initial-scale設置成1.0
就能夠了。
前者比較容易理解, 後者設置成1就能夠是爲何? 首先要理解設置成1.0就是意味着沒有縮放,而這樣卻能夠達到ideal viewport的效果, 那麼很明顯, 縮放是相對於 ideal viewport來進行縮放的
,當對ideal viewport進行100%的縮放,也就是縮放值爲1的時候,不就獲得了 ideal viewport。
若是兩個屬性都能設置ideal viewport, 那麼當兩個屬性衝突時怎麼解決?
遇到這種狀況時,
瀏覽器會取它們兩個中較大的那個值
。例如,當width=400,ideal viewport的寬度爲320時,取的是400;當width=400, ideal viewport的寬度爲480時,取的是ideal viewport的寬度。
在移動端中, 1個css像素並不等於1個設備像素
, 而是取決於設備像素比(物理像素(設備像素)/獨立像素(css像素))
,像Iphone的Retina屏幕, 就有2倍屏(ip6s)、3倍屏(ip6 plus), 也就是設備像素比的值分別是2和3,即1個css像素等同於4個設備像素或者9個, 如圖:
而且, 咱們能夠經過window.devicePixelRatio
來獲取設備像素比dpr。
問題的產生
公司的設計大佬一般給的設計稿是基於ip6s的, 也就是750px(i6s的屏幕是375px,並且是上面說的兩倍屏,因此有750個物理像素)。假設設計稿上面有個1px的border
,咱們一般直接這樣寫:
border {
border: 1px solid #ccc;
}
複製代碼
而後設計審覈的時候就被打回來了, 由於設計以爲變大了,也就是他以爲是2px的線
了。
究其緣由,是由於設計稿是750px, 裏面的1px實際上在真機只有0.5px
,因此就有了著名的1px問題。
問題的解決
1.直接使用0.5px
在iOS8下,蘋果系列都已經支持0.5px了,那麼意味着在devicePixelRatio = 2時,咱們能夠藉助媒體查詢來處理:著做權歸做者全部。
@media (-webkit-min-device-pixel-ratio: 2) {
.border {
border-width: 0.5px
}
}
複製代碼
這種使用簡單,可是兼容性不太好。
2.使用border-image或者background
也就是拿一張圖片,一半透明,一半是咱們想要的顏色,而後填充上去, 具體的例子就不講了, 這種基本沒啥人會用, 改個顏色還要修改圖片,太麻煩了。
3.box-shadow
.box-shadow-1px {
box-shadow: inset 0px -1px 1px -1px #c8c7cc;
}
複製代碼
這種顏色有陰影, 估計過不了設計大佬的那關。
4.PostCSS的插件postcss-write-svg
直接藉助插件幫助咱們實現, 其實也就是postcss幫咱們生成圖片而已。
@svg 1px-border {
height: 2px;
@rect {
fill: var(--color, black); width: 100%; height: 50%;
}
}
.example { border: 1px solid transparent;
border-image: svg(1px-border param(--color #00b1ff)) 2 2 stretch;
}
複製代碼
最後Postcss會把對應的css編譯出來, 這種兼容性好, 就是依賴於插件。
.example { border: 1px solid transparent;
border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch
}
複製代碼
5.僞類 + transform 實現
原理是把原先元素的 border 去掉,而後利用 :before 或者 :after 重作 border ,並 transform 的 scale 縮小一半,原先的元素相對定位,新作的 border 絕對定位。
.scale-1px{
position: relative;
border:none;
}
.scale-1px:after{
content: '';
position: absolute;
bottom: 0;
background: #000;
width: 100%;
height: 1px;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
}
複製代碼
這種兼容好, 可是會和僞類衝突, 也是我司採用的方式。
6.縮小viewport
原理是使用meta標籤中的viewport, 也就是上面所說的設置viewport, 將整個頁面縮小0.5倍, 這個主要是麻煩在其餘的元素也要跟着放大一倍再縮小, 爲了這個小問題而這樣, 彷佛有點得不償失。
然而, 淘寶的flexible方案(rem佈局,見下文)幫咱們搞定了這個問題。
上面提到了flexible和rem的佈局方案, 在剛推出的時候, 確實很火, 公司的一些項目目前仍然是採用該方案, 這裏簡單的說下其原理。
px、em、rem
/* 做用於根元素,相對於原始大小(16px),因此html的font-size爲32px*/
html {font-size: 2rem}
/* 做用於非根元素,相對於根元素字體大小,因此爲64px */
p {font-size: 2rem}
複製代碼
flexible rem佈局原理
flexible rem佈局原理便是把設計稿等比寬的切成100份
, 假設每份的單位是x
, 那麼咱們在佈局的時候就能夠以x
爲單位, 將設計稿等比例的放大縮小到對應的屏幕了
,這樣就不用爲各個屏幕作適配。
然而像上面所說的x是不存在的, 不過好在咱們有rem, 只要咱們將rem設置成1x
,那麼開發過程當中,不就達到咱們的目的了嗎?
如何將rem設成1x呢? 回想一下, 咱們是否是能取得viewpor
t的寬度(document.documentElement.clientWidth
),咱們能取得設備像素比
(window.devicePixelRatio
), 咱們可以設置html
的樣式(html.style.fontSize = '...'
), 因此簡單的實現方案就有了
document.documentElement.style.fontSize = document.documentElement.clientWidth / 100 + 'px';
複製代碼
固然,flexible並不這麼簡單,它還對於不一樣dpr作了處理, 它幫你處理了2倍屏、3倍屏等狀況,經過設置viewport的縮放比,這也就是上面所說的0.5px處理方案之一, 具體的這裏再也不贅述, 有興趣的同窗能夠看看原文。
最後,移動端 iOS 8 以上以及 Android 4.4 以上已經有了vw\vh
單位, 1vw\1vh至關於viewport的百分之一寬/高
,也就是咱們上面所說的x單位, 若是你的手機支持該api, 也可使用該單位方案。
依稀記得, 某次使用了rem處理活動頁的時候, 被設計大佬駁回了。
大佬認爲, 當用戶使用更大的屏幕的時候, 他應該能看到更多的內容
, 並且設計稿被放大或者縮小的話, 會失去他原來的感受
。
因此, 對於rem方案其實可能已經不太適合當前的狀況了, 畢竟使用媒體查詢和px以及em就能解決各類響應式問題, 雖然效率會比較低下, 而關於這個, 也剛好看到了有人在知乎上提了這麼個問題, 有興趣的能夠前去圍觀。
本文可能是概念上的,也參考了許多文章,要真正理解還須要多參考實際項目。
移動前端開發之viewport的深刻理解
A tale of two viewports — part one
A tale of two viewports — part two
Meta viewport
7 種方法解決移動端 Retina 屏幕 1px 邊框問題
再談Retina下1px的解決方案
vh,vw單位你知道多少?
Rem佈局的原理解析