使用负边距在flex-box中控制空间

15

我一直在研究flex-box以解决我遇到的问题。我阅读的主要文章是这篇文章

最近我在这里这里提出了这个话题,感谢@Michael_B的帮助,我们找到了如何将某个元素(在本例中为<h2>)居中放置在flex-box中,只要flex-box中始终且仅有三个flex-item(在此布局中:<div><h2><div>)。

我想扩展这些观点,因为我意识到可能有一种解决方案,即使总共的flex-item数量不到3个或者大于3个也可以解决。

它涉及使用外边距。如果您在一个flex-item上使用margin: auto;,它会在主轴上居中一个项(请参见以下图片了解flex-box的工作原理)。

img {
 width: 100%;
 height: 100%;
}
<img src="http://i.stack.imgur.com/9Oxw7.png" alt="flex-box demo" />

那是我能想到的最好方法,可以实现一个下拉式照片......

从我的理解来看,使用 margin: auto 是当前在交叉轴上与 align-self 属性相对应的方式。

因此,可以通过以下方式之一来使 flex 盒中的 flex 项居中:

$(document).on('click', 'button', function(e) {
  $this = $(this);
  if ($this.attr('id') === 'button1') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "toggle1");
  }
  if ($this.attr('id') === 'button2') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "toggle1");
  }
  if ($this.attr('id') === 'button3') {
    $('h2').parent().attr("class", "toggle3");
    $('h2').attr("class", "");
  }
  if ($this.attr('id') === 'button4') {
    $('h2').parent().attr("class", "toggle4");
    $('h2').attr("class", "");
  }
  if ($this.attr('id') === 'button0') {
    $('h2').parent().attr("class", "");
    $('h2').attr("class", "");
  }
});
div:not(.cont),
div:not(.cont) * {
  border: 1px dotted red;
}
div {
  align-items: center;
  display: flex;
}
button {
  margin: 16px;
}
.toggle1 {
  margin: auto;
}
.toggle2 {
  flex: 1;
}
.toggle3 {
  justify-content: center;
}
.toggle4 {
  justify-content: space-between;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<div>
  <h2>I'm an h2</h2>
</div>
<div>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
</div>
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
</div>
<div class="cont">
  <button id="button0">clear</button>
  <button id="button1">margins</button>
  <button id="button2">flex-grow</button>
</div>
<div class="cont">
  <button id="button3">justify-content: center
    <br />(on parent)</button>
  <button id="button4">justify-content: space-around
    <br />(on parent)</button>
</div>

从上面的代码段中可以看出,它只能在每个<h2>两侧有相同数量的伸缩项时起作用(并且您还可以看到justify-content: space-around不能与一个对象一起使用)。

但是,如果在您尝试居中的对象的两侧存在不平衡数量的伸缩项怎么办? - 当然,您可以在那里放置空元素并使用flex: 1;,但这似乎有点粗糙。

我的想法是利用负边距。我读了一篇很棒的文章(点击此处),介绍了如何使用负边距以及它们如何将对象拉向它们等内容。(而且,连w3也表示它们是合法的代码-不是粗糙的代码)。

思路是将<h2>设置为margin: auto;,然后以某种方式(我强调用所有我一直在做的研究都开始失去希望)从同一伸缩盒中的任何兄弟伸缩项中取出宽度并将其从<h2> margin: auto;中去除...听起来很粗糙,但希望有一种方法可以在不使用JS / jQuery或CSS编译器的情况下完成它。

是否有人知道如何从同一伸缩盒中的兄弟伸缩项中减去另一个伸缩项的宽度?

谢谢。

P.S./更新

也许可以使用position属性来实现这一点;但是,我所做的所有测试都没有产生结果...也许其他人已经成功使用了这种方法?


4
如果 Michael_B 都不知道,那么没人知道了。 - Paulie_D
1
@Paulie_D,或许我正在工作,现在无法进入它 :-) - Michael Benjamin
4
可能是这样,但整个问题的基础是要做一些Flexbox并不适用的事情。Flexbox并不能解决所有布局问题。Javascript存在的原因就在于此... 它可以执行CSS无法完成的计算任务。 - Paulie_D
3
你想要做什么? - Chet
你是否需要类似于如何在flex box布局中保持中心框居中?这样的东西? - Oriol
显示剩余5条评论
5个回答

2

一种方法是将正常的flex流程中心元素取出。可以通过在容器内部绝对定位来实现。缺点是flex容器不再考虑绝对定位元素的高度。

codepen

.container {
  position: relative;
  display: flex;
  width: 500px;
  justify-content: flex-start;
  align-items: center;
  outline: 1px solid blue;
}

.item {
  flex: 0 0 auto;
  outline: 1px solid red;
  margin: 0;
}

.item--center {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
}

.item--right {
  margin-left: auto;
}
<div class="container">
  <span class="item">I'm a span!</span>
  <span class="item">I'm a span!</span>
  <h2 class="item item--center">I'm an h2</h2>
  <span class="item item--right">I'm a span!</span>
</div>


1
我已经设置了一种笨拙的方法来处理这个问题。
  1. 使flex容器换行
  2. 添加伪元素以强制在设置处换行
  3. 添加排序属性,以便第一行只有中心元素,然后是强制换行的伪元素
  4. 使第二行的span跳到行首和行尾
  5. 使用一些变换技巧,使实际上是2行的内容出现为单行。
  6. 当然,使用自动边距将中心元素居中

片段中的第一个容器像往常一样居中对齐,作为比较。 第二个容器左侧有更多的元素而右侧没有,但它工作正常:

div {
  display: flex;
  background-color: lemonchiffon;
  margin-top: 10px;
}

h2 {
  margin: auto;
}

span {
  border: solid 1px red;
  padding: 3px;
}

.container {
  flex-wrap: wrap;
  transform: scaleY(0.5);
}
.container:after {
  content: "";
  height: 0px;
  width: 100%;
  flex-shrink: 1;
  background-color: green;
  order: 10;
}

.container span {
  transform-origin: center top;
  transform: translateY(-100%) scaleY(2);
  position: relative;
  order: 20;
}

.center {
  transform-origin: center top;
  transform: scaleY(2) ;
}
.center ~ span {
  order: 30;
}

.center + span {
  margin-left: auto;
}
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
</div>
<div class="container">
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2 class="center">I'm an h2</h2>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
</div>


1

我不太明白,为什么你不直接使用以下的justify-content: center;

div{
  align-items: center;
  display: flex;  
  justify-content: center;
}
<div>
  <h2>I'm an h2</h2>
</div>


1

解决方案1:margin-top

  • 不涉及定位(但HTML结构会被改变)。
  • 本机的 flex h2 被隐藏并设置为 flex: 1;,以留出空间用于居中的 h2
  • 居中的 h2 将使用负 margin-top 替换本机的 flex h2

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
div {
  display: flex;
  flex-flow: row wrap;
  background: gainsboro;
}
div h2 {
  flex: 1;
  color: transparent;
}
h2.center {
  color: black;
  text-align: center;
  background: transparent;
  margin-top: -28px;
}
span {
  border: 1px solid gray;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
</div>
<h2 class="center">I'm an h2</h2>

解决方案2:使用absolute position

  • 将居中的h2设置为absolute,并放置在flex h2上方。
  • 修改HTML结构。

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
div {
  display: flex;
  flex-flow: row wrap;
  background: gainsboro;
}
div h2 {
  flex: 1;
  color: transparent;
}
h2.center {
  color: black;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  text-align: center;
  line-height: 28px;
}
span {
  border: 1px solid gray;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
</div>
<h2 class="center">I'm an h2</h2>

解决方案3:absolute + translate

  • HTML结构未被改变。
  • 总体上最短的代码,最小化,但值得注意。

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
div {
  display: flex;
  justify-content: flex-start;
  background: gainsboro;
  position: relative;
  width: 100%;
}
div h2 {
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
  line-height: 20px;
}
span {
  border: 1px solid gray;
}
div h2 + span {
  margin-left: auto;
}
<div>
  <span>I'm a span!</span>
  <span>I'm a span!</span>
  <h2>I'm an h2</h2>
  <span>I'm a span!</span>
</div>


0

刚看到这个问题。我不太清楚你想做什么,但我会尽力解释。

给定一个容器内任意数量的元素

  1. 指定一个元素始终位于数组中心
  2. 保留使用 flex 布局
  3. 不使用 JavaScript

这是我在 JSFiddle 上的解决方案

工作原理

  1. 我们通过将 h2 元素设为绝对定位,使其脱离了容器的流程。
  2. 我们将父元素设置为相对定位,以确保其子元素相对于自身进行定位。
  3. 我们通过将 topleft 属性设置为 50%,告诉 h2 元素我们希望它在容器元素的中心位置。
  4. 我们通过在 h2 上设置 transform: translate(-50%, -50%) 属性来纠正元素的偏移量(由于其跟踪是基于左上角顺序)。translate 值是相对于受影响元素的大小的,告诉它将其宽度的 50% 向左滑动,将其高度的 50% 向上滑动。
  5. 我们添加了一个“假”元素来模拟绝对定位的 h2 在流中的顺序。我们给这个虚拟元素设置了一个尺寸来保留空间,但您也可以添加一些文本或其他值来确保它被布局。
  6. 我们将虚拟元素的可见性设置为 hidden。在元素上使用 visibility: hidden 可以将其从显示中隐藏,但仍然会保留其在文档流中的空间。
在创建这个布局的过程中,我破坏了你示例中的按钮,但是我清理了你的JS代码,使其更易读。如果按钮对你的理解很重要,我很乐意修复它们。
相关的CSS如下:
div {
  position: relative;
}

h2 {
  position:absolute;
  margin: 0;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  line-height: 40px;
}
h3.filler {
  height: 40px;
  visibility: hidden;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接