在只有 CSS 的 flex 布局的 row/column 中,让某些项目沿着横轴堆叠
Have certain items stack along the cross axis within a row/column of a flex layout with only CSS
有一次我曾考虑过 CSS 规范建议,但后来我想可能已经有一个我缺少的解决方案了。我正在谈论的那种布局的示例如下所示:
+-----------+---+
| 1 | 6 |
+---+---+---+ |
| 2 | 3 | 4 +---+
+---+---+---+ 7 |
| 5 | |
+-----------+---+
问题是左列中间的那三个框沿交叉轴堆叠,我在 CSS 中找不到执行此操作的机制。我知道这可以通过 div 环绕这 3 个项目来完成,这是一个行方向 flex 布局,但这种方法破坏了 flex 布局的灵活性,因为这些项目不能再围绕外部布局重新排序并且 column/row 中断不能再在它们之间发生。那么,如何仅使用 CSS 来实现这一点,从而使 flex 布局保持灵活?
HTML:
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
CSS:
#flex-layout {
display: flex;
flex-direction: column;
height: 300px;
width: 400px;
flex-wrap: wrap;
align-items: stretch;
}
#item1 {
flex: 0 0 100px;
width: 300px;
}
#item2 {
flex: 0 0 100px;
width: 100px;
}
#item3 {
flex: 0 0 100px;
width: 100px;
}
#item4 {
flex: 0 0 100px;
width: 100px;
}
#item5 {
flex: 0 0 100px;
}
#item6 {
flex: 0 0 150px;
width: 100px;
}
#item7 {
flex: 0 0 150px;
}
使用多个 flex 容器会更容易。
但是如果你想要一个容器,你仍然可以做到,前提是:
- 已知6和7的宽度
- 2、3、4的身高已知
那么,你可以
使用行布局
┌─┬─┬─┬─┬─┬─┬─┐
│1│2│3│4│5│6│7│
└─┴─┴─┴─┴─┴─┴─┘
重新排序弹性项目:1,6,2,3,4,5,7
┌─┬─┬─┬─┬─┬─┬─┐
│1│6│2│3│4│5│7│
└─┴─┴─┴─┴─┴─┴─┘
允许使用 flex-wrap: wrap
换行。
使用伪元素在2前4后强制换行
┌─┬─┐
│1│6│
├─┼─┼─┐
│2│3│4│
├─┼─┼─┘
│5│7│
└─┴─┘
在 6 和 7 上使用 flex-grow: 0
。在其他的上使用 flex-grow: 1
。
┌─────────┬─┐
│ 1 │6│
├───┬───┬─┴─┤
│ 2 │ 3 │ 4 │
├───┴───┴─┬─┤
│ 5 │7│
└─────────┴─┘
将所需的 w
设置为 6 和 7。将 margin-right: w
添加到 4
┌─────┬───┐
│ 1 │ 6 │
├─┬─┬─┼───┘
│2│3│4│
├─┴─┴─┼───┐
│ 5 │ 7 │
└─────┴───┘
设置所需的高度,h
为2、3和4。将margin-bottom: -h/2
添加到6,将margin-top: -h/2
添加到7。
┌─────┬───┐
│ 1 │ 6 │
├─┬─┬─┤ │
│2│3│4├───┤
├─┴─┴─┤ 7 │
│ 5 │ │
└─────┴───┘
此外,将 width
或 max-width
添加到 2,3,4 可能是个好主意。否则,如果它们的内容足够宽,它们将被放在不同的行中,破坏布局。
代码如下:
#flex-layout {
display: flex; /* Magic begins */
flex-wrap: wrap; /* Multiline */
}
#item1 { order: 1; }
#item6 { order: 2; }
#item2 { order: 3; }
#item3 { order: 4; }
#item4 { order: 5; }
#item5 { order: 6; }
#item7 { order: 7; }
#flex-layout > div {
border: 1px solid;
box-sizing: border-box;
}
#item2, #item3, #item4 {
height: 50px; /* h */
}
#item6 {
margin-bottom: -25px; /* -h/2 */
}
#item7 {
margin-top: -25px; /* -h/2 */
}
#item1, #item2, #item3, #item4, #item5 {
flex-grow: 1;
}
#item6, #item7 {
width: 25%; /* w */
flex-grow: 0;
}
#item4 {
margin-right: 25%; /* w */
}
#flex-layout:before {
/* Force line break before #item2 */
content: '';
width: 100%;
order: 3;
}
#flex-layout:after {
/* Force line break after #item4 */
content: '';
width: 100%;
order: 5;
}
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
我的解决方案使用绝对位置。
#flex-layout {
border: 4px solid tomato;
width: 100%;
display: flex;
flex-flow: row wrap;
position: relative;
min-width: 200px;
}
#flex-layout div {
box-sizing: border-box;
border: 4px solid white;
background: #ddd;
padding: 1em;
text-align: center;
flex: 0 1 25%;
}
#flex-layout #item1 {
flex: 0 1 75%;
margin-right: 25%;
}
#flex-layout #item5 {
flex: 0 1 75%;
}
#flex-layout #item6 {
width: 25%; height: 50%;
position: absolute;
top: 0; right: 0;
}
#flex-layout #item7 {
width: 25%; height: 50%;
position: absolute;
top: 50%; right: 0;
}
<body>
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
以防万一现在有人碰巧遇到这种情况,实现这种布局的一种较简单的方法是较新的网格布局系统。网格更适合这种布局,而 flex 更适合在单个 row/column 中分布项目(包括垂直居中元素之类的东西)或者当项目沿 flex 轴的大小不一致并将它们彼此排列时跨多个 rows/columns 不是问题。
#grid-layout {
display: grid;
grid-gap: 1em;
/* 4 columns each 25% width - 3/4 of an em for the grid-gap */
grid-template-columns: repeat(4, calc(25% - (3em / 4)));
grid-template-rows: 10em 4.5em 4.5em 10em;
}
#grid-layout > div {
border: solid 1px black;
padding: .5em;
}
#item1 { grid-area: 1 / 1 / span 1 / span 3; }
#item2 { grid-area: 2 / 1 / span 2 / span 1; }
#item3 { grid-area: 2 / 2 / span 2 / span 1; }
#item4 { grid-area: 2 / 3 / span 2 / span 1; }
#item5 { grid-area: 4 / 1 / span 1 / span 3; }
#item6 { grid-area: 1 / 4 / span 2 / span 1; }
#item7 { grid-area: 3 / 4 / span 2 / span 1; }
<div id="grid-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
有一次我曾考虑过 CSS 规范建议,但后来我想可能已经有一个我缺少的解决方案了。我正在谈论的那种布局的示例如下所示:
+-----------+---+
| 1 | 6 |
+---+---+---+ |
| 2 | 3 | 4 +---+
+---+---+---+ 7 |
| 5 | |
+-----------+---+
问题是左列中间的那三个框沿交叉轴堆叠,我在 CSS 中找不到执行此操作的机制。我知道这可以通过 div 环绕这 3 个项目来完成,这是一个行方向 flex 布局,但这种方法破坏了 flex 布局的灵活性,因为这些项目不能再围绕外部布局重新排序并且 column/row 中断不能再在它们之间发生。那么,如何仅使用 CSS 来实现这一点,从而使 flex 布局保持灵活?
HTML:
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
CSS:
#flex-layout {
display: flex;
flex-direction: column;
height: 300px;
width: 400px;
flex-wrap: wrap;
align-items: stretch;
}
#item1 {
flex: 0 0 100px;
width: 300px;
}
#item2 {
flex: 0 0 100px;
width: 100px;
}
#item3 {
flex: 0 0 100px;
width: 100px;
}
#item4 {
flex: 0 0 100px;
width: 100px;
}
#item5 {
flex: 0 0 100px;
}
#item6 {
flex: 0 0 150px;
width: 100px;
}
#item7 {
flex: 0 0 150px;
}
使用多个 flex 容器会更容易。
但是如果你想要一个容器,你仍然可以做到,前提是:
- 已知6和7的宽度
- 2、3、4的身高已知
那么,你可以
使用行布局
┌─┬─┬─┬─┬─┬─┬─┐ │1│2│3│4│5│6│7│ └─┴─┴─┴─┴─┴─┴─┘
重新排序弹性项目:1,6,2,3,4,5,7
┌─┬─┬─┬─┬─┬─┬─┐ │1│6│2│3│4│5│7│ └─┴─┴─┴─┴─┴─┴─┘
允许使用
flex-wrap: wrap
换行。使用伪元素在2前4后强制换行
┌─┬─┐ │1│6│ ├─┼─┼─┐ │2│3│4│ ├─┼─┼─┘ │5│7│ └─┴─┘
在 6 和 7 上使用
flex-grow: 0
。在其他的上使用flex-grow: 1
。┌─────────┬─┐ │ 1 │6│ ├───┬───┬─┴─┤ │ 2 │ 3 │ 4 │ ├───┴───┴─┬─┤ │ 5 │7│ └─────────┴─┘
将所需的
w
设置为 6 和 7。将margin-right: w
添加到 4┌─────┬───┐ │ 1 │ 6 │ ├─┬─┬─┼───┘ │2│3│4│ ├─┴─┴─┼───┐ │ 5 │ 7 │ └─────┴───┘
设置所需的高度,
h
为2、3和4。将margin-bottom: -h/2
添加到6,将margin-top: -h/2
添加到7。┌─────┬───┐ │ 1 │ 6 │ ├─┬─┬─┤ │ │2│3│4├───┤ ├─┴─┴─┤ 7 │ │ 5 │ │ └─────┴───┘
此外,将
width
或max-width
添加到 2,3,4 可能是个好主意。否则,如果它们的内容足够宽,它们将被放在不同的行中,破坏布局。
代码如下:
#flex-layout {
display: flex; /* Magic begins */
flex-wrap: wrap; /* Multiline */
}
#item1 { order: 1; }
#item6 { order: 2; }
#item2 { order: 3; }
#item3 { order: 4; }
#item4 { order: 5; }
#item5 { order: 6; }
#item7 { order: 7; }
#flex-layout > div {
border: 1px solid;
box-sizing: border-box;
}
#item2, #item3, #item4 {
height: 50px; /* h */
}
#item6 {
margin-bottom: -25px; /* -h/2 */
}
#item7 {
margin-top: -25px; /* -h/2 */
}
#item1, #item2, #item3, #item4, #item5 {
flex-grow: 1;
}
#item6, #item7 {
width: 25%; /* w */
flex-grow: 0;
}
#item4 {
margin-right: 25%; /* w */
}
#flex-layout:before {
/* Force line break before #item2 */
content: '';
width: 100%;
order: 3;
}
#flex-layout:after {
/* Force line break after #item4 */
content: '';
width: 100%;
order: 5;
}
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
我的解决方案使用绝对位置。
#flex-layout {
border: 4px solid tomato;
width: 100%;
display: flex;
flex-flow: row wrap;
position: relative;
min-width: 200px;
}
#flex-layout div {
box-sizing: border-box;
border: 4px solid white;
background: #ddd;
padding: 1em;
text-align: center;
flex: 0 1 25%;
}
#flex-layout #item1 {
flex: 0 1 75%;
margin-right: 25%;
}
#flex-layout #item5 {
flex: 0 1 75%;
}
#flex-layout #item6 {
width: 25%; height: 50%;
position: absolute;
top: 0; right: 0;
}
#flex-layout #item7 {
width: 25%; height: 50%;
position: absolute;
top: 50%; right: 0;
}
<body>
<div id="flex-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>
以防万一现在有人碰巧遇到这种情况,实现这种布局的一种较简单的方法是较新的网格布局系统。网格更适合这种布局,而 flex 更适合在单个 row/column 中分布项目(包括垂直居中元素之类的东西)或者当项目沿 flex 轴的大小不一致并将它们彼此排列时跨多个 rows/columns 不是问题。
#grid-layout {
display: grid;
grid-gap: 1em;
/* 4 columns each 25% width - 3/4 of an em for the grid-gap */
grid-template-columns: repeat(4, calc(25% - (3em / 4)));
grid-template-rows: 10em 4.5em 4.5em 10em;
}
#grid-layout > div {
border: solid 1px black;
padding: .5em;
}
#item1 { grid-area: 1 / 1 / span 1 / span 3; }
#item2 { grid-area: 2 / 1 / span 2 / span 1; }
#item3 { grid-area: 2 / 2 / span 2 / span 1; }
#item4 { grid-area: 2 / 3 / span 2 / span 1; }
#item5 { grid-area: 4 / 1 / span 1 / span 3; }
#item6 { grid-area: 1 / 4 / span 2 / span 1; }
#item7 { grid-area: 3 / 4 / span 2 / span 1; }
<div id="grid-layout">
<div id="item1">1</div>
<div id="item2">2</div>
<div id="item3">3</div>
<div id="item4">4</div>
<div id="item5">5</div>
<div id="item6">6</div>
<div id="item7">7</div>
</div>