1.绝对定位-margin修正方案
1.1 实现
父盒子开启相对定位,限制子元素的绝对定位。再利用定位将子元素的原点(左上角)定位到父盒子的中心,最后利用 margin 对子元素原点位置进行修正。
<style>.parent {position: relative;width: 800px;height: 600px;background: green;}.child {width: 300px;height: 250px;background: red;}/* 绝对定位-margin修正方案 */.solution {position: absolute;left: 50%;top: 50%;margin-left: -150px;margin-top: -125px;}</style><body><div class="parent"><div class="child solution"></div></div></body>
1.2 评价
对子元素的修正依赖子元素的宽高,如果子元素的宽高需要动态调整,则响应的修正值也需要调整,如下给出了该方案的改进方案。
<style>
:root {
--child-width: 300px;
--child-height: 250px;
}
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
width: var(--child-width);
height: var(--child-height);
background: red;
}
.solution {
position: absolute;
left: 50%;
top: 50%;
margin-left: calc(var(--child-width) / -2);
margin-top: calc(var(--child-height) / -2);
}
</style>
<body>
<body>
<div class="parent">
<div class="child solution"></div>
</div>
</body>
<script>
const root = document.querySelector(":root");
setInterval(() => {
root.style.setProperty(
"--child-width",
Math.floor(Math.random() * 6 + 2) * 50 + "px"
);
root.style.setProperty(
"--child-height",
Math.floor(Math.random() * 4 + 2) * 50 + "px"
);
}, 1000);
</script>
</body>
如果子元素没有设置高度而默认被孙子元素撑开,那么实现自适应实现将变得更加复杂,思路同样是在借助 CSS 变量进行桥接。
<style>
:root {
--child-padding: 20px;
--child-boder-width: 10px;
--grandson-width: 200px;
--grandson-height: 150px;
}
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
padding: var(--child-padding);
border: var(--child-boder-width) solid black;
width: var(--grandson-width);
height: var(--grandson-height);
background-color: yellow;
}
.grandson {
width: var(--grandson-width);
height: var(--grandson-height);
background: red;
}
.solution {
position: absolute;
left: 50%;
top: 50%;
margin-left: calc(
(var(--grandson-width) + var(--child-padding) + var(--child-boder-width)) /
-2
);
margin-top: calc(
(var(--grandson-height) + var(--child-padding) + var(--child-boder-width)) /
-2
);
}
</style>
<body>
<div class="parent">
<div class="child solution">
<div class="grandson"></div>
</div>
</div>
<script>
setInterval(() => {
let randomWidth = Math.floor(Math.random() * 6 + 2) * 50;
let randomHeight = Math.floor(Math.random() * 4 + 2) * 50;
document
.querySelector(":root")
.style.setProperty("--grandson-width", randomWidth + "px");
document
.querySelector(":root")
.style.setProperty("--grandson-height", randomHeight + "px");
}, 1000);
</script>
</body>
2.绝对定位-margin自适应方案
2.1 实现
前面的“绝对定位-margin修正方案”依赖子元素的宽高,而本方案却通过居中定位和 margin 自适应实现居中。
<style>
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
width: 300px;
height: 250px;
background: red;
}
/* 绝对定位-margin自适应方案 */
.solution {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
}
</style>
<body>
<div class="parent">
<div class="child solution"></div>
</div>
</body>
2.2 评价
本方案虽然不依赖子元素宽高,但是子元素必须显式指定宽高,不然子元素会填充父盒子,为了适用子元素被孙子元素撑开且要求居中的场景,在此方案下,同样必须使用 CSS 变量桥接。
<style>
:root {
--grandson-width: 300px;
--grandson-height: 250px;
--child-padding: 20px;
--child-border-width: 10px;
}
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
width: var(--grandson-width);
height: var(--grandson-height);
padding: var(--child-padding);
border: var(--child-border-width) solid black;
background: yellow;
}
.grandson {
width: var(--grandson-width);
height: var(--grandson-height);
background: red;
}
.solution {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
}
</style>
<body>
<div class="parent">
<div class="child solution">
<div class="grandson"></div>
</div>
</div>
<script>
const root = document.querySelector(":root");
setInterval(() => {
root.style.setProperty(
"--grandson-width",
Math.floor(Math.random() * 6 + 2) * 50 + "px"
);
root.style.setProperty(
"--grandson-height",
Math.floor(Math.random() * 4 + 2) * 50 + "px"
);
}, 1000);
</script>
</body>
3.绝对定位-transform修正方案
3.1 实现
父盒子开启相对定位,限制子元素的绝对定位。再利用定位将子元素的原点(左上角)定位到父盒子的中心,最后利用 transform 对子元素原点位置进行修正。
<style>
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
width: 300px;
height: 250px;
background: red;
}
/* 绝对定位-transform修正方案 */
.solution {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>
<body>
<div class="parent">
<div class="child solution"></div>
</div>
</body>
3.2 评价
该方案与“绝对定位-margin修正方案”的基本原理类似,只是修正手段不同。与前两种方案比较,这是绝对定位条件下的最佳居中方案,其优势是:代码实现简单,且适应性较强。
下面给出一个示例:
<style>
.parent {
position: relative;
width: 800px;
height: 600px;
background: green;
}
.child {
padding: 20px;
border: 10px solid black;
background: yellow;
}
.grandson {
width: 300px;
height: 250px;
background: red;
}
.solution {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>
<body>
<div class="parent">
<div class="child solution">
<div class="grandson"></div>
</div>
</div>
</body>
<script>
const grandson = document.querySelector(".grandson");
setInterval(() => {
grandson.style.width = Math.floor(Math.random() * 6 + 2) * 50 + "px";
grandson.style.height = Math.floor(Math.random() * 4 + 2) * 50 + "px";
}, 1000);
</script>
4.Flex方案
4.1 实现
父盒子开启弹性布局,让子元素在主轴和交叉轴上都居中,从而实现父盒子居中。
<style>
.parent {
width: 800px;
height: 600px;
background: green;
}
.child {
width: 300px;
height: 250px;
background: red;
}
/* flex方案 */
.solution {
display: flex;
justify-content: center;
align-items: center;
}
</style>
<body>
<div class="parent solution">
<div class="child"></div>
</div>
</body>
4.2 评价
该方案完全不依赖子元素,居中完全有父盒子实现,其原理简单,代码简洁,适应性极强,是一种几乎完美的居中方案。
<style>
.parent {
width: 800px;
height: 600px;
background: green;
}
.child {
border: 10px solid black;
padding: 20px;
background-color: yellow;
}
.grandson {
width: 300px;
height: 250px;
background-color: red;
}
.solution {
display: flex;
justify-content: center;
align-items: center;
}
</style>
<body>
<div class="parent solution">
<div class="child">
<div class="grandson"></div>
</div>
</div>
<script>
const grandson = document.querySelector(".grandson");
setInterval(() => {
grandson.style.width = Math.floor(Math.random() * 6 + 2) * 50 + "px";
grandson.style.height = Math.floor(Math.random() * 4 + 2) * 50 + "px";
}, 1000);
</script>
</body>
除了完全由父盒子实现居中之外,该方案中也可以不设置align-items: center;,而是在子元素中指定子元素属性align-self: center;,但是一般少有场景使用这种居中变异方案。
5.Grid方案
5.1 实现
该方案与“Flex方案”几乎完全一样,仅父盒子的布局模式不一样。
<style>
.parent {
width: 800px;
height: 600px;
background: green;
}
.child {
width: 300px;
height: 250px;
background: red;
}
/* grid方案 */
.solution {
display: grid;
justify-content: center;
align-items: center;
}
</style>
<body>
<div class="parent solution">
<div class="child"></div>
</div>
</body>
5.2 评价
该方案与“Flex方案”几乎相同,也具有同样的优点,但是实际开发中并几乎不使用这种方案实现居中效果,Grid 更多被用来页面布局,而不是实现居中。
后记
这五种方案是实际开放当中比较常见的基本方案,另外还能很多实现居中的方案,有些是在这五种方案上异变而来,而有些则相当奇怪。
综合这五种基础方案的比较,推荐在实际开发中使用“绝对定位-transform修正方案”和“Flex方案”,其余三种方案需要根据具体情况进行选择或者修改。
