彈性盒模型Flex指南

彈性盒模型Flex指南

Web layout 是Web UI中的基礎(chǔ)架構(gòu), 重要性不言而喻. 傳統(tǒng)的盒模型, 借助display, position, float 屬性應(yīng)對普通布局游刃有余, 但針對復(fù)雜的或自適應(yīng)布局, 常常捉襟見肘. 比如垂直居中, 就是一個老大難的問題, 借助flex彈性盒模型, 兩行代碼就可以優(yōu)雅的實現(xiàn)之. (該方法曾在?16種方法實現(xiàn)水平居中垂直居中?一文中提到). 當(dāng)然, 本次我們不會只討論垂直居中的問題, 我將努力盡可能的還原flex的應(yīng)用場景. 本文后面還將講解flex子項目壓縮比計算, 多層flex嵌套的常見問題.

Flex

Flex即彈性盒模型, 該布局方案由W3C于2009年提出. 此后, Flex方案便歷經(jīng)v2009, v2011, v2012, v2014, v2015, v2016等版本, 最近方案是2016年5月26日起草的?CSS Flexible Box Layout Module Level 1.

兼容性

首先, 我們來回顧下如今PC端的兼容性(以下為完全兼容版本).

IEEdgeFirefoxChromeSafariOpera

-12+28+21+6.1+12.1+

以上, IE10+僅支持2012版W3C的flex語法, 且存在較多已知的bug, 此時使用flex布局需謹(jǐn)慎.

Chrome瀏覽器v21~v28版本需要添加 "-webkit-" 前綴.

Safari瀏覽器v6.1~v8版本需要添加 "-webkit-" 前綴.

Opera瀏覽器v15~v16版本需要添加 "-webkit-" 前綴.

因此, 看到一些sass編譯后的css文件中帶有 "-webkit-" 前綴無需驚慌.

平時開發(fā)時最為擔(dān)心的便是移動端兼容性, 請看:

IOS SafariOpera miniAndroidAndroid ChromeUC微信

7.1+√4.4+55-當(dāng)前支持

微信當(dāng)前版本已支持flex.

UC不對外提供webview內(nèi)核, 除去一些H5app的應(yīng)用, 各種分享頁基本(常在微信下打開)基本不需要擔(dān)心對其兼容性, 實在需要實現(xiàn), UC還是支持老版本的彈性盒子的, 可以優(yōu)雅降級. 可見, Android4.4以上基本可以安心使用flex.

Autoprefixer

強(qiáng)記各種瀏覽器的前綴是沒有必要的, 因為autoprefixer該做的, 都幫我們做了. 因此建議嘗試下以下三個插件之一.

grunt-autoprefixer

gulp-autoprefixer

PostCSS-autoprefixer

優(yōu)勢

Flex布局使得子項目能夠"彈性"的改變其高寬, 自由填充容器剩余空間, 以適應(yīng)容器變大, 或者壓縮子項目自身, 以適應(yīng)容器變小; 同時還可以方便的調(diào)節(jié)子項目方向和順序. flex常用于高寬需要自適應(yīng), 或子項目大小成比例, 或水平垂直對齊等場景.

概念鋪墊

Flex彈性盒模型里, 有容器和項目之分. 設(shè)置display:flex的為容器, 容器內(nèi)的元素稱作它的子項目, 容器有容器的一套屬性, 子項目有子項目的另一套屬性. (可以這么理解: father作為彈性盒子, 制定行為規(guī)范, son享受盒子的便利, 按照規(guī)范劃分各自的"轄區(qū)").

以下圖片摘自大漠的一個完整的Flexbox指南文中.

father制定的規(guī)范, 基于兩個方向 — 水平和垂直.

水平方向的稱之為主軸(main axis), 垂直方向的稱之為交叉軸(cross axis).

主軸起始位置, 叫做main start, 末尾位置叫做main end;

交叉軸起始位置, 叫做cross start, 末尾位置叫做cross end.

子項目在主軸上所占的寬(高)度, 叫做main size, 在交叉軸上所占的高(寬)度, 叫做cross size.

屬性

display: flex | inline-flex;(元素將升級為彈性盒子). 前者容器升級為塊級盒子, 后者容器將升級為行內(nèi)盒子. 元素采用flex布局以后, 子元素的float, clear, vertical-align屬性都將失效.

容器屬性

容器具有以下6個屬性.

flex-direction 指定主軸的方向.

flex-direction的值描述

row(默認(rèn))指定主軸水平, 子項目從左至右排列?

row-reverse指定主軸水平, 子項目從右至左排列??

column指定主軸垂直, 子項目從上至下排列??

column-reverse指定主軸垂直, 子項目從下至上排列??

flex-wrap 指定如何換行.

flex-wrap的值描述

nowrap(默認(rèn))默認(rèn)不換行

wrap正常換行

wrap-reverse換行, 且前面的行在底部

flex-flow 它是flex-direction 和 flex-wrap的簡寫形式, 默認(rèn)值為row nowrap.

justify-content 指定主軸上子項目的對齊方式.(通常為水平方向?qū)R方式)

justify-content的值描述(子項目--主軸方向)

flex-start(默認(rèn))子項目起始位置與main start位置對齊

flex-end子項目末尾位置與main end位置對齊

center在主軸方向居中于容器

space-between與交叉軸兩端對齊, 子項目之間的間隔全部相等

space-around子項目兩側(cè)的距離相等, 它們之間的距離兩倍于它們與主軸起始或末尾位置的距離.

align-items 指定交叉軸上子項目的對齊方式.(通常為垂直方向?qū)R方式)

align-items的值描述(子項目—交叉軸方向)

flex-start子項目起始位置與cross start位置對齊

flex-end子項目末尾位置與cross end位置對齊

center在交叉軸方向居中于容器

baseline第一行文字的基線對齊

stretch(默認(rèn))高度未定(或auto)時, 將占滿容器的高度

align-content 指定多根主軸的對齊方式. 若只有一根主軸, 則無效.

align-content的值描述(子項目)

flex-start頂部與cross start位置對齊

flex-end底部與cross end位置對齊

center在交叉軸方向居中于容器

space-between與交叉軸兩端對齊, 間隔全部相等

space-around子項目兩側(cè)的距離相等, 它們之間的距離兩倍于它們與主軸起始或末尾位置的距離.

stretch(默認(rèn))多根主軸上的子項目充滿交叉軸

子項目屬性

子項目具有以下6個屬性.

flex-grow?指定子項目的放大比例, 默認(rèn)為0(即不放大). 該屬性可取值為任何正整數(shù). 假設(shè)各個子項目的放大比例之和為n, 那么容器內(nèi)剩余的空間將分配n份, 每個子項目各自分到x/n份. (x為該子項目的放大比例)

flex-shrink?指定子項目的縮小比例, 默認(rèn)為1. 設(shè)置為0時, 空間不足該子項目將不縮小. 我們知道,?容器的縮小總寬度=子項目所需要的總寬度-容器實際寬度, 假設(shè)容器需要縮小的寬度為W, 某子項目的默認(rèn)寬度為L, 其縮小比例為p, 那么該子項目實際的寬度為L-p*W.

上面輕描淡寫的給出了子項目的縮小比例, 可能會給你一種錯覺— "縮小比例很容易計算", 實際上,?我們在計算元素需要縮小比例時, 總是要考慮到元素自身默認(rèn)的大小.

假設(shè)上述子項目其flex-shrink值為x1, 另一個子項目的默認(rèn)寬度為R, flex-shrink值為x2, 考慮到元素自身大小. 最終第一個子項目的縮小比例是加權(quán)了自身默認(rèn)大小后的結(jié)果, 即rate = L*x1/(L*x1 + R*x2).

為什么計算會如此復(fù)雜, 如此不直觀??? 這是因為, 子項目的大小各不一致, 假如一個子項目是另一個子項目主軸寬度的9倍, 前者的flex-shrink值為1, 后者為9, 而容器實際上只有他們默認(rèn)總寬度的一半. 這意味著, 這兩個子項目共計要壓縮為默認(rèn)的一半. 如果僅僅按照flex-shrink值來決定比例, 那么第二個子項目需要壓縮其默認(rèn)的9/10, 而我們知道, 它默認(rèn)是如此的小, 即使全部壓縮了, 也無濟(jì)于事; 而第一個元素僅需要壓縮其默認(rèn)的1/10, 簡直就是九牛一毛, 根本達(dá)不到默認(rèn)總寬度壓縮一半的效果. 很明顯, 這種壓縮比例的分配方式是不合理的. 因此最終的壓縮比例加入了默認(rèn)寬度值(即flex-basis值), 表達(dá)式的分子為?flex-shrink * flex-basis, 分母為各子項目?flex-shrink * flex-basis?之和.

flex-basis?指定子項目分配的默認(rèn)空間, 默認(rèn)為auto. 即該子項目的原本大小.

flex?是 flex-grow, flex-shrink, flex-basis 3個屬性的縮寫. 默認(rèn)為0 1 auto. 該屬性取值為auto時等同于設(shè)置為1 1 auto, 取值為none時等同于設(shè)置為0 0 auto.

align-self?指定單個子項目獨立的對齊方式. 默認(rèn)為auto, 表示繼承父元素的align-items屬性, 如無父元素, 則等同于stretch. 該屬性共有6種值, 其他值與上述align-items屬性保持一致.

order?指定子項目的順序, 數(shù)值越小, 順序越靠前, 默認(rèn)為0.

flex屬性的優(yōu)先級

我們可以給input設(shè)置flex:1, 使其充滿一行, 并且隨著父元素大小變化而變化. 也可以給div設(shè)置flex:1使其充滿剩余高度.

使用flex布局這些都不是難事, 需要注意的是, 這其中有坑. 為了避免踩坑, 我們先來看下flex屬性的優(yōu)先級:

width|height > 自適應(yīng)文本內(nèi)容的寬度或高度 > flex:數(shù)值

這意味著, 首先是元素寬高的值優(yōu)先, 其次是內(nèi)容的寬高, 再次是flex數(shù)值. 現(xiàn)在我們來看看坑是什么.

給input元素設(shè)置flex:1時需要注意, 通常input擁有一個默認(rèn)寬度(用于展示默認(rèn)數(shù)量的字符), 在chrome v55下, 這個寬度默認(rèn)為126px(同時還包含2px的border). 因此想要實現(xiàn)input寬度自適應(yīng), 可以設(shè)置其width為0.

給div元素設(shè)置flex:1時, 因div的高度會受子級元素影響, 為了使得該div占滿其父元素剩余的高度, 且不超出, 建議將該div的height屬性設(shè)置為0.

場景回顧

想要實現(xiàn)垂直居中的效果, 只需要設(shè)置父元素為display:flex;justify-content:center?即可. (當(dāng)然, 父元素樣式采用:display:table;, 子元素樣式采用:display:table-cell;vertical-align:middle?也是可以實現(xiàn)的), 如下圖.

想要實現(xiàn)左右兩個元素等高(父元素高度由子元素?fù)伍_), 并且各占一半的寬度. 如上圖.

早期的實現(xiàn)方案, 需要借助負(fù)margin. 父元素樣式設(shè)置為overflow:hidden, 子元素樣式設(shè)置為margin-bottom:-10000px;padding-bottom:10000px;, 這樣, 每個子元素便能借助padding撐開, 同時, 借助負(fù)margin和overflow合理裁剪.

第二種方案就是借助IE8都支持的display:table屬性, 父元素樣式設(shè)置為display:table?, 子元素設(shè)置為display:table-cell. 利用表格的行高一致性, 輕松實現(xiàn)行高一致.

最終, 我們發(fā)現(xiàn), 還是flex彈性盒模型來得方便快捷, 它只需要父級元素樣式設(shè)置為display:flex.

有關(guān)flex的舊語法, 請戳這篇回顧?Flex布局新舊混合寫法詳解(兼容微信)?.

有關(guān)移動端的最佳實踐, 請戳這篇圍觀?移動端全兼容的flexbox速成班?.

當(dāng)然, 這里還有一個?Flexbugs?列表, github上已有近6k的star, 感興趣可以前去看看.

相關(guān)鏈接:

https://www.cnblogs.com/xiaohuochai/p/5323146.html

http://www.reibang.com/p/53c44e02427c

https://segmentfault.com/a/1190000009061028推薦

http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html?utm_source=tuicool

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末篡悟,一起剝皮案震驚了整個濱河市昧狮,隨后出現(xiàn)的幾起案子小作,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掠廓,死亡現(xiàn)場離奇詭異昔脯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)际度,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涵妥,“玉大人乖菱,你說我怎么就攤上這事∨钔” “怎么了窒所?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帆锋。 經(jīng)常有香客問我吵取,道長,這世上最難降的妖魔是什么锯厢? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任皮官,我火速辦了婚禮,結(jié)果婚禮上实辑,老公的妹妹穿的比我還像新娘捺氢。我一直安慰自己,他們只是感情好徙菠,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布讯沈。 她就那樣靜靜地躺著郁岩,像睡著了一般婿奔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上问慎,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天萍摊,我揣著相機(jī)與錄音,去河邊找鬼如叼。 笑死冰木,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的笼恰。 我是一名探鬼主播踊沸,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼社证!你這毒婦竟也來了逼龟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤追葡,失蹤者是張志新(化名)和其女友劉穎腺律,沒想到半個月后奕短,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡匀钧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年翎碑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片之斯。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡日杈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出佑刷,到底是詐尸還是另有隱情达椰,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布项乒,位于F島的核電站啰劲,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏檀何。R本人自食惡果不足惜蝇裤,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望频鉴。 院中可真熱鬧栓辜,春花似錦、人聲如沸垛孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽周荐。三九已至狭莱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間概作,已是汗流浹背腋妙。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留讯榕,地道東北人骤素。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像愚屁,于是被迫代替她去往敵國和親济竹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容