实用的 Web 布局技巧:Flex 主轴上的自动外边距

html5

浏览数:246

2019-5-25

AD:资源代下载服务

问题描述

先来看一个需求场景:

上图中需要实现在水平方向上子元素之间、子元素和父容器边框之间的间距要相等。

实现的方法有很多,我们这里要讨论的是:如何简洁地使用 Flex 布局来实现?
我这里采用的方法是:使用自动的外边距在主轴上对齐。

自动的外边距在主轴上对齐

我们先来看一下 MDN 关于这个的解释:

… 自动的外边距会占据全部的多余的空间——在一个块上设置自动的左右外边距可以使它居中。两边尽可能占据多的空间,块就被置于中间位置了。

这很好理解:自动外边距将平分全部的剩余空间。下面就来尝试下这个方案吧,代码如下:

<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
</div>
.container {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  border: 2px dashed #7cb305;
  width: 600px;
  height: 200px;
  margin: auto;
}


.item {
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0 auto;
  width: 100px;
  height: 100px;
  background: #722ed1;
  border-radius: 50%;
  color: #fff;
  font-size: 22pt;
}

然后看一下效果:

貌似有点不对。仔细看看子元素之间的间距比到边框的大,大概是子元素到边框的两倍,跟我们预期的效果有差异。
按照 MDN 的解释来看,自动的外边距会等分剩余空间,但为什么会出现上图的情况呢?下面来谈一下我的理解。

主轴剩余空间“分配权重”与子元素自动外边距的关系

这个分配权重关系没有在 MDN 上找到相关解释,纯粹是个人见解,我们可以这样来理解:

  • 如果一个子元素在主轴的一个区域(或方向)上声明了 margin-*: auto,那么这个空间的分配权重 + 1
  • 如果另外一个子元素也在同样的区域(或方向)上有自动外边距的声明,那么分配权重再 + 1
  • 在这些声明了自动外边距的区域上,剩余空间根据分配权重来划分间距大小

首先,这个理解显然是可以满足 MDN 上的解释。然后我们再来看看上图的情况怎么解释:

  • 我们把 子元素1 的左边区域命名为 间距区域1子元素1 的右边区域命名为 间距区域2
  • 子元素1 的左边和右边都有自动外边距,那么 间距区域1的权重 = 1间距区域2的权重 = 1
  • 子元素2 的左边和右边都有自动外边距,那么 间距区域2的权重 = 1 + 1 = 2
  • 最后 间距区域1的权重间距区域1的权重 就是 1:2 的关系
  • 其他区域依次类推,最后根据权重划分间距区域大小

然后依据这种理解我们来调整下代码,只需要在每个相隔的区域上声明一次自动外边距就好了:

  • 首先我们把 .item 这个样式上的 margin 去掉
  • 然后只在 html 的 子元素1子元素3 上设置 style="margin: 0 auto"

调整后如下:

<div class="container">
  <div class="item" style="margin: 0 auto">1</div>
  <div class="item">2</div>
  <div class="item" style="margin: 0 auto">3</div>
</div>
.container {
  display: flex;
  align-items: center;
  box-sizing: border-box;
  border: 2px dashed #7cb305;
  width: 600px;
  height: 200px;
  margin: auto;
}


.item {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 100px;
  background: #722ed1;
  border-radius: 50%;
  color: #fff;
  font-size: 22pt;
}

OK,问题解决了
根据这个理解还有一种设置也可以达到同样效果:

<div class="container">
  <div class="item" style="margin-left: auto">1</div>
  <div class="item" style="margin: 0 auto">2</div>
  <div class="item" style="margin-right: auto">3</div>
</div>

以上代码已放到 codepen 上:https://codepen.io/deepfunc/pen/BembyQ

作者:deepfunc