背景
来自ui设计的一套文字与标签组合规则。
- 容器宽度一定,优先展示文字部分,缩略展示标签
- 当文字本身长度超出最大宽度,hover 时使用tooltip展示全部文字及标签
如图:(手动画图,暂时忽略间距)
思路1:
- totalWidth: 获取item的总宽度,减去左侧icon的宽度和左间距
- 根据获取的name字符去计算宽度,计算方法
- 根据获取的label字符去计算宽度
- 所有的宽度计算好再传入一个壳子组件,壳子组件里的主要就是渲染+拿到宽度根据组合规则书写逻辑
思路2:
- totalWidth: 获取item的总宽度,减去左侧icon的宽度和左间距
- 在组件内部定义两个ref,根据ref去获取元素的宽度
- nameWidth:nameRef.current?.offsetWidth labelWidth:labelRef.current?.offsetWidth
- 直接在壳子内部根据组合规则书写逻辑
组合规则
- nameRef的宽度+label的宽度+margin<limitWith 全部展示
- nameRef的宽度+label的宽度+margin>limitWith 先展示name,再看limitWith-nameRef的宽度-margin<label的宽度 label多的部分...
- nameRef的宽度>limitWith 展示tooltip 不展示label
根据字符计算宽度方法
const calculateCharacterLength = (words: string | number) => {
let nums = 0;
for (let i = 0, length = words?.toString().length; i < length; i++) {
let leg = words.toString().charCodeAt(i);
if (leg > 255) {
nums += 2;
} else {
nums += 1;
}
}
return nums;
};
const calculateWordsFontSize = (nums: number, fontSize: number) => {
return Math.ceil(nums / 2) * fontSize;
};
const calculateWordsLength = (words: string | number, fontSize: number) => {
const nums = calculateCharacterLength(words);
return calculateWordsFontSize(nums, fontSize);
};
代码实现1
type Props = {
name: ReactNode;
label: string;
limitWidth: number;
marginWidth?: number;
nameWidth?: number;
labelWidth?: number;
classes?: Record<string, any>; //自定义样式
};
const ItemWrapWithCalculate: FC<Props> = props => {
const {
name,
label,
limitWidth = 0,
marginWidth = 0,
nameWidth=0,
labelWidth=0,
classes: classesCustom = {}
} = props;
//label宽度不够的情况,超出部分...展示
const showFullLabel = useMemo(() => {
return limitWidth - nameWidth - marginWidth < labelWidth;
}, [limitWidth, nameWidth, marginWidth, labelWidth]);
const showLabel = useMemo(() => {
//全部展示
if (nameWidth + labelWidth + marginWidth < limitWidth) {
return limitWidth - (nameWidth + marginWidth);
}
if (nameWidth > limitWidth) {
return 0;
}
//将所剩宽度给label
if (showFullLabel) {
return limitWidth - nameWidth - marginWidth;
}
return 0;
}, [nameWidth, labelWidth, showFullLabel]);
//剩余的宽度与lable判断 剩余多就全部按照label的现实
return (
<>
<span
style={{ width: `${nameWidth > limitWidth ? limitWidth : nameWidth}px` }}
className={clsx(styles.namewrap, showLabel < 36 && styles.fullshow)}
>
{name}
</span>
{showLabel !== 0 && showLabel > 16 && (
<span
style={{ width: `${labelWidth < showLabel ? labelWidth : showLabel}px` }}
className={clsx(styles.label, showLabel && styles.fullshow, classesCustom.customlabel)}
>
{label}
</span>
)}
</>
);
};
样式
.center {
display: flex;
align-items: center;
}
.root {
.center;
& span {
white-space: nowrap;
}
}
.fullshow {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.label {
height: 20px;
line-height: 20px;
padding: 0 5px;
color: #6c798c;
font-size: 12px;
background: #ffffff;
border: 1px solid #e9ecf0;
border-radius: 2px 2px 2px 2px;
text-align: center;
}
结论:
调用时,直接用这个组件包要渲染的列表item。展示上比较可控,有误差可以在外部计算的时候略加一点误差值,达到预期效果。
代码实现2
纯Css 更简单,性能更好
<div className={classes.itemwrap}>
{option.desc}
<span>{option.label}</span>
</div>
//css
.itemwrap {
max-width:240px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
& > span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 4px;
}
}
结论
最外层需要一个最大宽度的限制,然后内层应用超出的css,span也应用超出css,这样最外层容器宽度一定,优先展示文字部分,缩略展示标签,如果都够,就都会正常展示。