没有媒体查询,如何实现桌面端3列到移动端1列的布局?

4

我看了一些相关问题,但它们并不能完全解决我的问题。

假设我有一个网站,我希望在桌面上显示如下:

enter image description here

这很容易。grid-template-columns: repeat(3, 33%)(基本上就是这样)

然而,在移动设备上,我希望呈现以下内容:

enter image description here

问题出现在切换到单列之前:

enter image description here

我尝试使用clamp()minmax()和各种方法,但没有任何一种方法能像我想要的那样工作。是的,我完全可以使用媒体查询,但我希望使用现代CSS(如clamp、grid、minmax等)创建一个真正流动的网格/弹性布局,以便基本布局更改不需要媒体查询。

我知道这行不通,但作为一个起点,按照要求,这是我100次尝试中的一个简单版本 :) 在这个版本中,我尝试使用clamp从repeat(3)切换到repeat(1)。

.wrapper {
  display: grid;
  gap: 15px;
  grid-template-columns: repeat(clamp(1, calc(100% - 500px), 3), 33%);
}

.one {
  background: red;
}

.two {
  background: green;
}

.three {
  background: blue;
}
<div class="wrapper">
  <div class="item one"><h3>Example A</h3></div>
  <div class="item two"><h3>Example Two</h3></div>
  <div class="item three"><h3>Third Example</h3></div>
</div>


1
你能分享一下你的代码吗? - Dominik
我省略了一些内容,因为我试图理解更高层次的概念,但现在我已经包含了一个简单的示例。 - Oscar Godson
3个回答

6

更多通用规则的完整文章:https://css-tricks.com/responsive-layouts-fewer-media-queries/

这里有一个使用 max(0px, (400px - 100vw)*1000) 的想法来设置 flex-basis。这个公式会在 100vw(屏幕大小)大于 400px 的情况下给出 0px,或在相反情况下给出非常大的值,从而为每个元素创建一个大的 flex-basis 并且进行换行。只需调整 400px,它起到了 @media (max-width:400px) 的作用。

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

.container div {
  height:100px;
  border:2px solid;
  background:red;
  flex-basis:max(0px, (400px - 100vw)*1000);
  flex-grow:1;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
</div>

使用CSS grid,可以实现如下效果:

.container {
  display:grid;
  grid-template-columns:repeat(auto-fill,minmax(clamp(30%, (400px - 100vw)*1000, 100%),1fr));
  grid-gap:5px;
}

.container div {
  height:100px;
  border:2px solid;
  background:red;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
</div>

一个类似的问题,我控制最大列数而不使用媒体查询:CSS网格 - 最大列数没有媒体查询


我们可以将上述解决方案扩展到考虑更复杂的情况。

从6列到3列再到1列的移动示例:

.container {
  display:grid;
  grid-template-columns:
    repeat(auto-fill,
      minmax(clamp(clamp(15%,(800px - 100vw)*1000, 30%), (400px - 100vw)*1000, 100%)
      /* if(screen> 800px) 15% elseif(screen> 400px) 30% else 100% */
      ,1fr));
  grid-gap:5px;
}

.container div {
  height:100px;
  border:2px solid;
  background:red;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

为了理解这些值,考虑以下范围:

100%/7  100%/6  100%/5  100%/4  100%/3  100%/2  100%/1
 14.3%  16.7%    20%     25%     33.3%   50%     100%

为了获得6列,我们需要在范围内使用一个值]14.3%16.7%](我考虑了15%)。 为了获得3列,我们需要在范围内使用一个值]25% 33.3%](我考虑了30%)。
我们只需避免边缘以确保我们考虑到间隙。
一种更通用的解决方案是使用CSS变量,在此我将添加0.1%以确保值足够大以获得所需的列数,并且它可以容纳间隙。
让我们还添加一些动态着色(相关:如何根据其高度或宽度更改
元素的颜色?

.container {
  /* first breakpoint*/
  --w1:800px;
  --n1:6;
  /* second breakpoint*/
  --w2:400px;
  --n2:3;

  display:grid;
  grid-template-columns:
    repeat(auto-fill,
      minmax(clamp(clamp(100%/(var(--n1) + 1) + 0.1%, (var(--w1) - 100vw)*1000,
                         100%/(var(--n2) + 1) + 0.1%), (var(--w2) - 100vw)*1000,
                         100%), 1fr));
  grid-gap:5px;
  margin:10px 0;
}

.container div {
  height:100px;
  border:2px solid;
  background:
    linear-gradient(blue  0 0) 0 /1% calc(var(--w2) - 100vw),
    linear-gradient(green 0 0) 0 /1% calc(var(--w1) - 100vw),
    red;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="container" style="--w1:900px;--n1:8;--w2:500px;--n2:4;grid-gap:10px;">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="container" style="--w1:600px;--n1:4;--n2:2;grid-gap:2vw;">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

使用弹性盒子布局(flexbox)可以实现不同的(可能是所需的)行为,其中一行的最后一个项目将占用所有剩余空间:

.container {
  /* first breakpoint*/
  --w1:800px;
  --n1:6;
  /* second breakpoint*/
  --w2:400px;
  --n2:3;

  display:flex;
  flex-wrap:wrap;
  margin:10px 0;
}

.container div {
  height:100px;
  border:2px solid;
  margin:5px;
  flex-basis:clamp(clamp(100%/(var(--n1) + 1) + 0.1% ,(var(--w1) - 100vw)*1000, 
                         100%/(var(--n2) + 1) + 0.1%),(var(--w2) - 100vw)*1000, 
                         100%);
  flex-grow:1;
  box-sizing:border-box;
  background:
    linear-gradient(blue  0 0) 0 /1% calc(var(--w2) - 100vw),
    linear-gradient(green 0 0) 0 /1% calc(var(--w1) - 100vw),
    red;
}
<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="container" style="--w1:900px;--n1:8;--w2:500px;--n2:4;">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div class="container" style="--w1:600px;--n1:4;--n2:2;">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>


0

@Temani的回答很疯狂但也很好 :). 我需要实现一个类似的东西,用于4列布局(分别为2列和1列),但发现将15%和30%的值替换为25%和50%并不起作用。这似乎与百分比需要考虑网格间隙有关,因此@Temani的答案只能在四舍五入误差的情况下起作用。因此,基于4列网格的更健壮(甚至更疯狂)的解决方案是:

:root {
  --grid-gap: 10px;
  --grid-gap-x2: calc(var(--grid-gap));
}

.container {
  display:grid;
  grid-gap: var(--grid-gap-x2);
  grid-template-columns:
    repeat(auto-fill, minmax(clamp(clamp(calc(25% - var(--grid-gap-x2)),(800px - 100vw)*1000, calc(50% - var(--grid-gap-x2))), (400px - 100vw)*1000, 100%)
      /* if(screen> 800px) 25% elseif(screen> 400px) 50% else 100%. */
      /* (Subtracting grid gap from either side of percentage width.) */
      ,1fr));
}

答案之所以有效,是因为舍入误差。这不是错误,我依赖于1fr功能,因此我不需要考虑那个间隙,我只需要使其足够大以获得所需的列数。30%比25%大,比33%小,因此使用1fr我将获得3列。对于15%也是同样的逻辑。 - Temani Afif

0
你可以通过像这样使用网格来实现这个:

.btnContainer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); /* here you set when the width should change to be moved to the next row, in this example, the divs will move when the screen reduces size and their width is less than 200px */
}

.btnCenter3 {
  background-color: rgb(6, 198, 247);
  height: 400px;
}
.btnCenter4 {
  height: 400px;
  background-color: rgb(196, 95, 1);
}
.btnCenter5 {
  height: 400px;
  background-color: rgb(192, 231, 19);
}
<div class="btnContainer">
<div class="btnCenter3"></div>
      <div class="btnCenter4"></div>
      <div class="btnCenter5"></div>
 </div>


不理解为什么会被踩。如果您检查代码片段,它确实实现了您的要求。在桌面模式下,它将显示三列,在移动屏幕上,它将显示1列。没有使用媒体查询! - Alex Yepes
2
我没有点踩,但这个程序遇到了我描述的同样问题。它在第一行是蓝棕色,然后在第二行变成了绿色。 - Oscar Godson

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