vue3.0 实现多行文本的展开收起

4,783 阅读2分钟

文本的隐藏省略是我们平时项目中最常见的需求,包括

  • 单行文本省略
  • 多行文本省略

单行文本省略

主要通过 css 来实现,代码如下

.omit {
  // 强制文字不换行显示
  white-space: nowrap;
  // 超出显示省号
  text-overflow: ellipsis;
  // 超出隐藏
  overflow: hidden;
}

多行文本省略

主要通过 css 来实现,代码如下

主要实现方案 是通过 webkit 内核来实现的,目前兼容性比较好(遇到不兼容的情况下,我们可以通过 js 来实现

.omit {
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  // 控制行数  当不兼容时  
  // 我们可以通过 js 来控制容器的高度、从而控制行数
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

多行文本省略 展开 收起

以上我们介绍了文本的省略方式, 接下来我们来看下如何实现展开收起的功能

效果图

面对这种需求的时候,单纯的依靠多行文本省略是无法满足我们的需求的

实现该功能需要考虑的点:

  • 展开 按钮的出现条件
  • 展开按钮出现之后覆盖省略号怎么处理
  • 收起 按钮出现的时候,如果能同文本一行显示,就一行显示,如果会覆盖文本就换行显示

具体代码实现如下

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      padding: 10px;
    }

    .omit_block {
      position: relative;
    }

    .text {
      font-size: 16px;
      color: #131313;
      line-height: 20px;
    }

    .text_placeholde {
      content: '占位用的,用来判断是否需要显示展开按钮';
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      opacity: 0;
      z-index: -100;
    }

    .operation_btn {
      position: absolute;
      right: 0;
      bottom: 0;
      background-color: #fff;
    }

    .omit_line1 {
      white-space: nowrap;
      text-overflow: ellipsis;
      overflow: hidden;
    }

    .omit_line2 {
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 2;
      -webkit-box-orient: vertical;
    }

    .omit_line3 {
      overflow: hidden;
      text-overflow: ellipsis;
      display: -webkit-box;
      -webkit-line-clamp: 3;
      -webkit-box-orient: vertical;
    }

    .operation_btn_placeholde {
      content: '我是占位用的, 如果收起按钮遮挡了文字就换行显示';
      display: inline-block;
      width: 32px;
      height: 20px;
      background-color: aqua;
      opacity: 0;
    }

    .flex_block {
      margin-top: 10px;
      display: flex;
      flex-flow: row nowrap;
    }

    .flex_block img {
      margin-right: 5px;
      width: auto;
      max-height: 100px
    }

    .flex_block p {
      flex: 1;
      white-space: nowrap;
      text-overflow: ellipsis;
      overflow: hidden;
    }
  </style>
</head>

<body>
  <div id="app">
    <h1>单行文本省略隐藏</h1>
    <the-text-is-omitted  :text="text"></the-text-is-omitted>

    <br />
    <br />

    <h1>两行文本省略隐藏</h1>
    <the-text-is-omitted class-name="omit_line2" :text="text"></the-text-is-omitted>

    <br />
    <br />

    <h1>三行文本省略隐藏</h1>
    <the-text-is-omitted class-name="omit_line3" :text="text2"></the-text-is-omitted>

    <br />
    <br />

    <h1>flex 下单行文本省略隐藏</h1>
    <div class="flex_block">
      <img src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1605442293766&di=9db606ad0ae5149196e7f7338d368715&imgtype=0&src=http%3A%2F%2Fimg.woyaogexing.com%2F2016%2F09%2F19%2F7bae82f53c160927%2521600x600.jpg" alt="">
      <p>无症状感染者 居住环境及密接者检测均为阴性】关注新冠肺炎疫情11月14日,成都新增境外输入无症状感染者1人:男,26</p>
    </div>
    
  </div>

  <script src="https://unpkg.com/vue@3.0.2/dist/vue.global.js"></script>
  <script>
    const { createApp, ref, onMounted, reactive, computed } = Vue

    // 自定义组件
    const TheTextIsOmitted = {
      props: {
        text: {
          type: String,
          default: true
        },
        className: {
          type: String,
          default: 'omit_line1'
        }
      },
      template: `
        <div class="omit_block">
          <p class="text" :class="{[className]: !state.isShowMore}" :ref="setEl">
            {{text}}
            <span v-if="state.isShowMore" class="text operation_btn_placeholde"></span>
          </p>

          <p class="text text_placeholde" :ref="setEl">
            {{text}}
          </p>
          
          <span v-if="state.isShowMoreBtn" class="text operation_btn" @click="setIsShowMove">{{ operationBtnText }}</span>
        </div>
      `,
      setup() {
        // 定义响应式变量
        const elArr = ref([])
        const state = reactive({
          isShowMoreBtn: false,
          isShowMore: false,
        })

        // 计算属性
        const operationBtnText = computed(() => {
          return !state.isShowMore ? ' ... 展开' : '收起'
        })

        // 声明周期 挂载
        onMounted(() => {
          const [text, textPlaceHolde] = elArr.value

          if (textPlaceHolde.offsetHeight > text.offsetHeight) {
            state.isShowMoreBtn = true
          }
        })

        // 收集 元素
        // 同 2.0 的 $refs 功能
        const setEl = el => {
          elArr.value.push(el)
        }
        
        const setIsShowMove = () => {
          state.isShowMore = !state.isShowMore
        }

        return {
          elArr,
          state,
          setEl,
          setIsShowMove,
          operationBtnText
        }
      }
    }

    const App = createApp({
      components: {
        TheTextIsOmitted
      },
      setup() {
        // 这种方式声明的变量是非响应式的
        let text = '成都新增1例无症状感染者 居住环境及密接者检测均为阴性】关注新冠肺炎疫情11月14日,成都新增境外输入无症状感染者1人:男,26 岁,尼日利亚籍。从尼日利亚出发,10月17日乘KQ882次航班抵达广州,入境后即在当地集中隔离。10月31 日隔离期满,核酸检测阴性。当日乘HU7351次航班来蓉,乘专车入住锦江宾馆,期间自行隔离。11月14日复查新冠病毒核酸阳性,诊断为无症状感染者,转送至定点医院隔离治疗。 该无症状感染者居住的环境已进行消毒,环境样本检测为阴性。'
        const text2 = '成都新增1例无症状感染者 居住环境及密接者检测均为阴性】关注新冠肺炎疫情11月14日,成都新增境外输入无症状感染者1人:男,26 岁,尼日利亚籍。从尼日利亚出发,10月17日乘KQ882次航班抵达广州,入境后即在当地集中隔离。10月31 日隔离期满,核酸检测阴性。当日乘HU7351次航班来蓉,乘专车入住锦江宾馆,期间自行隔离。11月14日复查新冠病毒核酸阳性,诊断为无症状感染者,转送至定点医院隔离治疗。'

        onMounted(() => {
          // 验证
          text = 'change'
          console.log(text)
        })

        return {
          text,
          text2
        }
      }
    })

    App.mount('#app')
  </script>
</body>

</html>

博文推荐

【笔记不易,如对您有帮助,请点赞,谢谢】