作者:Alsiso
網址:http://www.cnblogs.com/tugenhua0707/p/4722696.html
雖然使用 CSS 建立居中效果需要耍一些花招,特別是垂直居中效果,但我認為由此生出的詆毀,對於 CSS 則是不公平的。實際上我們有太多的方式使用 CSS 建立居中效果了,而且作為一名前端開發者,你真的有必要對其中的原理瞭解一二。
寫作本文的目的不是為了向各位解釋這些方法的工作原理,而是介紹將這些方法編寫為 Sass mixin 的方式,繼而將它們復用到各類專案中。如果你還不熟悉使用 CSS 建立居中效果的方法,我建議你仔細閱讀以下這篇文章:Centering In CSS: A Complete Guide。
總體概述
本文將會專註於解決子元素居中於父類容器的問題,就實踐經驗來說,這也是最常使用到的居中效果。當你請教別人 CSS 中和居中效果相關的問題時,他們往往會反問你:你知道元素具體的寬高嗎?之所以會有這樣的反問,是因為如果知道元素的寬高,那麼最好的解決方案就是使用 CSS transform 屬性。雖然該屬性在瀏覽器中的支援度稍低,但卻有著高度靈活的特性;如果因為瀏覽器相容性令你不能使用 CSS transform 屬性,或者也不知道元素的寬高,那麼實現居中效果的最簡單方法就是使用負向 margin。
我們今天要建立的 Sass mixin 就是基於上述的方法:將元素的左上角絕對定位到容器的中心位置,然後為 mixin 新增兩個可選引數,分別代表元素的寬高,如果傳遞了引數,那麼就使用負向 margin 的方法實現居中;如果沒有傳遞引數,就使用 CSS transform 的方法。
當我們的 Sass mixin 建立成功後,基本的使用方式如下所示:
/**
* 為子元素設定定位背景關係
*/
.parent {
position: relative;
}
/**
* 讓子元素絕對居中於父容器
* 沒有向 Sass mixin 傳遞引數,使用 CSS transform 屬性實現居中效果
*/
.child-with-unknown-dimensions {
@include center;
}
/**
* 讓子元素絕對居中於父容器
* 向 Sass mixin 傳遞了寬度,所以就使用負向 margin 處理水平位置,
* 使用 CSS transform 處理垂直位置
*/
.child-with-known-width {
@include center(400px);
}
/**
* 讓子元素絕對居中於父容器
* 向 Sass mixin 傳遞了高度,所以就使用負向 margin 處理垂直位置,
* 使用 CSS transform 處理水平位置
*/
.child-with-known-height {
@include center($height: 400px);
}
/**
* 讓子元素絕對居中於父容器
* 向 Sass mixin 傳遞了高度和寬度,所以就使用負向 margin 處理水平和垂直位置
*/
.child-with-known-dimensions {
@include center(400px, 400px);
}
上述 Sass 程式碼經過編譯之後,輸出結果如下:
.parent {
position: relative;
}
.child-with-unknown-dimensions {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.child-with-known-width {
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
width: 400px;
transform: translateY(-50%);
}
.child-with-known-height {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%);
margin-top: -200px;
height: 400px;
}
.child-with-known-dimensions {
position: absolute;
top: 50%;
left: 50%;
margin-left: -200px;
width: 400px;
margin-top: -200px;
height: 400px;
}
還不錯,就是看起來有點囉嗦,不過鑒於是用來做 demo 的,也不必太過強求了。
建立 mixin
思路屢清楚了,下麵開工!根據上面的程式碼片段,我們已經知道了這個 mixin 的主要特徵:接收兩個可選的引數,用來表示元素的寬高($width 和 $height)。
/// Horizontal, vertical or absolute centering of element within its parent
/// If specified, this mixin will use negative margins based on element’s
/// dimensions. Else, it will rely on CSS transforms which have a lesser
/// browser support but are more flexible as they are dimension-agnostic.
///
/// @author Hugo Giraudel
///
/// @param {Length | null} $width [null] – Element width
/// @param {Length | null} $height [null] – Element height
///
@mixin center($width: null, $height: null) { .. }
然後,由分析知,要實現居中必須讓元素絕對定位:
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
}
在這裡讓我們暫停一下,深入分析後續邏輯的層次:
width | height | solution |
---|---|---|
null | null | translate |
defined | defined | margin |
defined | null | margin-left + translateY |
null | defined | margin-right + translateX |
秀程式碼:
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
@if not $width and not $height {
// Go with `translate`
} @else if $width and $height {
// Go width `margin`
} @else if not $height {
// Go with `margin-left` and `translateY`
} @else {
// Go with `margin-top` and `translateX`
}
}
透過上面的程式碼,我們已經搭好了 mixin 的骨架,只需要再新增上具體的邏輯程式碼即可:
@mixin center($width: null, $height: null) {
position: absolute;
top: 50%;
left: 50%;
@if not $width and not $height {
transform: translate(-50%, -50%);
} @else if $width and $height {
width: $width;
height: $height;
margin: -($width / 2) #{0 0} -($height / 2);
} @else if not $height {
width: $width;
margin-left: -($width / 2);
transform: translateY(-50%);
} @else {
height: $height;
margin-top: -($height / 2);
transform: translateX(-50%);
}
}
註意!上面程式碼中的 #{0 0} 實際上一種容錯措施,如果直接使用 0 0 的話,Sass 解析器會嘗試進行數值運算(在這裡會自動進行 0 -($height / 2) 的數學運算),進而導致我們得到 margin: mt 0 ml; 而不是想要得到的 margin: mt 0 0 ml;。
深入淺出
基本的功能實現後,我們還可以新增更多的特性,比如新增 @support 來檢查瀏覽器對 CSS transform 的支援度,進而可以根據 CSS transform 的支援度輸出相應的條件樣式。此外,我們還可以更嚴謹地去測試出入的引數是否是有效數值……
使用 Flexbox
看到 Flexbox 這個詞是不是就很興奮啊,少年!確實,使用 Flexbox 確實是最簡單的方式,它和前面方法主要的差別在於,使用 Flexbox 需要為父容器設定相關樣式,而使用前面的方法則主要是為子元素設定相關樣式(當然,父容器需要被設定為除 static 之外的任意 position)。
使用 Flexbox 實現子元素的居中效果,只需三行程式碼:
@mixin center-children {
display: flex;
justify-content: center;
align-items: center;
}
由於 Flexbox 還是比較新的屬性,那麼新增上相關的瀏覽器字首的話,會讓它擁有更廣泛的相容性。
.parent {
@include center-children;
}
正如你料想的那樣,就這麼簡單我們就實現了:
.parent {
display: flex;
justify-content: center;
align-items: center;
}
總結
我們就想要一個簡短的 mixin 讓元素在父容器中居中,我們做到了,而且做的更好。它不僅僅簡單易用無副作用,而且提供了良好的開發介面。