在工作中遇到一个需求,多行显示一段文字,超过一定的行数后显示省略号和“查看全文”,如图:
由于这里需要添加“查看全文”四个字,所以我不能像以前一样使用css进行多行溢出省略处理。最后我使用vue指令实现了该需求,多次渲染文本,通过getComputedStyle获取渲染后的高度与单行情况下的高度进行对比,使用二分查找找出刚好是所需行数的下标,对文本进行截取。
以下是代码实现:
<p v-cut-text="{text: text,rows:3}">
directives: {
cutText: {
inserted: (el, binding, vnode) => {
if (!binding.value.rows) return
//先获取到单行情况下的高度
el.innerHTML = 'a'
let rowHeight = getComputedStyle(el).height.split('px')[0]
//获取所需行数
let rowNums = binding.value.rows
//获取所需行数会显示的高度,高度可能有小数,存在浮点精点丢失,此处处理为整数
let totalHeight = parseInt(rowHeight * rowNums)
//获取完整文本会显示的高度
el.innerHTML = binding.value.text
let allTextHeight = parseInt(getComputedStyle(el).height.split('px')[0])
//判断完整文本是否会超出我们所需的行数,如果超出则进行截取
//这里需要注意的是
//如果指令用在行内元素上,getComputedStyle(el).height获取的高度为‘auto’
//会导致我得到的totalHeight为NaN,在二分查找时进入死循环,所以这里进行判断
if(allTextHeight <= totalHeight||getComputedStyle(el).height == 'auto') {
return
} else {
//得出下标,截取文本
let index = searchChar(binding.value.text)
el.innerHTML = binding.value.text.slice(0,index) + '...<span style="color:#979CA5;">查看全文<span>'
}
// 使用二分查找,找出刚好满足所需行数的下标
let searchChar = function(nums) {
let left = 0;
let right = nums.length;
while (left < right) {
let mid = left + Math.floor((right - left) / 2);
//我当前的需求是在末尾添加省略号和“查看全文”,所以计算高度时也需要加上
el.innerHTML = nums.slice(0,mid) + '...查看全文'
let currentHeight = parseInt(getComputedStyle(el).height.split('px')[0])
//当前截取后的文本高度与我们所需行数高度进行对比
if(currentHeight == totalHeight) {
left = mid + 1
} else if(currentHeight > totalHeight) {
right = mid
} else if(currentHeight < totalHeight) {
left = mid + 1
}
}
return left - 1;
}
}
},
}
这个指令需要十分注意的是不要在行内元素上使用,因为getComputedStyle(el).height获得的高度时auto,然后进行所需行数高度计算时时候,会得到一个NaN,进而导致在二分查找的判断中进入死循环。这里可以先改变dom的display值为block或者inline-block,最后数据在处理完成后改回去,我这里就没有去做处理。
实际处理过程:
不足的地方:
1.如果文本中存在emoji标签的Unicode,可能会被截断破坏;
2.会导致页面重绘,当原文本很长的时候,计算次数也会增加,页面如果多个地方都使用了这个指令,可能需要考虑一下性能问题;
3.改写指令到vue3中使用时,指令的钩子改为mounted,出现了获取不到dom的情况,导致计算得到的高度为auto,所以我在指令的第一行添加了await nextTick(),问题解决;