场景
在业务中,需要在一个卡片组件中展示多个标签,标签组件高度相同,宽度和出现顺序不同,要求标签只能在有限的空间内展示(比如满足放入两行标签的空间),并在满足最大展示数量的情况下,对超出部分进行隐藏,然后通过鼠标 hover 的方式展示隐藏内容
思路
标签的高度相同,空间内可支持的标签行数固定,多标签的内容可以转化为一个数组数据,所以可把问题概括为:获取在给定的行数内,按顺序放入的不同宽度标签的最大数量,则余下的内容为需要隐藏的标签内容
有明确的输入输出,可以再进一步转化为一个算法问题:给定一个数字数组、限制的行数和行最大值,将数组中的数字依次放入行中并相加,相加的值不能超过行最大值,如果加入下一个数字后超出最大值,则该行不再计入该数字,并在下一行重新开始放入计算,如果超出行数,则停止,最后统计可以放入给定行中的最大数字数量
typescript
const calculateLength = (rowNums: number[], rowCount: number, rowMaxValue: number) => {
let count = 0; // 统计数字数量
let currRow = 0; // 当前行数
let currSum = 0; // 当前行的累加和
for (let i = 0; i < rowNums.length; i++) {
// 当前值大于最大值,停止
if (rowNums[i] > rowMaxValue) {
break;
}
// 如果加入下一个数字后超出最大值,重新开始计算
if (currSum + rowNums[i] > rowMaxValue) {
currRow++;
currSum = 0;
}
// 如果行数超过设定的行数,停止统计
if (currRow >= rowCount) {
break;
}
// 更新当前行的累加和
currSum += rowNums[i];
// 统计数字数量
count++;
}
return count;
};
项目中的实现
在明确思路后的,在项目中的实现只需要准备好算法中的需要的参数,再代入使用即可 需要获取的内容有:
- 标签宽度数组
- 行最大值
- 行数:行数是基于设计稿限制的,可以直接使用常量定义
标签宽度数组、行最大值获取
标签宽度数组的获取似乎有点矛盾,既要隐藏超出的标签,但又需要标签渲染为实际的 Dom 后才可以获取其宽度。这里直接采用比较粗暴的方式,使用绝对定位,脱离文档流全量渲染标签内容并隐藏,只用于获取标签的宽度
tsx
<div>
{renderTag()}
<div ref={ref} style={{ positions: 'absolute', opacity: 0 }}>
{renderAllTag()}
</div>
</div>
基于 ref 即可获行最大值,即标签父级 Dom 的宽度
typescript
const containerWidth = ref.current.clientWidth
在基于该 Dom 的 children 内容,获取标签的宽度数组
typescript
const tagChildren = ref.current.children;
const itemWidthList = Array.from(tagsChildren).map(item => item.clientWidth)
最后带入上述 calculateLength
方法,计算出实际需要渲染的最大数量,并对后端返回的标签数据进行分割处理
最后
实现时还有一些需要注意的点:
- 计算中,当单个标签的宽度大于行最大宽度的时候,需要直接返回
- 计算出可以放入给定行中的最大数量后,还需要考虑给展示隐藏数量的 Dom 留空间,可以在计算结果的基础上,再减 1或减 2