具有响应式正方形的网格布局

59

我想创建一个具有响应式正方形的网格布局。

我觉得可以使用CSS Grid布局来实现,但很难设置每个正方形的高度等于宽度。

同时,我也很难设置正方形之间的间距。

我是否最好使用flexbox?

目前我的HTML看起来像这样,但会是动态的,因此可能会添加更多正方形。当然,它需要是响应式的,所以最好使用媒体查询将其折叠为一列。

<div class="square-container">

  <div class="square">
    <div class="content">
    </div>
  </div>

  <div class="square">
    <div class="content spread">
    </div>
  </div>

  <div class="square">
    <div class="content column">
    </div>
  </div>

</div>

使用CSS Grid,我只做到了这一步

.square-container{
    display: grid;
    grid-template-columns: 30% 30% 30%;
    .square {

    }
}

我能够通过flexbox做得更进一步,使用space-between对齐带有漂亮间隙的正方形,但仍然无法使每个正方形的高度与宽度匹配。

我没有找到使用flexbox或grid完成这个任务的任何示例,但如果有任何示例,将不胜感激。

谢谢


我应该补充说明,在内容div中,我想使用flexbox来创建各种img和文本的布局,因此我希望避免在内容div中使用position或padding。 - Stretch0
1
@kukkuz,你可能想考虑保留你的答案。它可能在某一天变得有效(当浏览器制造商达成共识时)。此外,这将防止其他人发布相同的解决方案。 - Michael Benjamin
5个回答

54

padding-bottom 诀窍是最常用的方法来实现这一点。

您可以将其与 Flexbox 和 CSS Grid 结合使用,并且由于在旧版浏览器上使用百分比的 margin/padding 会导致不一致的结果(请参见下面的编辑注释),因此可以添加一个额外的包装器,或者像这里一样使用伪元素,以便带有百分比的元素不是 flex/grid 项目。


编辑:注意,规范已经进行了更新,现在在 flex/grid 项目上使用时应该得到一致的结果。请注意,旧版本仍存在问题。


请注意,如果要向 content 元素添加内容,则需要将其绝对定位以保持正方形的纵横比。

演示 - Flexbox

编辑 2:在评论中,我被问及如何拥有居中文本,因此我在下面的代码段中添加了它。

.square-container {
  display: flex;
  flex-wrap: wrap;
}

.square {
  position: relative;
  flex-basis: calc(33.333% - 10px);
  margin: 5px;
  border: 1px solid;
  box-sizing: border-box;
}

.square::before {
  content: '';
  display: block;
  padding-top: 100%;
}

.square .content {
  position: absolute;
  top: 0; left: 0;
  height: 100%;
  width: 100%;

  display: flex;               /* added for centered text */
  justify-content: center;     /* added for centered text */
  align-items: center;         /* added for centered text */
}
<div class="square-container">

  <div class="square">
    <div class="content">
      <span>Some centered text</span>
    </div>
  </div>

  <div class="square">
    <div class="content spread">
    </div>
  </div>

  <div class="square">
    <div class="content column">
    </div>
  </div>

  <div class="square">
    <div class="content spread">
    </div>
  </div>

  <div class="square">
    <div class="content column">
    </div>
  </div>

</div>

CSS Grid 版本

.square-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
  grid-gap: 10px;
}

.square {
  position: relative;
  border: 1px solid;
  box-sizing: border-box;
}

.square::before {
  content: '';
  display: block;
  padding-top: 100%;
}

.square .content {
  position: absolute;
  top: 0; left: 0;
  height: 100%;
  width: 100%;
}
<div class="square-container">

  <div class="square">
    <div class="content">
    </div>
  </div>

  <div class="square">
    <div class="content spread">
    </div>
  </div>

  <div class="square">
    <div class="content column">
    </div>
  </div>

  <div class="square">
    <div class="content spread">
    </div>
  </div>

  <div class="square">
    <div class="content column">
    </div>
  </div>

</div>


2
到目前为止,我测试过的所有浏览器都能正常工作。谢谢。 - Stretch0
1
这个解决方案在任何浏览器中都像魅力一样工作,哇!猜猜我是用Bootstrap实现的,而且像魅力一样工作! - Mr. Pyramid
LGSon,你能否添加一些关于这个魔法是什么以及它如何工作的解释吗? - aaaidan
@aaaidan 主要的魔法在于“填充”。为了保持元素为正方形,其高度和宽度应该相同。例如,padding-top 起作用的原因是,当使用百分比设置顶部(或底部)填充时,它使用自己的宽度来计算填充的值,这也将成为其高度,因此它变成了一个正方形。第二部分,在处理其内容时,需要将其绝对定位,以便不影响正方形的内容,否则填充计算也需要考虑到这一点(这需要使用脚本)。 - Asons
我该如何在 flex 项目中垂直对齐文本? - FluffyBeing
更新了答案,并附上了一个垂直对齐的文本示例。 - Asons

15

尝试使用视窗百分比单位

jsFiddle

.square-container {
  display: grid;
  grid-template-columns: repeat(3, 30vw);
  grid-template-rows: 30vw;
  grid-gap: 2.5vw;
  padding: 2.5vw;
  background-color: gray;
}

.square {
  background-color: lightgreen;
}

body {
  margin: 0; /* remove default margins */
}
<div class="square-container">
  <div class="square">
    <div class="content"></div>
  </div>
  <div class="square">
    <div class="content spread"></div>
  </div>
  <div class="square">
    <div class="content column"></div>
  </div>
</div>

从规范中得知:

5.1.2. 视口百分比长度:单位 vwvhvminvmax

视口百分比长度相对于最初包含块的大小,当最初包含块的高度或宽度改变时,它们会相应缩放。

  • vw单位 - 相当于最初包含块宽度的1%。
  • vh单位 - 相当于最初包含块高度的1%。
  • vmin单位 - 相当于vwvh中较小的那个。
  • vmax单位 - 相当于vwvh中较大的那个。

4
很不幸,.square-container 嵌套在几个容器内,因此使用 vw 会导致正方形超出 .square-container 包装器的范围。 - Stretch0
发布完整演示。这可能是可以克服的问题。 - Michael Benjamin
我非常喜欢这个答案,因为它不需要填充技巧。 - Stuart Casarotto
这个解决方案的浏览器支持情况如何? - FluffyBeing
1
如果存在垂直滚动条,那么该解决方案将不再起作用,因为100vw将变为正文宽度+滚动条宽度。以下是与上面相同的示例,其中包含破坏显示的垂直滚动条:https://jsfiddle.net/0obt9cvs/3/ - Etienne Coumont

7
您可以利用 padding 基于宽度进行计算的事实,并直接将 padding-top:100%设置为 square 网格项(现在网格项将成为正方形)。


2019年更新

请注意,对于 flex项目 以及先前的网格项,这不起作用-请参见此答案评论中链接的帖子:

现在,由于浏览器(较新版本)之间达成了关于 flex项目 网格项目具有相同行为的共识,因此您可以使用此解决方案。

请参见下面的演示:

.square-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
  grid-gap: 10px;
}

.square {
  background: cadetblue;
  padding-top: 100%; /* padding trick directly on the grid item */
  box-sizing: border-box;
  position: relative;
}

.square .content { /* absolutely positioned */
  position: absolute;
  top: 0;
  right:0;
  left: 0;
  bottom: 0;
}
<div class="square-container">
  <div class="square">
    <div class="content"> some content here</div>
  </div>
  <div class="square">
    <div class="content"> some content here</div>
  </div>
  <div class="square">
    <div class="content"> some content here</div>
  </div>
  <div class="square">
    <div class="content"> some content here</div>
  </div>
  <div class="square">
    <div class="content column">some content here and there is a lot of text here</div>
  </div>
  <div class="square">
    <div class="content spread">text</div>
  </div>
  <div class="square">
    <div class="content column">some text here</div>
  </div>
</div>


1
作者应该完全避免在网格项的填充或边距中使用百分比,因为它们会在不同的浏览器中产生不同的行为。 - Michael Benjamin
在 Firefox 上试一下。 - Michael Benjamin
使用flexbox时遇到了同样的问题:https://dev59.com/81oV5IYBdhLWcg3wCrAn - Michael Benjamin
1
由于解决方案现在是有效的,我已经取消了答案的删除操作,谢谢大家 :) - kukkuz
1
这是一个完美的响应式正方形网格,运行非常良好。 - NickCoder

6

使用 CSS aspect-ratio 属性,你可以在所有现代浏览器中实现此功能。

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 5px;
}

.container div {
  aspect-ratio: 1 / 1;
  
  /* Styles below just for demo */
  background-color: orange;
  color: white;
  font-family: Arial;
  display: flex;
  justify-content: center;
  align-items: center;
}
<div class="container">
  <div>A</div>
  <div>B</div>
  <div>C</div>
  <div>D</div>
  <div>E</div>
  <div>F</div>
  <div>G</div>
</div>


1
请注意,这需要 Safari 15+。请参阅 https://caniuse.com/mdn-css_properties_aspect-ratio。 - Jo Liss

-1
几天来,我一直惊讶于在2020年没有简单的解决方案。我相信使用CSS网格布局会很容易... Ason提供的Flexbox解决方案是唯一一个适用于所有浏览器的解决方案。在Stack上,我找到了另一个使用padding-bottom: 100%的CSS网格解决方案,但它在Firefox中不起作用(页脚下面有很多空白)。
这是我对问题的看法,我认为这是我这些天遇到的所有解决方案中最简单的解决方案。
Codepen上的CSS Grid解决方案: https://codepen.io/abudimir/pen/ExKqyGp
<div class="square-container">

2
您正在使用方形图像 - 因此,这不适用于非方形缩放的图像; - low_rents

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