一、truncateArgs 函数
函数功能
该函数可以用于用户界面中的文本截断,确保长文本在有限的显示空间内能够适当显示,并且用户可以了解到部分文本已被省略。
函数参数
- args:参数数组。用于输出到界面上
- maxLength:当前界面上可容纳最大可输出字符长度。根据当前参数对要输出的内容进行转换(具体转换如下)
假设args中只有一个参数
let args = ['sksddsloxcc']
- 当 0 < maxLength <=3时,输出args[0]中的字符(计算公式:[args[0].slice(-availableLength)])。比如:maxLength = 1,输出 c 。maxLength = 2 输出 cc
- 当 3 < maxLength < arg[0].length时, 输出args[0]中的字符(计算公式:[“…” + args[0].slice(-availableLength + 3)])。比如:maxLength = 4 时,输出…c。maxLength = 5时,输出…cc
- 当 maxLength >= arg[0].length 时,直接返回arg[0]完整字符
假设args中有多个参数
let args = ['sksddsloxcc', 'dfdffrvxgsvchsgcvgcdcdsvcdbcuydcedvc', '2345vgsscgvschghgvghsgc', '35677vsgscysvcvgyvgv']
当多个参数时,truncateArgs 函数总是想着把多个参数内容,最大化输出到界面上,因此呈现如下规律。
- args中所有值的长度和6取最小值最后再相加的和(maxLength-参数间空格数)做比较
maxLength - lengths.length + 1 < arraySum(lengths.map(i => Math.min(i, 6)))
- 如果界面的空间放不下,则从右向左去掉一个参数,递归(这里args中数量会有变化),再做比较,如果多个最大值6相加小于maxLength - lengths.length + 1,则进入第3步
- 获取当前args数组中参数字符串长度总和并和当前maxLength - lengths.length + 1比较。如果当前内容放的下就直接返回。放不下则进入第4步
let currentLength = arraySum(args.map(a => `${a}`.length)); // Check if all fits into maxLength if (currentLength <= maxLength - lengths.length + 1) return args;
- 因为当前界面放不下当前参数,于是就截断参数(args)中最长的一个。比如:‘dfdffrvxgsvchsgcvgcdcdsvcdbcuydcedvc’,截取一次做一下比较看当前参数的总长度是否小于或等于可用长度。如果时,就进行输出。不是的话就再次进行挑选参数(args)中最长的一个(注意:本次挑选最长的一个和第一次最长的一个不一定是同一个字符,因为第一次已经对它进行了截取),进行截取
最终效果如下
源码分析
-
对当前args数组计算每一项值的长度,存为lengths,同时获取当前界面可用长度availableLength
const lengths = args.map(a => `${a}`.length); const availableLength = maxLength - lengths.length + 1;
maxLength - lengths.length + 1的意思是 空格也占用空间
-
对args数组中只有一项并且availableLength大于0
if (availableLength > 0 && args.length === 1) { if (availableLength >= args[0].length) { // 当前界面可用长度完全够用 return args; } else if (availableLength > 3) { // 当前界面可用长度大于3,则需要拦截并伴随'...' return ["..." + args[0].slice(-availableLength + 3)]; } else { // 当前界面可用长度太短了,只能显示几个字符 return [args[0].slice(-availableLength)]; // 这里是负号,注意顺序 } }
-
多参数情况。 检查lengths中每一项之和是否超过界面可用长度,超过了进行递归
if (availableLength < arraySum(lengths.map(i => Math.min(i, 6)))) { // remove args if (args.length > 1) return truncateArgs(args.slice(0, args.length - 1), maxLength); return []; }
如果lengths中长度超过6时按照6算,是为了实现在不充裕的界面可用长度下该字符最多显示的数量。就是6个字符。即‘…abc’
当前界面可用长度为16,去掉空格剩余14。因为装不下所以输出内容,通过availableLength < arraySum(lengths.map(i => Math.min(i, 6))),判断发现可以装下args数组中前3项。所以就打印了前3项内容。至于为什么第1和3项内容有’…',下面会有说明 -
检查当前lengths中所有项之和是否超过界面可用长度,没有的话就全部返回。有就需要进行截断最长的项
let currentLength = arraySum(lengths);
// Check if all fits into maxLength
if (currentLength <= availableLength) return args;
- 截断最长的项,直到总长度符合要求为止。否则进行循环♻️
while (currentLength > availableLength) {
const maxLength = Math.max(...lengths); // 获取当前最长项的长度
const shorterItems = lengths.filter(l => l !== maxLength); // 过滤得到不长的项
const nextToMaxLength =
shorterItems.length > 0 ? Math.max(...shorterItems) : 0; // 在不长项中找到次长项(比着最长项短点)
const maxReduce = maxLength - nextToMaxLength; // 获取最长项和次长项之间的差距
let maxItems = lengths.length - shorterItems.length; // 获取最长项个数,因为有可能lengths有多个最长项
let overrun = currentLength - availableLength; // 获取溢出的长度
for (let i = 0; i < lengths.length; i++) {
if (lengths[i] === maxLength) {
// 这里需要比较overrun / maxItems和maxReduce的最小值,以免多减
const reduce = Math.min(Math.floor(overrun / maxItems), maxReduce);
lengths[i] -= reduce;
currentLength -= reduce;
overrun -= reduce;
maxItems--;
}
}
}
- 对截取后的项进行输出
return args.map((a, i) => {
const str = `${a}`;
const length = lengths[i];
if (str.length === length) {
return str;
} else if (length > 5) {
return "..." + str.slice(-length + 3);
} else if (length > 0) {
return str.slice(-length);
} else {
return "";
}
});