Web layout 是Web UI中的基礎架構, 重要性不言而喻. 傳統的盒模型, 藉助display, position, float 屬性應對普通佈局遊刃有餘, 但針對複雜的或自適應佈局, 經常捉襟見肘. 好比垂直居中, 就是一個老大難的問題, 藉助flex彈性盒模型, 兩行代碼就能夠優雅的實現之. (該方法曾在 16種方法實現水平居中垂直居中 一文中提到). 固然, 本次咱們不會只討論垂直居中的問題, 我將努力盡量的還原flex的應用場景.css
原文: 彈性盒模型Flex指南html
本文將介紹flex子項目壓縮比計算, 多層flex嵌套的常見問題. 通讀本文, 你還將瞭解以下內容:css3
Flex即彈性盒模型, 該佈局方案由W3C於2009年提出. 此後, Flex方案便歷經v2009, v2011, v2012, v2014, v2015, v2016等版本, 最近方案是2016年5月26日起草的 CSS Flexible Box Layout Module Level 1.git
首先, 咱們來回顧下現在PC端的兼容性(如下爲徹底兼容版本).github
IE | Edge | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|---|
- | 12+ | 28+ | 21+ | 6.1+ | 12.1+ |
以上, IE10+僅支持2012版W3C的flex語法, 且存在較多已知的bug, 此時使用flex佈局需謹慎.web
Chrome瀏覽器v21~v28版本須要添加 "-webkit-" 前綴.chrome
Safari瀏覽器v6.1~v8版本須要添加 "-webkit-" 前綴.npm
Opera瀏覽器v15~v16版本須要添加 "-webkit-" 前綴.gulp
所以, 看到一些sass編譯後的css文件中帶有 "-webkit-" 前綴無需驚慌.segmentfault
平時開發時最爲擔憂的即是移動端兼容性, 請看:
IOS Safari | Opera mini | Android | Android Chrome | UC | 微信 |
---|---|---|---|---|---|
7.1+ | √ | 4.4+ | 55 | - | 當前支持 |
微信當前版本已支持flex.
UC不對外提供webview內核, 除去一些H5app的應用, 各類分享頁基本(常在微信下打開)基本不須要擔憂對其兼容性, 實在須要實現, UC仍是支持老版本的彈性盒子的, 能夠優雅降級. 可見, Android4.4以上基本能夠安心使用flex.
強記各類瀏覽器的前綴是沒有必要的, 由於autoprefixer該作的, 都幫咱們作了. 所以建議嘗試下如下三個插件之一.
Flex佈局使得子項目可以"彈性"的改變其高寬, 自由填充容器剩餘空間, 以適應容器變大, 或者壓縮子項目自身, 以適應容器變小; 同時還能夠方便的調節子項目方向和順序. flex經常使用於高寬須要自適應, 或子項目大小成比例, 或水平垂直對齊等場景.
Flex彈性盒模型裏, 有容器和項目之分. 設置display:flex
的爲容器, 容器內的元素稱做它的子項目, 容器有容器的一套屬性, 子項目有子項目的另外一套屬性. (能夠這麼理解: father做爲彈性盒子, 制定行爲規範, son享受盒子的便利, 按照規範劃分各自的"轄區").
如下圖片摘自大漠的一個完整的Flexbox指南文中.
father制定的規範, 基於兩個方向 — 水平和垂直.
main start
, 末尾位置叫作main end
; cross start
, 末尾位置叫作cross end
.main size
, 在交叉軸上所佔的高(寬)度, 叫作cross size
.display: flex | inline-flex;(元素將升級爲彈性盒子). 前者容器升級爲塊級盒子, 後者容器將升級爲行內盒子. 元素採用flex佈局之後, 子元素的float, clear, vertical-align屬性都將失效.
容器具備如下6個屬性.
flex-direction的值 | 描述 |
---|---|
row(默認) | 指定主軸水平, 子項目從左至右排列➜ |
row-reverse | 指定主軸水平, 子項目從右至左排列⬅︎ |
column | 指定主軸垂直, 子項目從上至下排列⬇︎ |
column-reverse | 指定主軸垂直, 子項目從下至上排列⬆︎ |
flex-wrap的值 | 描述 |
---|---|
nowrap(默認) | 默認不換行 |
wrap | 正常換行 |
wrap-reverse | 換行, 且前面的行在底部 |
row nowrap
.justify-content的值 | 描述(子項目--主軸方向) |
---|---|
flex-start(默認) | 子項目起始位置與main start 位置對齊 |
flex-end | 子項目末尾位置與main end 位置對齊 |
center | 在主軸方向居中於容器 |
space-between | 與交叉軸兩端對齊, 子項目之間的間隔所有相等 |
space-around | 子項目兩側的距離相等, 它們之間的距離兩倍於它們與主軸起始或末尾位置的距離. |
align-items的值 | 描述(子項目—交叉軸方向) |
---|---|
flex-start | 子項目起始位置與cross start 位置對齊 |
flex-end | 子項目末尾位置與cross end 位置對齊 |
center | 在交叉軸方向居中於容器 |
baseline | 第一行文字的基線對齊 |
stretch(默認) | 高度未定(或auto)時, 將佔滿容器的高度 |
align-content的值 | 描述(子項目) |
---|---|
flex-start | 頂部與cross start 位置對齊 |
flex-end | 底部與cross end 位置對齊 |
center | 在交叉軸方向居中於容器 |
space-between | 與交叉軸兩端對齊, 間隔所有相等 |
space-around | 子項目兩側的距離相等, 它們之間的距離兩倍於它們與主軸起始或末尾位置的距離. |
stretch(默認) | 多根主軸上的子項目充滿交叉軸 |
子項目具備如下6個屬性.
flex-grow 指定子項目的放大比例, 默認爲0(即不放大). 該屬性可取值爲任何正整數. 假設各個子項目的放大比例之和爲n, 那麼容器內剩餘的空間將分配n份, 每一個子項目各自分到x/n份. (x爲該子項目的放大比例)
flex-shrink 指定子項目的縮小比例, 默認爲1
. 設置爲0時, 空間不足該子項目將不縮小. 咱們知道, 容器的縮小總寬度=子項目所須要的總寬度-容器實際寬度
, 假設容器須要縮小的寬度爲W, 某子項目的默認寬度爲L, 其縮小比例爲p, 那麼該子項目實際的寬度爲L-p*W
.
上面輕描淡寫的給出了子項目的縮小比例, 可能會給你一種錯覺— "縮小比例很容易計算", 實際上, 咱們在計算元素須要縮小比例時, 老是要考慮到元素自身默認的大小.
假設上述子項目其flex-shrink值爲x1, 另外一個子項目的默認寬度爲R, flex-shrink值爲x2, 考慮到元素自身大小. 最終第一個子項目的縮小比例是加權了自身默認大小後的結果, 即rate = L*x1/(L*x1 + R*x2)
.
爲何計算會如此複雜, 如此不直觀??? 這是由於, 子項目的大小各不一致, 假如一個子項目是另外一個子項目主軸寬度的9倍, 前者的flex-shrink值爲1, 後者爲9, 而容器實際上只有他們默認總寬度的一半. 這意味着, 這兩個子項目共計要壓縮爲默認的一半. 若是僅僅按照flex-shrink值來決定比例, 那麼第二個子項目須要壓縮其默認的9/10, 而咱們知道, 它默認是如此的小, 即便所有壓縮了, 也無濟於事; 而第一個元素僅須要壓縮其默認的1/10, 簡直就是九牛一毛, 根本達不到默認總寬度壓縮一半的效果. 很明顯, 這種壓縮比例的分配方式是不合理的. 所以最終的壓縮比例加入了默認寬度值(即flex-basis值), 表達式的分子爲 flex-shrink * flex-basis
, 分母爲各子項目 flex-shrink * flex-basis
之和.
flex-basis 指定子項目分配的默認空間, 默認爲auto
. 即該子項目的本來大小.
flex 是 flex-grow, flex-shrink, flex-basis 3個屬性的縮寫. 默認爲0 1 auto
. 該屬性取值爲auto時等同於設置爲1 1 auto
, 取值爲none時等同於設置爲0 0 auto
.
align-self 指定單個子項目獨立的對齊方式. 默認爲auto
, 表示繼承父元素的align-items屬性, 如無父元素, 則等同於stretch
. 該屬性共有6種值, 其餘值與上述align-items屬性保持一致.
order 指定子項目的順序, 數值越小, 順序越靠前, 默認爲0
.
咱們能夠給input設置flex:1
, 使其充滿一行, 而且隨着父元素大小變化而變化. 也能夠給div設置flex:1
使其充滿剩餘高度.
使用flex佈局這些都不是難事, 須要注意的是, 這其中有坑. 爲了不踩坑, 咱們先來看下flex屬性的優先級:
width|height > 自適應文本內容的寬度或高度 > flex:數值
這意味着, 首先是元素寬高的值優先, 其次是內容的寬高, 再次是flex數值. 如今咱們來看看坑是什麼.
flex:1
時須要注意, 一般input擁有一個默認寬度(用於展現默認數量的字符), 在chrome v55下, 這個寬度默認爲126px(同時還包含2px的border). 所以想要實現input寬度自適應, 能夠設置其width爲0.flex:1
時, 因div的高度會受子級元素影響, 爲了使得該div佔滿其父元素剩餘的高度, 且不超出, 建議將該div的height
屬性設置爲0.想要實現垂直居中的效果, 只須要設置父元素爲display:flex;justify-content:center
便可. (固然, 父元素樣式採用:display:table;
, 子元素樣式採用:display:table-cell;vertical-align:middle
也是能夠實現的), 以下圖.
想要實現左右兩個元素等高(父元素高度由子元素撐開), 而且各佔一半的寬度. 如上圖.
overflow:hidden
, 子元素樣式設置爲margin-bottom:-10000px;padding-bottom:10000px;
, 這樣, 每一個子元素便能借助padding撐開, 同時, 藉助負margin和overflow合理裁剪.display:table
屬性, 父元素樣式設置爲display:table
, 子元素設置爲display:table-cell
. 利用表格的行高一致性, 輕鬆實現行高一致.display:flex
.有關flex的舊語法, 請戳這篇回顧 Flex佈局新舊混合寫法詳解(兼容微信) .
有關移動端的最佳實踐, 請戳這篇圍觀 移動端全兼容的flexbox速成班 .
固然, 這裏還有一個 Flexbugs 列表, github上已有近6k的star, 感興趣能夠前去看看.
本問就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論.
本文做者: louis
本文連接: louiszhai.github.io/2017/01/13/…
參考文章