CSS 揭秘(7)--垂直居中

Posted by liveipool on February 9, 2017

垂直居中

“44年前我们就把人类送上月球了,但现在我们仍然无法在CSS中实现垂直居中。” – James Anderson

在CSS 中度元素进行水平居中是非常简单的:如果它是一个行内元素,就对它的父元素应用text-align:center,如果它是一个块级元素,就对它自身应用margin:auto(设置了width的情况下)。然而要对一个元素进行垂直居中,一直以来都是前端开发圈内广为流传的笑话,因为看似简单,却一直难以实现。

本节中的html样例:

// jade
main.w-300.h-100
	h1 Am I center yet?
	p Center me, please!

// css
body {
	background: #fb3;
}

main {
	background: #58a;
	text-align: center;
	margin: auto;
}

40.1.png

基于绝对定位的解决方案

// jade
main
	h1 Am I center yet?
	p Center me, please!

// css
main {
	background: #58a;
	text-align: center;
	position: absolute;
	top: 50%;
	left: 50%;
	width: 18em;
	height: 6em;
	margin-left: -9em;
	margin-top: -3em;
}

40.2.png
这段代码的意思是:先把这个元素的左上角放在父元素的正中心,然后利用负外边距把它的向左、向上移动(移动距离为自身宽高的一半),从而把元素的正中心放在视口的正中心。

还可简写为:

main {
	background: #58a;
	text-align: center;
	position: absolute;
	top: calc(50% - 3em);
	left: calc(50% - 9em);
	width: 18em;
	height: 6em;
}

但这个方法最大的局限性在于,要求元素的高度是固定的。
不过,因为translate()变形函数中使用百分比值时,是以这个元素自身的宽度和高度为基准进行换算和移动的,因此我们正好可以利用它来解决问题:

main {
	background: #58a;
	text-align: center;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
}

可以得到以下效果:
40.3.png

最后这种方法看起来还是相当不错的完成任务了,也有了一点弹性。但是,依然有着问题:绝对定位对整个布局的影响太过强烈,有时候不太好使用它。
因此,我们还需要找寻更好的方法。

基于视口的解决方案

  • 1vw表示视口宽度的1%。
  • 1vh表示视口高度的1%。
  • 当时口宽度小于视口高度时,1vmin == 1vw。
  • 当时口宽度大于视口高度时,1vmax == 1vw。
main {
	background: #58a;
	text-align: center;
	width: 18em;
	padding: 1em 1.5em;
	margin: 50vh auto 0;
	transform: translateY(-50%);
}

40.4.png
这种方案又优雅了一些(至今IE11 和 Edge是部分支持如vh、vw等单位)。
但是,这种方案的实用性也有限,因为它只适用于在视口中居中的场景。

基于 Flexbox 的解决方案

40.5.png
这是毋庸置疑的最佳解决方案,因为Flexbox是专门针对这类需求所设计的。

body {
	background: #fb3;
	display: flex;
	min-height: 100vh;
	margin: 0;
}

main {
	background: #58a;
	text-align: center;
	margin: auto;
}

上面这段代码就解决了所有的问题:
40.6.png

因为当我们使用Flexbox时,margin:auto不仅在水平方向上将元素居中,垂直方向上也是如此。这个居中元素分配到的宽度等于max-content。

Flexbox还有一个好处在于,它可以将匿名容器垂直居中。
我们先给这个main元素指定一个固定的尺寸,然后借助Flexbox引入的align-items和justify-content属性,让它内部的文本也居中。

// jade
main Center me, please!

// css
body {
	background: #fb3;
	display: flex;
}

main {
	background: #58a;
	width: 18em;
	height: 10em;
	display: flex;  /*设置了它才能设置align-items和justify-content*/
	align-items: center;  /* 使文本垂直居中*/
	justify-content: center;  /* 使文本水平居中*/
	margin: auto;    /*需要父元素设置display:flex*/
}

40.7.png

最后一个,也是最6666666的一个属性来了,有了它,两行代码搞定垂直居中。

body {
	background: #fb3;
	display: flex;
}

main {
	background: #58a;
	width: 18em;
	height: 10em;
	align-self: center;  /*666*/
}

40.8.png