1.绝对定位-margin修正方案

1.1 实现

父盒子开启相对定位,限制子元素的绝对定位。再利用定位将子元素的原点(左上角)定位到父盒子的中心,最后利用 margin 对子元素原点位置进行修正。

  1. <style>
  2. .parent {
  3. position: relative;
  4. width: 800px;
  5. height: 600px;
  6. background: green;
  7. }
  8. .child {
  9. width: 300px;
  10. height: 250px;
  11. background: red;
  12. }
  13. /* 绝对定位-margin修正方案 */
  14. .solution {
  15. position: absolute;
  16. left: 50%;
  17. top: 50%;
  18. margin-left: -150px;
  19. margin-top: -125px;
  20. }
  21. </style>
  22. <body>
  23. <div class="parent">
  24. <div class="child solution"></div>
  25. </div>
  26. </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方案”,其余三种方案需要根据具体情况进行选择或者修改。

参考文章