上個周末,在北航新主樓會議廳裡舉行了首屆 CSS 開發者大會,GitCafe 的前端工程師 Jaych Su 在會上做了演講,與大家分享了一款更優雅的前端布局——Flexbox。現在,我們就邀請他來為大家講一下 Flexbox 的相關知識吧~
在設計的眼中,排版的操作是一件很簡單的事情,靠左、置中、靠右,我只要點一下,所有元素,就會乖乖的到指定的位置。
但到了前端在排版的實現上,就不是這樣了。
我們常常得用一堆其實本來不是這樣用的屬性來做 hack,比如說用 line-height 來做垂直置中,這樣做的確能達到效果,但是在語意上就有點不順,拿剛剛提到的 line-height 來說,這本來是用來當作段落中的行距,但卻因為這個屬性能擴展文字的上下空間,結果也被拿來做垂直置中。那有沒有一個方法能用來更好地實現 Web 布局呢?
這是 Google 的 Angular。他們幾個月前做了一套 UI 來實現在 Angular 上的 Material Design。這套框架用來實現布局的方式,不是以往的 float,而是用了 Flexbox。
Flexbox 是什麼呢?就 W3C 官方給到的解釋是,這是設計來實現更複雜的版面布局。那我自己對他的定義是,Flexbox 從本質上就是一個 Box-model 的延伸,我們都知道 Box-model 定義了一個元素的盒模型,然而 Flexbox 更進一步的去規範了這些盒模型之間彼此的相對關係。而不需要去用一些很 cheat 的做法,去 hack 一些本來其實不應該用來做版面布局的屬性。
身為一個喜歡去玩一些新東西的前端,應該說每個跟網際網路有所接觸的人,都需要去學新東西。
這是我碰到新東西的時候,一定會問自己的三個問題:
1. 這能做什麼?也就是他能解決什麼問題?
2. 能用在哪裡?在哪些地方能用這個方法?
3. 為什麼能用?他實現所用到的邏輯是什麼?
接下來就跟大家分享一下,當初看到 Flexbox 的我問了自己這個三個問題之後,到目前為止我找到的答案。
舉一個例子,所有前端都會有的痛點,置中,我們以前是怎麼實現的?
最常看到就是用絕對定位,然後把 top 和 left 偏移 五零%,在用 margin 偏移回去。但是這隻適用在已經固定大小的元素。
最近幾年常看到的做法是這樣,在想置中的元素之前,加上一個元素,不想管太舊的 IE 的話,甚至偽元素也可以。在容器用 text-align,然後把底下的兩個元素弄成 inline 的形式,在用 vertical-align。他的好處就是,即使底下的元素會隨內容改變大小,但不管怎麼改變,就是可以始終維持垂直和水平置中。
當然啦,還有很多置中的方法,就不一一介紹了,我們來看一下用 flex 的話怎麼置中。
用 Flex 來做置中的話,你可以很從容地做到置中,不用一堆即使本來不是這樣用的屬性。我只要先指定容器為一個 Flex 容器,然後 justify-content 讓他水平方向置中,再 align-items 讓他垂直方向置中。我可以很簡單很優雅的就做到置中。
那也許你會說,欸?既然一個可以的話,那我再多放幾個可不可以?其實可以的。
假設我們現在容器底下有三個元素,喔,這裡就要提到 Flexbox 另外一個屌炸天的功能。
假設一個元素是四零%,另一個是一二%,那在一個 Flex 容器中,只要你有設定 flex-grow 這項屬性的話,他的第三個元素就會自適應寬度,填滿剩下容器的空白。而在多個元素的狀態之下,我們仍然能很輕易的就置中。
剛剛我們提到過,flexbox 是用來規範盒模型之間的相對關係,從這裡你就可以看到。現在我將 justify-content 設成 space-around,元素就會變成已分散對齊的方式去分佈在 flex 容器中。
關於元素的分步,我們再來看幾個例子。
這是一個我最近看到的網站。我們可以看到他底下有一個 Slider,這有個問題,而且也常常是前端在版面上的一個痛點,我們想讓所有的子元素能夠等高。在以前我們很難只用 CSS 去做到這樣。
而 flexbox 可以很輕易地只用 CSS 做到這點。只需要在 flex 容器加上 align-items 就好。就能實現容器底下的所有元素,與最高的那個元素等高。
即使我在本來最高的那個元素多加一些內容,其他的元素也一定會維持等高。
Flex 最初被 W3C 於 09 年制定出來,隨後就被大量的討論。拿指定元素為一個 flex 容器來講,第一個版本裡是 display:box,第二個版本是 display: flexbox,第三個版本是 display: flex。實在太複雜,還好現在在開源的世界裡已經有大大把這三個版本的 flex 做成一些 mixin,使用的時候,你只要 include 進來就可以。
就跟 IE 的使用體驗一樣,所有的好東西跟 IE 基本都沾不上邊,所以如果你需要考慮 IE 用戶,那請慎入。所以有人說 IE 的功能只剩下用來下載 Chrome 和 Firefox。
如果你到網上搜 flex,大多都會著墨在 他的對齊、他的控制 DOM 順序是如何如何好用。但今天我們想聊一聊更深一點的東西,flex item 寬度的計算,大多數情況下,我們只在意顯示的比例,這也是寬度的計算比較少被討論的原因,但如果你想要更精確的控制 item 的顯示寬度,其實你是需要去了解,在一個 flex 容器當中,item 的寬度是如何被計算出來的。
當我們把一個容器指定為 flex 容器時,它裡面的 item 其實是有著這樣的設定:flex: 0 1 auto
這三個數字其實分別代表:flex-grow、flex-shrink、flex-basis,這三個屬性可以說是 flex 之所以智能的原因。
我們先來聊聊 flex-basis 好了,這個屬性在 flex 容器為橫向的時候,其實就是寬度,當我們把 item 指定成 flex: 0 0 480px 時,其實就是把它的寬度設定成 480px。但是這樣並不能表現出 flex 有什麼特別的地方啊?為什麼要重複設定寬度?
這時候就要講到另外兩個屬性:flex-grow、flex-shrink
這兩個屬性其實是雙胞胎,grow 表示在 item 總寬度比容器小的時候,為了讓 item 填滿容器,每個 item 增加的寬度。假設有三個 basis 為 100px 的 item。我們從左到右給予 grow 值分別為 3、2、1,那麼當 flex 作用之後,最左邊的 item 實際增加的寬度是多少?從圖中可以算到增加的寬度是 90px,於是最後最左邊 item 的寬度是 190px。
我們剛才提到 grow 跟 shrink 其實是雙胞胎,其實他們真的很像,shrink 表示在 item 總寬度比容器大的時候,為了讓 item 填滿容器,每個 item 減少的寬度。但是計算的公式卻是不一樣的。為什麼?因為當你在加的時候無所謂,但是在減的時候,如果只計算賦予的 shrink 值,那麼很有可能最後減少的寬度比 basis 大,於是 item 的寬度就變成負值。那我們該怎麼修正?把 basis 當成參數計算進去,這樣就能保證減少的寬度永遠小於 basis。所以我們可以得到修正後的公式,一樣以最左邊為例子,最後計算出來減少 60px,於是 item 就變成 140px。以上腦子不好使,沒關係,實際上最常用的只是 flex: 1。
講到這裡,你剛剛講的好像這東西很厲害的樣子,那你有沒有一個最快最簡單粗暴的方式去說 Flexbox 真的是個好東西?
嗯⋯⋯有點難,不過我想應該可以。
請點擊【閱讀原文】進入 slides 在 GitCafe 上的開源地址,歡迎大家下載並與我們交流~