欢迎关注我的公众号:前端侦探
最近在项目中看到这样一个布局,如下
布局本身没什么奇怪的,就是 4 * 2 的网格,比较特殊的是第二行布局是从右往左的,整体是一个这样的顺序
而这个列表是通过一个数组动态渲染的,可能有很多同学会将这个数组分成两份,然后将第二份进行反向,类似于这样
let arr1 = list.slice(0, 4)
let arr2 = list.slice(4, 8).reverse()
然后,由于第二行的第一个其实是原数组的第八个,还需要针对第二行做额外的处理,比如序列
// 第一行
第 {{ i }} 个
// 第二行
第 {{ 8 - i }} 个
而且,如果有点击事件,传值也需要额外处理,虽然也能实现,但显然是太麻烦,而且还容易出 bug。
那么,有没有其他更简单、更稳定的方式来解决呢?也就是如何让第二行子项反向呢?
一、flex 布局实现
由于这里是动态渲染,所以最理想的结构应该是这样的,直接一层循环搞定
<div class="list">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
</div>
通过 flex
或者 grid
都很容易实现4 * 2
的布局,先用 flex
实现
.list{
display: flex;
width: 600px;
gap: 20px;
flex-wrap: wrap;
}
.item{
width: calc( (100% - 60px) / 4 );
aspect-ratio: 1/1;
background: royalblue;
color: #fff;
font-size: 30px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
可以得到这样的布局
有什么办法在不改变 html
的情况下改变第二行的位置呢?🤔
在 flex
中,可以直接通过order
进行排序,order
越大,元素越靠后。
https://developer.mozilla.org/zh-CN/docs/Web/CSS/order
比如,我们将第 5 个元素的order
设置成1
.item:nth-child(5){
order: 1
}
由于默认是0
,现在第 5 个元素的order
最大,所以它跑到了最后面
按照这样的规则,我们可以将第 5、6、7、8 个子项的order
分别一次减少就行了(大于0)
.item:nth-child(5){
order: 4;
}
.item:nth-child(6){
order: 3;
}
.item:nth-child(7){
order: 2;
}
.item:nth-child(8){
order: 1;
}
这样第二行就反向了,如下
当然还可以在循环的时候,加上 CSS 变量
<div class="list">
<div class="item" style="--i: 1">1</div>
<div class="item" style="--i: 2">2</div>
<div class="item" style="--i: 3">3</div>
<div class="item" style="--i: 4">4</div>
<div class="item" style="--i: 5">5</div>
<div class="item" style="--i: 6">6</div>
<div class="item" style="--i: 7">7</div>
<div class="item" style="--i: 8">8</div>
</div>
然后可以用 calc动态去改变order
,如下
.item:nth-child(n + 5){
order: calc( 8 - var(--i))
}
同样能达到相同的效果,完整代码可以查看
二、grid 布局实现
还是同样的布局,现在换 grid 实现,正常情况下,可能会直接用repeat(4, 1fr)
来实现一个 4 * 2
的布局
.list{
display: grid;
width: 600px;
gap: 20px;
grid-template-columns: repeat(4, 1fr);
}
.item{
aspect-ratio: 1/1;
background: royalblue;
color: #fff;
font-size: 30px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
}
效果如下
那么,如何让下面一行反过来呢?
除了使用上面的方式,还可以用带有名称的grid-template-areas
来实现,比如我们这样命名
.list{
/**/
grid-template-areas:
'a1 a2 a3 a4'
'a5 a6 a7 a8';
}
这样就划分成了a1~a8
一共八块区域,为了方便映射,我们可以在生成 html
时,通过 CSS 变量带上这些名称
<div class="list">
<div class="item" style="--i: a1">1</div>
<div class="item" style="--i: a2">2</div>
<div class="item" style="--i: a3">3</div>
<div class="item" style="--i: a4">4</div>
<div class="item" style="--i: a5">5</div>
<div class="item" style="--i: a6">6</div>
<div class="item" style="--i: a7">7</div>
<div class="item" style="--i: a8">8</div>
</div>
这样就可以很方便的把每一个子项“填入”对应的区域了
.item{
/**/
grid-area: var(--i);
}
现在仍然是默认顺序,如果要改变第二行的顺序,直接grid-template-areas
就行了
.list{
/**/
grid-template-areas:
'a1 a2 a3 a4'
'a8 a7 a6 a5'; /* 把第二行反向 */
}
这样就很方便直观的改变了子项的位置了,效果如下
完整代码可以查看
三、更加灵活的“蛇形布局”
上面的例子只有两行,如果有多行呢,并且行数不定,如何处理呢?就像这样的
这种时候用 grid 可能少许不方便了(可能我还没找到精髓😂),下面用 flex 实现
目前 CSS 中并没有能够检测第几行的选择器,所以只能用其他方式。假设每一行的个数是确定的,这里是 4
,那么,第二行就是5
、6
、7
、8
,隔一行,第四行就是13
、14
、15
、16
,依次类推。
有什么方式可以匹配第几个呢?没错,就是:nth-child
,由于是隔一行,所以是每 8 个一个循环,可以这样来选择偶数行,如下
.item:nth-child(8n + 5){
/*选择第5、13、21...*/
}
.item:nth-child(8n + 6){
/*选择第6、14、22...*/
}
.item:nth-child(8n + 7){
/*选择第7、15、23...*/
}
.item:nth-child(8n + 8){
/*选择第8、16、24...*/
}
由于默认的order
是0
,如果改变其他的order
肯定会跑到后面去,为了避免影响,可以先手动设置order
.item{
/**/
order: var(--i);
}
下面要对偶数行的顺序进行调整,比如第二行
第 1 个的位置调到第4个位置,所以 order
需要加 3
第 2 个的位置调到第3个位置,所以 order
需要加 1
第 3 个的位置调到第2个位置,所以 order
需要减 1
第 4 个的位置调到第1个位置,所以 order
需要减 3
最后实现就是
.item:nth-child(8n + 5){
order: calc(var(--i) + 3)
}
.item:nth-child(8n + 6){
order: calc(var(--i) + 1)
}
.item:nth-child(8n + 7){
order: calc(var(--i) - 1)
}
.item:nth-child(8n + 8){
order: calc(var(--i) - 3)
}
这样就实现了行数不固定的“蛇形布局”,完整代码如下
四、优势和总结
这样实现有什么优势呢?很明显 JavaScript 无需关注布局,只用处理业务逻辑就行,也无需单独对第二行元素做特殊处理,特别是序列,之前很容易混乱,最重要的是实现更加清晰明了,也更加稳定。下面总结一下要点
- 用 js 来修改布局比较麻烦,而且逻辑容易混乱
- flex 中可以用 order 来改变子项位置
- 借助 CSS 变量可以更加方便地映射到每个子项
- grid 中可以用 grid-template-areas 手动指定每个子项的位置
- 蛇形布局可以用 nth-child 选中偶数项,从而改变位置
按照我的经验,布局最好 CSS 单独完成,不要让 JS 参与其中,这样逻辑也会更加清晰。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤
欢迎关注我的公众号:前端侦探
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。