Vue实现多行溢出显示省略号,并显示查看更多

1,428 阅读2分钟

在工作中遇到一个需求,多行显示一段文字,超过一定的行数后显示省略号和“查看全文”,如图:

由于这里需要添加“查看全文”四个字,所以我不能像以前一样使用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(),问题解决;