CSS秘密花園: 垂直居中

IKEASH 8年前發布 | 13K 次閱讀 CSS 前端技術

CSS Secrets 》是 @Lea Verou 最新著作,這本書講解了有關于CSS中一些小秘密。是一本CSSer值得一讀的一本書,經過一段時間的閱讀,我、@南北和@彥子一起將在W3cplus發布一系列相關的讀后感,與大家一起分享。

問題

44年前我們把人送上月球,但在CSS中我們仍然不能很好實現垂直居中—— @James Anderson

讓一個元素水平居中對于CSS來說非常簡單:如果是一個內聯元素,我們可以在他的父元素上設置 text-align:center; ;如果是一個塊元素,我們可以使用 margin:auto; 。然而,只要一想到讓一個元素垂直居中,讓人死的心都有了。

多年來,垂直居中已成為CSS的不朽神話,也是前端專業人士群體中的一個內部笑話。原因是:

  • 經常需要使用
  • 理論上看上去非常簡單
  • 過去實戰中要實現是極其困難,特別是元素大小固定時

前端開發人員多年來盡所能的來解決這個問題,共中最令人不安的是使用了很多Hack手段。在這一節中,我們一起探索一些實現垂直居中現代技術。 注意,有一些受歡迎的技術,不在這里討論,主要是因為:

  • 表格布局不討論(表格顯示模式),因為它需要一些冗余的HTML標簽
  • inline-block 方法不包括,因為要使用很多Hack手段

然而,如果你對這方面感興趣,你可以閱讀Chris Coyier寫的博文《 Centering in the Unknown 》,里面介紹了這兩種技術。

如果沒有特殊聲明,后面示例用的HTML結構都是在 <body> 元素中插入下面的標簽:

<main>
    <h1>Am I centered yet?</h1>
    <p>Center me, please!</p>
</main>

為了得到下圖所示效果,我們也應用一些基本的CSS樣式,比如 background 、 padding 等等:

絕對定位解決方案

最早實現垂直居中的技術是元素需要一個固定的寬度和高度:

main {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -3em; /* 6/2 = 3 */
    margin-left: -9em; /* 18/2 = 9 */
    width: 18em;
    height: 6em;
}

從本質上講,它將元素的左上角點移到視窗中心點,然后使用負的 margin (使用 margin-top 和 margin-left ),而且 margin 的值是元素寬度和高度的一半,使元素的中心點與視窗的中心點重合。如果使用 calc() 可以減少兩個樣式:

main {
  position: absolute;
  top: calc(50% - 3em);
  left: calc(50% - 9em);
  width: 18em;
  height: 6em;
}

顯然,這種方法最大的問題是,元素需要一個固定的尺寸,而需要垂直居中元素的尺寸常常是需要由它的內容來決定。如果我們有辦法使用百分比來控制元素尺寸,我們的問題就解決了。不幸的是,對于大多數CSS屬性(包括 margin )百分比的值是相對于其父元素的寬度來決定。

在CSS中常常可以看到很多解決方案是不可思議。在這個例中,就可以使用CSS3的 transform 。可以在 transform 中的 translate() 使用百分比,讓元素移動是相對于自身的寬度和高度,這種方案正是我們需要的。

main {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

當然,沒有哪門技術是完美的,在實際使用中要注意一下以下幾個事項:

  • 絕對定位通常不是一個很好的選擇,因為它對整體的布局影響相當的大。
  • 如果垂直居中元素的內容比視窗高度更高的話,它的頂部會被裁剪掉,如下圖所示。但是這個問題可以解決,只不過需要使用一些Hack手段。
  • 在一些瀏覽器中,可能會導致元素出現略微的模糊,那是因為元素有可能被放置在半個像素位置上。我們可以通過 transform-style:preserve-3d 來解決,盡管這是一個Hack手段,不能保證它不會過時。

視窗單位的解決方案

如果想避免使用絕對定位,我們仍然可以使用 translate() 方法,其值剛好是元寬度和高度的一半。然而,我們如何不使用 top 和 left 將元素從 top 和 left 移動 50% 的偏移量呢?

首先想到的是給 margin 屬性一個百分數,像這樣:

main {
    width: 18em;
    padding: 1em 1.5em;
    margin: 50% auto 0;
    transform: translateY(-50%);
}

然而,正如你所看到的效果,如下圖所示:

這產生了非常奇怪的效果。主要原因是 margin 的百分比計算是相對于父容器的 width 來計算。是這樣的,甚至包括 margin-top 和 margin-bottom 。

值得慶幸的是,如果我們想讓元素在視窗中居中,還是有希望的。 CSS Values and Units Level 3 定義了一種新的單位,稱為相對視窗( viewport-relative )長度單位。

  • vw 是相對于視窗的寬度。與你預期剛好相反, 1vw 相當于視窗寬度的 1% ,而不是 100%
  • 與 vw 相似的是, 1vh 相當于視窗高度的 1%
  • 如果視窗的寬度小于高度, 1vmin 等于 1vw ,反之,如果視窗寬度大于高度, 1vmin 等于 1vh
  • 如果視窗的寬度大于高度, 1vmax 等于 1vw ,反之,如果視窗寬度小于高度, 1vmax 等于 1vh

在這個示例中,我們需要給 margin 的值設置 vh 單位:

main {
    width: 18em;
    padding: 1em 1.5em;
    margin: 50vh auto 0;
    transform: translateY(-50%);
}

正如你看到效果很完美。

當然,這種方法有用性是有極限的,因為它只適用于元素在視窗中垂直居中。

請注意,還可以使用相對視窗單位來創建全屏效果,而且不需要使用任何腳本。更多細節可以閱讀 Andrew Ckor 寫的《 Make full screen sections with 1 line of CSS 》博文。

Flexbox的解決方案

這無疑是最好的解決方案,因 Flexbox 的出現就是為了解決這樣的問題。其他解決方案仍然可用,唯一原因是他們能更好的在瀏覽器上呈現,不過Flexbox在現代瀏覽器也得到更好的好支持。

只需要兩個樣式,在需要垂直居中的父元素上設置 display:flex (這個示例中就是 <body> )和在垂直居中的元素上設置 margin:auto (這個示例中就是 <main> ):

body {
    display: flex;
    min-height: 100vh;
    margin: 0; 
}
main {
    margin: auto;
}

注意,當使用Flexbox和 margin:auto 時,元素不僅水平居中,而且也會垂直居中。也注意,我們甚至沒有設置寬度(如果我們想要也可以設置),其實指定的寬度相當于 max-content 。

如果瀏覽器不支持Flexbox,那么結果看起來就會像下圖(如果我們給元素設置了寬度):

CSS秘密花園: 垂直居中

即使不是垂直居中,還是可以接受的。

Flexbox的另一個優點是,可以讓匿名容器垂直居中。例如,我們將結構換成:

<main>Center me, please!</main>

我們可以通過 align-items 和 justify-content 屬性使設置固定尺寸的 <main> 容器里面的文本居中。如下圖所示:

我們可以在 <body> 和需要居中的元素 <main> 使用相同的屬性,同時使用 margin:auto 做為備用,以于優雅降級。

main {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18em;
    height: 10em;
}

新特性:對齊所有東西

CSS Box Alignment Level 3 已經在計劃,在未來我們甚至不需要使用不同的布局模式就能非常容易的實現垂直居中,我們只需要像下面這樣做:

align-self: center;

不管元素上使用其他樣式,這個將來都能運行。這聽起來令人難以置信,但將來在瀏覽器中是可以渲染的。

來自: http://www.w3cplus.com/css3/css-secrets/vertical-centering.html

 本文由用戶 IKEASH 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!