单行文本展示(自动隐藏或缩小)

1,042 阅读2分钟

一、背景


❓ 在开发h5页面的时候,常常会需要展示各种文案,前端开发会根据UI出的设计图去实现。这里列出的是单行文本的展示,当文本的宽度超出了可视页面宽度的时候,会出现什么情况呢?

  • 一行只放一种类型的文本,文本会超出元素展示
  • 一行放了多种类型的文本,文本会覆盖其他元素,错乱展示

❎ 错误展示:

✅ 正确展示:

二、思考


除了上面列出的2种展示方式,还会有其他的情况么?

答案肯定是有的。能想到的还有很多,举例说明

  • 超出宽度换行展示,不滚动(没有最大高度)
  • 超出宽度换行展示,滚动(有最大高度)
  • 超出宽度换行展示,超过几行隐藏,并展示缩略符

上面的情况算是多行文本了,重点不在这儿,就没详细说明,不过设计成公共组件的时候还是需要把能想到的情况都需要考虑进去。

需要实现的2种展示方式是:

  • 单行文本超出宽度自动隐藏,并展示缩略符
  • 单行文本超出宽度自动缩小字体

自动缩小字体后,会导致在页面上看不清楚么?

会,所以设计的时候需要设置最小字体。但是设置最小字体后,还是会出现字体超出的情况,此时要考虑是隐藏还是换行展示。

三、实现


3.1、自动隐藏

单行文本超出宽度自动隐藏,并展示缩略符

第一种:-webkit-line-clamp会有兼容性的问题,需要考虑不同浏览器的情况。

display: -webkit-box;
-webkit-line-clamp: 1; // 单行文本
-webkit-box-orient: vertical;
overflow: hidden;

第二种:必须设置宽度,否则不生效

width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;

3.2、自动缩小

单行文本超出宽度自动缩小字体

本块的难点:就是如何计算出最合适的字体?

公式 =(可视宽度 - 内边距 * 2)/ 滚动宽度 * 基准字号

=(offsetWidth - padding * 2)/ scrollWidth * basicFontSize

四、封装组件


4.1、组件封装

<template>
  <!-- 单行文本超过固定宽度 缩小 -->
  <!-- 单行文本超过固定宽度 隐藏展示缩略点  :key="`text-${uniqueId}`" -->
  <div
    ref="text"
    :class="[hidden ? 'text-comp' : '']"
    >
    {{ isOk ? text : '' }}
    <p
      ref="computeText"
      :class="['compute-text']"
      :style="{
        fontSize: `${basicFontSize}px`,
        display: isOk ? 'none' : '',
      }"
      >
      <span>{{ text }}</span>
    </p>
  </div>
</template>

<script>
  export default {
    name: 'TextComp',
    props: {
      text: {
        type: [String, Number, Boolean],
        default: '',
      },
      // 直接隐藏,展示缩略符
      hidden: {
        type: Boolean,
        default: false,
      },
      // 最小字号
      minFontSize: {
        type: Number,
        default: 8,
      }
    },
    data() {
      return {
        isOk: this.hidden,
        paddingRight: 4, // 距离右边的边距
        basicFontSize: 16, // 基准文字大小
      }
    },
    watch: {
      text: {
        handler(value) {
          if (value && !this.hidden) {
            this.$nextTick(() => {
              // 不加nextTick拿不到div元素
              this.setElSize();
            })
          }
        },
        immediate: true,
        deep: true,
      }
    },
    methods: {
      setElSize () {
        const setNewElSize = (size) =>{
          const textElement = this.$refs.text;
          textElement.style.fontSize = `${size}px`;
          if (size === this.minFontSize) {
            textElement.style.whiteSpace = `nowrap`;
            textElement.style.overflow = `hidden`;
            textElement.style.textOverflow = `ellipsis`;
          }
          this.isOk = true;
        }

        const element = this.$refs.computeText;
        const actualWidth = element?.offsetWidth;
        const scrollWidth = element?.scrollWidth;

        if (scrollWidth > actualWidth) {
          const size = (actualWidth - this.paddingRight) / scrollWidth * this.basicFontSize;
          setNewElSize(size >= this.minFontSize ? size : this.minFontSize);
        } else {
          this.isOk = true;
        }
      }
    },
  }
</script>

<style scoped lang="less">
  .text-comp {
    width: 100%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .compute-text {
    width: 100%;
    overflow: scroll;
    visibility: hidden;
    white-space: nowrap;
  }
</style>