彈性盒模型Flex指南

Web layout 是Web UI中的基礎架構, 重要性不言而喻. 傳統的盒模型, 藉助display, position, float 屬性應對普通佈局遊刃有餘, 但針對複雜的或自適應佈局, 經常捉襟見肘. 好比垂直居中, 就是一個老大難的問題, 藉助flex彈性盒模型, 兩行代碼就能夠優雅的實現之. (該方法曾在 16種方法實現水平居中垂直居中 一文中提到). 固然, 本次咱們不會只討論垂直居中的問題, 我將努力盡量的還原flex的應用場景.css

原文: 彈性盒模型Flex指南html

本文將介紹flex子項目壓縮比計算, 多層flex嵌套的常見問題. 通讀本文, 你還將瞭解以下內容:css3

  1. Flex
  2. 兼容性
  3. Autoprefixer
  4. 優點
  5. 概念鋪墊
  6. 屬性
    1. 容器屬性
    2. 子項目屬性
  7. flex屬性的優先級
  8. 場景回顧

Flex

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

強記各類瀏覽器的前綴是沒有必要的, 由於autoprefixer該作的, 都幫咱們作了. 所以建議嘗試下如下三個插件之一.

優點

Flex佈局使得子項目可以"彈性"的改變其高寬, 自由填充容器剩餘空間, 以適應容器變大, 或者壓縮子項目自身, 以適應容器變小; 同時還能夠方便的調節子項目方向和順序. flex經常使用於高寬須要自適應, 或子項目大小成比例, 或水平垂直對齊等場景.

概念鋪墊

Flex彈性盒模型裏, 有容器和項目之分. 設置display:flex的爲容器, 容器內的元素稱做它的子項目, 容器有容器的一套屬性, 子項目有子項目的另外一套屬性. (能夠這麼理解: father做爲彈性盒子, 制定行爲規範, son享受盒子的便利, 按照規範劃分各自的"轄區").

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

flexbox

father制定的規範, 基於兩個方向 — 水平和垂直.

  • 水平方向的稱之爲主軸(main axis), 垂直方向的稱之爲交叉軸(cross axis).
  • 主軸起始位置, 叫作main start, 末尾位置叫作main end;
  • 交叉軸起始位置, 叫作cross start, 末尾位置叫作cross end.
  • 子項目在主軸上所佔的寬(高)度, 叫作main size, 在交叉軸上所佔的高(寬)度, 叫作cross size.

屬性

display: flex | inline-flex;(元素將升級爲彈性盒子). 前者容器升級爲塊級盒子, 後者容器將升級爲行內盒子. 元素採用flex佈局之後, 子元素的float, clear, vertical-align屬性都將失效.

容器屬性

容器具備如下6個屬性.

  • flex-direction 指定主軸的方向.
flex-direction的值 描述
row(默認) 指定主軸水平, 子項目從左至右排列➜
row-reverse 指定主軸水平, 子項目從右至左排列⬅︎
column 指定主軸垂直, 子項目從上至下排列⬇︎
column-reverse 指定主軸垂直, 子項目從下至上排列⬆︎
  • flex-wrap 指定如何換行.
flex-wrap的值 描述
nowrap(默認) 默認不換行
wrap 正常換行
wrap-reverse 換行, 且前面的行在底部
  • flex-flow 它是flex-direction 和 flex-wrap的簡寫形式, 默認值爲row nowrap.
  • justify-content 指定主軸上子項目的對齊方式.(一般爲水平方向對齊方式)
justify-content的值 描述(子項目--主軸方向)
flex-start(默認) 子項目起始位置與main start位置對齊
flex-end 子項目末尾位置與main end位置對齊
center 在主軸方向居中於容器
space-between 與交叉軸兩端對齊, 子項目之間的間隔所有相等
space-around 子項目兩側的距離相等, 它們之間的距離兩倍於它們與主軸起始或末尾位置的距離.
  • align-items 指定交叉軸上子項目的對齊方式.(一般爲垂直方向對齊方式)
align-items的值 描述(子項目—交叉軸方向)
flex-start 子項目起始位置與cross start位置對齊
flex-end 子項目末尾位置與cross end位置對齊
center 在交叉軸方向居中於容器
baseline 第一行文字的基線對齊
stretch(默認) 高度未定(或auto)時, 將佔滿容器的高度
  • align-content 指定多根主軸的對齊方式. 若只有一根主軸, 則無效.
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.

flex屬性的優先級

咱們能夠給input設置flex:1, 使其充滿一行, 而且隨着父元素大小變化而變化. 也能夠給div設置flex:1使其充滿剩餘高度.

使用flex佈局這些都不是難事, 須要注意的是, 這其中有坑. 爲了不踩坑, 咱們先來看下flex屬性的優先級:

width|height > 自適應文本內容的寬度或高度 > flex:數值

這意味着, 首先是元素寬高的值優先, 其次是內容的寬高, 再次是flex數值. 如今咱們來看看坑是什麼.

  1. 給input元素設置flex:1時須要注意, 一般input擁有一個默認寬度(用於展現默認數量的字符), 在chrome v55下, 這個寬度默認爲126px(同時還包含2px的border). 所以想要實現input寬度自適應, 能夠設置其width爲0.
  2. 給div元素設置flex:1時, 因div的高度會受子級元素影響, 爲了使得該div佔滿其父元素剩餘的高度, 且不超出, 建議將該div的height屬性設置爲0.

場景回顧

  1. 想要實現垂直居中的效果, 只須要設置父元素爲display:flex;justify-content:center 便可. (固然, 父元素樣式採用:display:table;, 子元素樣式採用:display:table-cell;vertical-align:middle 也是能夠實現的), 以下圖.

  2. 想要實現左右兩個元素等高(父元素高度由子元素撐開), 而且各佔一半的寬度. 如上圖.

    • 早期的實現方案, 須要藉助負margin. 父元素樣式設置爲overflow:hidden, 子元素樣式設置爲margin-bottom:-10000px;padding-bottom:10000px;, 這樣, 每一個子元素便能借助padding撐開, 同時, 藉助負margin和overflow合理裁剪.
    • 第二種方案就是藉助IE8都支持的display:table屬性, 父元素樣式設置爲display:table , 子元素設置爲display:table-cell. 利用表格的行高一致性, 輕鬆實現行高一致.
    • 最終, 咱們發現, 仍是flex彈性盒模型來得方便快捷, 它只須要父級元素樣式設置爲display:flex.

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

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

固然, 這裏還有一個 Flexbugs 列表, github上已有近6k的star, 感興趣能夠前去看看.


本問就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論.

本文做者: louis

本文連接: louiszhai.github.io/2017/01/13/…

參考文章

相關文章
相關標籤/搜索