从零开始Vue3+Element Plus后台管理系统(26) 多行文字展开折叠

1,673 阅读3分钟

多行文字超出部门截断,并且可以展开收起。这个需求其实很容易实现,只需要 CSS 和一点简单的 JS 就可以。本文只讨论利用 css 截断,不考虑直接截取文字(不付费不给看剩余内容)的情况。

实现一个基础版本 1.0

<div class="more-text">文字内容。。。</div>

在此使用了 css 变量,row 是可以配置的,height 根据 row 计算。 需要先在 js 中给 css 变量赋值,然后在 style 中使用

 refMoreText.value.style.setProperty('--row', props.row)
 refMoreText.value.style.setProperty('--height', textHeight + 'px')
.more-text {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  word-break: break-all;
  white-space: pre-wrap;
  -webkit-line-clamp: var(--row);
  line-clamp: var(--row);
  height: var(--height);

image.png

省略号效果实现了,然后加一个 展开/收起效果,修改html 如下: 增加几个 ref 便于计算高度,isExceed 表示是否超出高度,如果为 true,显示“展开更多”,反之不显示。

<div ref="refMoreText" class="more-text">
    <div ref="refContent" class="content">{{ text }}</div>
    <div
      ref="refTrigger"
      class="trigger text-primary trigger-bg"
      @click="handleTrigger"
      v-if="isExceed"
    >
      <span>{{ moreVisible ? '收起' : '... 展开更多' }}</span>
    </div>
  </div>
...
    // 实际内容的高度
    fullHeight = refContent.value.clientHeight
    // 文本的行高,用于计算 n 行所占的高度
    lineHeight = Number(window.getComputedStyle(refMoreText.value).lineHeight.slice(0, -2))
    // n 行的高度
    textHeight = lineHeight * props.row
    // 判断是否超出高度
    isExceed.value = textHeight < fullHeight
...

这里 css 有一个小技巧,我们把content的 position 设为 absolute,这样它不会被父元素的高度影响,始终可以获取自身实际高度。

image.png

 .content {
    // 设为absolute,避免高度被父元素影响
    position: absolute;
    height: auto;
  }

在此获取实际文本的高度,还有一个重要作用:当我们使用 transition 来实现展开折叠的动画效果,需要有开始高度和结束高度,我们知道如果把结束高度 height设为 auto,那么这个动画就出不来,展开效果比较生硬。

另一个 css 小技巧:我们给“展开更多”加了一点背景,让它和文字看起来更协调

image.png

 .trigger-bg {
    background: linear-gradient(
      to right,
      rgb(255, 255, 255, 0) 0,
      rgb(255, 255, 255, 1) 60%,
      #fff 100%
    );
  }

接下来,我们通过文本展开/折叠状态,设置对应的 css 变量

// visible 
// true:展开,false:折叠
const setCssVar = (visible: boolean) => {
  if (visible) {
    refMoreText.value.style.setProperty('--height', fullHeight + lineHeight + 'px')
    refMoreText.value.style.setProperty('--row', '')
  } else {
    refMoreText.value.style.setProperty('--row', props.row)
    // 收起在文本下面,所以额外增加一行的高度
    refMoreText.value.style.setProperty('--height', textHeight + 'px')
  }
}

至此,已经实现了 1.0 版本,但是有一点小问题:在改变窗口大小或者容器大小时,展开内容的高度不会变,收起按钮的位置也不会跟着变化。

我希望它具有更好的自适应能力。接着写 1.1 版本

增强自适应能力

通过监听内容区域的变化,实时改变元素的高度。这里使用 Resize Observer API(文档参考:developer.mozilla.org/zh-CN/docs/… resize 事件`,Resize Observer的性能更优。

let resizeObserver: ResizeObserver | null = null

onMounted(async () => {
  fullHeight = refContent.value.clientHeight

  lineHeight = Number(window.getComputedStyle(refMoreText.value).lineHeight.slice(0, -2))

  textHeight = lineHeight * props.row

  if (refTrigger.value) refTrigger.value.style['lineHeight'] = lineHeight + 'px'

  resizeObserver = new ResizeObserver((entries) => {
    for (let entry of entries) {
      fullHeight = entry.contentRect.height
      isExceed.value = textHeight < fullHeight
      setCssVar(moreVisible.value)
    }
  })
  resizeObserver.observe(refContent.value)
})

const unobserveElementSize = () => {
  if (resizeObserver) {
    resizeObserver.disconnect()
    resizeObserver = null
  }
}


onUnmounted(() => {
  unobserveElementSize()
})

OK,至此已经实现了我需要的功能

2.gif

项目地址

本项目GIT地址:github.com/lucidity99/…

如果有帮助,给个star ✨ 点个赞