α 产品经理有个需求-多列布局的实现
- 产品-彦祖 :
- 家辉啊,我需要一个这样的场景展示数据,可以
自定义列数,后端数据返回的就是数组,你看你前端咋弄啊
- 家辉啊,我需要一个这样的场景展示数据,可以
- 切图仔-渣渣辉 :
- 好的彦祖,
自定义多列嘛简单。思考 ing: 我有一个数组list,输入对应的列数col,就可以展示对应的列数,大概的demo我都写好了在下面
- 好的彦祖,
const cols:number = 3;
const list:Array<any> = [1,2,3,4,5,6,7]
<MultiBox columns={cols} list={list}/>
// show list
1 | 2 | 3
4 | 5 | 6
7 | |
β 思考🤔 column-conut 实现
- 多列展示,这还不简单嘛,flex 就是天然的多列啊,看了下文档发现不能自定义列数;想起常用的
column-countcss 属性,先用字符串试试, 在实际使用数组的时候发现不好使;- 详情查看 codesandbox
- 我们来看看
column-count的定义
column-count CSS属性,描述元素的列数。
column-count: 3;
column-count: auto;
Block containers except table wrapper boxes
定义: 是个严格的正数 ,用来描述元素内容被划分的理想列数. 假如 column-width (en-US)也被设置为非零值, 此参数仅表示所允许的 "最大列数"
• 注意上面的 最大列数 这里就是 坑 了,你指定的 number 并不一定是现在的列数,而是最大列数
import React from "react";
type IMultiBoxProps = {
cols?: number;
list: string | Array<any>;
};
const MultiBox = (props: IMultiBoxProps) => {
const { cols = 1, list } = props;
return (
<div
style={{
columnCount: cols
}}
>
{Array.isArray(list) ? list.join("") : list}
</div>
);
};
export default function App() {
const defaultMultiBox1Props = {
cols: 3,
list:
"当我年轻的时候,我梦想改变这个世界;当我成熟以后,我发现我不能够改变这个世界,我将目光缩短了些,决定只改变我"
};
const defaultMultiBox2Props = {
cols: 3,
list: [1, 2, 3, 4, 5, 6, 7, 8, 9]
};
return (
<div className="App">
<h1>Test MultiBox</h1>
<MultiBox {...defaultMultiBox1Props} />
<MultiBox {...defaultMultiBox2Props} />
</div>
);
}
改进
- 既然 div 不行,ul>li 的文字排列可以吧, 把 list 的内容放进 li 中,想到了就做;初看没问题,搞定;
const MultiBox = (props: IMultiBoxProps) => {
const { cols = 1, list } = props;
return (
<div>
<ul style={{
columnCount: cols
}}>
{list.map(val => <li>{val}</li>)}
</ul>
</div>
);
};
column-count 简易算法
- 彦祖:渣渣辉啊,你这个 5 列的时候有 bug 啊!是不是我操作有问题?
list = [1, 2, 3, 4, 5, 6, 7, 8, 9];
<MultiBox cols={3} list={list} />
// 3 列没问题
| 1 | 4 | 7 |
| 2 | 5 | 8 |
| 3 | 6 | 9 |
<MultiBox cols={6} list={list} />
// 只有 5 列 ?
| 1 | 3 | 5 | 7 | 9
| 2 | 4 | 6 | 8 |
这里就要提到上面的 此参数仅表示所允许的 "最大列数" 这个坑了
- 我们来看
column-count的计算方法, 首先计算每列可以承载最大的 items 数, 我们来试着模拟计算一下
const cols = 6;
const list = [1,2,3,4,5,6,7,8,9];
const result = [];
const countSize = Math.ceil(list.length / cols); // 2
for (let i = 0, len = list.length; i < len; i += countSize) {
result.push(list.slice(i, i + countSize));
}
// [1,2]
// [3,4]
// [5,6]
// [7,8]
// [9]
反思 chunk 二维数组实现
- 可以看到上面只有 5 列,问题就出现了,当我们的承载数足够的时候,这种按位优先补偿的算法就有明显的问题,但是也符合上面提到的最大列数原则,但是这里 彦祖 可是要求符合
cols的数量。 - column-count 不能满足,既然我们已经透析了它的算法为什么不改进一下啦?col 和 row 的网格布局,本质可以看做一个
二维数组,我们可以先搞一个table[cols]的数组,然后按照每列插值保证列数优先,就安全了,能保证定义多少列就展示多少列。
const cols = 6;
const list = [1,2,3,4,5,6,7,8,9];
function chunker(cols, lists){
const table = new Array(cols);
for (let i = 0; i < table.length; i++) {
table[i] = new Array();
}
while (lists.length > 0) {
// insert one rows
for (let col = 0; col < table.length; col++) {
table[col].push(lists.shift());
}
}
return table.filter(Boolean);
}
chunker(cols, list)
// [1,7]
// [2,8]
// [3,9]
// [4]
// [5]
// [6]
- css 和 tsx 模块实现
// css
.flex-direction-column{
flex-direction: column;
}
.flex {
display: flex;
}
// tsx
const MultiBox = (props: IMultiBoxProps) => {
const { cols = 1, list } = props;
const chunks = chunker(cols, list);
return (
<div className="flex">
{chunks.map(col =>
<div className="flex-direction-column">
{col.map(item => item)}
</div>
)}
</div>
);
};
γ gird 布局 实现
- 哪还有什么 css 属性能直接展示自定义多列啦?除了 flex,当然还有 gird 网格布局了,这下马上去看了 gird 的属性文档;
- 遇事不决 阮一峰教程
- 发现了这样一个属性,这不完美解决了多列布局的问题嘛
.gird-layout {
display: grid;
grid-template-columns: repeat(3, 33.33%);
// 三列布局
}
- 完整代码(伪代码)
const MultiBox = (props: IMultiBoxProps) => {
const { cols = 1, list } = props;
return (
<div
style={{
display: 'gird',
gridTemplateColumns: repeat(cols, 1/cols);
}}
>
{list.map( item => <div>{item}</div>)}
</div>
);
};
- 留个问题:这里的
gridTemplateColumns: repeat(cols, 1/cols);repeat 不能直接使用, gird 方法是找到了,有什么优雅的方法解决这个问题啦?