移動端經常會有這樣的展示頁面,頁面中有一些獨立的元素,並且伴隨各種交互效果、轉場動畫。
針對頁面佈局的話,大概有這樣的需求:
- 不滾動頁面,所有元素均在一屏之類展示
- 像圖片那樣縮放元素---寬度改變時高度按比例變化
- 背景圖需要把瀏覽器窗口撐滿
- 往往項目是獨立的,需要快速上線,開發週期短
這種形式頁面在正常的文檔流中佈局顯然不太合適,比較傳統的做法是這樣:
- 佈局以絕對定位爲主
- 用百分比配合px控制寬高、位置
- 背景圖用background-size:cover撐滿
- 用css媒體查詢來適應多種屏幕
- css搞不定的請js幫忙
佈局一個單屏展示頁
比如這樣一個640*960的設計稿,分背景和按鈕兩層,要求背景全屏(可以不顯示全)按鈕定位在精確的位置上。
傳統做法
一般會把背景圖用background-size:cover顯示在一個寬高撐滿屏幕的容器上以適應各種屏幕, 這個按鈕怎麼定位呢,一般是會這樣做吧position:absolute;bottom:18%;
可以看到iPhone4中按鈕錯位,原因是當屏幕尺寸改變時,背景圖與按鈕的尺寸與位置會遵循不同的規則變化。這個例子中的效果貌似還能接受,可有些場景對定位的要求可能比這更高。
解決這個問題一般會用媒體查詢針對屏幕做適配吧
@media screen and (max-height: 500px) {
.btn{
bottom:12%;
}
}
Demo 在這裏 http://jsbin.com/zugape/edit
如果頁面中元素多、定位要求精確,這種做法是比較暴力的。下面是支付寶十年曬單頁面中針對不同尺寸屏幕做的適配
/* <=640 */
@media screen and (min-width: 319px) and (max-width: 321px) {
...
}
/* 720 */
@media screen and (min-width: 359px) and (max-width: 361px) {
...
}
/* 1024*768, 2056*1536 */
@media screen and (min-width: 383px) and (max-width: 385px) {
...
}
/* 800 */
@media screen and (min-width: 399px) and (max-width: 401px) {
...
}
/* 1080 */
@media screen and (min-width: 539px) and (max-width: 541px) {
...
}
/* 1200 */
@media screen and (min-width: 599px) and (max-width: 601px) {
...
}
/* 1440 */
@media screen and (min-width: 719px) and (max-width: 721px) {
...
}
/* 1600 */
@media screen and (min-width: 799px) and (max-width: 801px) {
...
}
/* for small screen */
@media screen and (max-height: 500px) {
...
}
/* for iPhone6 Plus */
@media only screen and (device-width: 414px) and (-webkit-min-device-pixel-ratio: 3),
only screen and (device-width: 375px) and (-webkit-min-device-pixel-ratio: 3){
...
}
總結下這種方式的缺點:
- 要把設計稿中的PX在大腦中換算成%
- 百分比控制寬高是相對於父元素,計算繁瑣
- 用媒體查詢針對各種尺寸的屏幕做兼容工作繁瑣
圖片的自適應規則
一張圖片如果給他設置<img src="pic" style="max-width: 100%;max-height:100%” />
的話,無論視口大小,他都會顯示在視口裏面,如圖
類似的還有圖片作爲背景顯示時的background-size屬性。
background-size: contain:縮放圖像的最大值,其寬度和高度都能放入內容區域
background-size: cover:縮放圖像的最小值,其寬度和高度都能放入內容區域
當你改變圖片的寬度或高度時,圖片的另一邊會自動按比例縮放,如果div有這種能力多好啊!
vw、vh--爲移動而生
vw、vh是CSS3中出現的新的長度單位,vw 相對於視窗的寬度,視窗寬度是100vw;vh 相對於視窗的高度,視窗高度是100vh。
其中視窗指的是瀏覽器實際顯示區域,即window.innerWidth/window.innerHeight的大小。
如 想一個元素的寬是視口寬的一半,只需設置width:50vw
與百分比的異同
乍一看好像和百分比類似,並沒發現多少優勢,在進行一些嘗試之後我總結了一下他們的異同
百分比 | vw、vh |
---|---|
相對父級元素 | 相對視口 |
橫向/縱向相對父級橫向/縱向 | vw/vh相對視口寬/高(即寫vh就是相對視口高、寫vw就是相對視口寬,不必橫向與橫向縱向與縱向對應) |
對字體大小無效 | 對字體大小有效 |
Tips:其實margin-top/bottom以百分比爲單位時他的參照對象並不是父元素的height而是width。詳情閱讀 doyoe margin系列文章
這些特性可以實現一些之前用CSS難以解決的問題。
相對視口100%高度
由於百分比高度是相對父級的,之前如果想讓文檔流中的某容器相對視口高度100%,只用css的話得從html>body>element一層層下來都設置height:100%
,而現在只需給元素設置height:100vh
。
height 100% Demo http://jsbin.com/risegu/edit
響應式文字 && 像圖片一樣佈局
先上Demo
百分比方式 http://jsbin.com/vemudo/edit
vw方式 http://jsbin.com/huwido/edit
這兩個Demo建議把瀏覽器模擬成移動設備的樣子,拉伸瀏覽器窗口寬度觀察效果。
可以看到當瀏覽器寬度改變時,百分比Demo中只有元素的寬度發生了變化,而統一使用vw做單位的Demo中文字的大小、元素的高度與寬度均按縮放比例發生了變化,這正是我們想要的“像圖片一樣佈局”的效果。
vmin 與 vmax
還有vw、vh延生出來的單位vmin、vmax。
vmin:關於視口高度和寬度兩者的最小值
vmax:關於視口高度和寬度兩者的最大值
我覺得這是一組神奇的單位,但是暫時對這個使用場景的理解有限,能想到的場景是一個正方形,height:100vmin;width:100vmin
時,得到的是這個視口中能顯示滿的一個最大正方形(http://jsbin.com/baheko);height:100vmax;width:100vmax
時得到的是能把這個視口全部顯示滿的最小正方形(http://jsbin.com/tamasi)。
然後在codepen上找到這樣一個demo,他讓16:9的視頻窗口在視口中自適應http://codepen.io/CreativeJuiz/pen/KzkgL
其他適用場景有待發掘
vw、vh的問題
vw vh 在桌面端瀏覽器兼容性已經是比較好了,不過在移動端貌似還需要等待,國產安卓瀏覽器的話UC到現在都不支持真不讓人省心。
另外對張鑫旭的視區相關單位vw, vh..簡介以及可實際應用場景 中他的結論:vw vh只適用於非定位元素;vh高度值的內部元素不支持百分比%高度 有些疑問,我在測試中並未發現這樣的情況,這篇文章寫於2012年,不知道是不是這樣的情況只發生在當時的瀏覽器。Demo http://jsbin.com/dineka/edit
rem方案
回頭來看我們的佈局,vw vh的方案因爲兼容性問題只能放棄,重新分析下我們的需求:
其實我們需要那麼一個全局的相對單位...
我們可以通過修改這個單位來控制整個佈局...
我們可以通過判斷屏幕的比例來縮放整個頁面以展示全部內容...
然後想到類似手機淘寶的動態rem的方案:所有佈局元素單位使用rem,由於rem是相對於根元素的,所以不同屏幕按一定規則控制的font-size 即可兼容各屏幕。
var root = document.getElementsByTagName('html')[0],
NATIVE_W = 640;
function updateSize() {
var w = window.innerWidth;
var cw = w / (NATIVE_W / 100);
root.style.fontSize = cw + 'px';
}
window.onload = updateSize;
window.onresize = updateSize;
這樣的頁面一般會有設計稿的,我們需要設置rem和設計稿中px的轉換比例。
1rem = 1px 這樣固然方便,但是有些瀏覽器有最小字體的限制。
所以我想讓 1rem = 100px。
我們的設計稿一般爲640 x 960,這個尺寸是針對iPhone4,設備像素比是2,那麼我們實際需要html的font-size爲50px。
這樣的話假如設計稿中一個元素尺寸爲240px x 200px,
那麼這個元素需要在屏寬爲320px的設備上顯示的尺寸是120px x 100px,而我們需要他在佈局時設置的寬高是2.4rem x 2rem。
設計稿寬640px 也就是整個頁面應爲 6.4rem。 所以對應不同寬度的設備時用window.innerWidth(視口寬) / 6.4 得到的就是對應此設備的font-size。
佈局時,把設計稿上的尺寸除以100然後把px單位換成rem。
怎麼解決比較短的屏幕顯示不完全的情況呢,可以先判斷一下視口的比例,當視口高寬比低於設計稿寬高比時,縮小html節點的font-size這樣就能把內容顯示全了
var root = document.getElementsByTagName('html')[0],
NATIVE_W = 640,
NATIVE_H = 960;
function htmlSize() {
var cw = 50,
w = window.innerWidth,
h = window.innerHeight;
if ((w / h) > (NATIVE_W / NATIVE_H)) {
cw = h / (NATIVE_H / 100);
} else {
cw = w / (NATIVE_W / 100);
}
root.style.fontSize = cw + 'px';
}
window.onload = htmlSize;
window.onresize = htmlSize;
Demo http://jsbin.com/rupole/edit
可以試下縮小、拉大窗口布局也會像圖片那樣相應變化,並且無論窗口是什麼樣的尺寸,元素都會出現在比較合理的位置,當然,一些特殊情況仍然要用媒體查詢來適配。
然後在佈局時,其實只要把這段js放在頁面中,就rem當成px來用就行了,不需要多考慮啥,這個方案的優點就是構建頁面成本低,跨屏顯示效果也能滿意。
calc
另外有些元素我們想他垂直方向上針對中心定位而不是頂部或底部,比如想讓一個小球居中之前大概是這樣:
#cir{
width:200px;
height:200px;
border-radius:100px;
background:#888;
position:absolute;
left:50%;
top:50%;
margin-left:-100px;
margin-top:-100px;
}
其實CSS3中有這樣一個表達式:calc()(CSS里居然有這玩意,我第一次看到時懷疑是不是SASS或LESS裏的),他可以給元素的border、margin、pading、font-size和width等屬性動態計算值,例如:top:calc(50% - 1rem)
,這在這種展示頁面的佈局中是相當有用的,上面的css可以改寫成:
#cir{
width:200px;
height:200px;
border-radius:100px;
background:#888;
position:absolute;
left:calc(50% - 100px);
top:calc(50% - 100px);
}
Demo http://jsbin.com/gasato/edit
關於更多calc的介紹建議閱讀大漠的這篇博文 http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html
遺憾的是這玩意在桌面端瀏覽器中支持的已經比較好了,不過在移動端的表現令人堪憂,安卓原生瀏覽器從4.4開始支持,而最新版的安卓UC仍然不支持。
原文地址:https://github.com/xiangpaopao/blog/issues/9