组件化-文本溢出自动处理省略号

305 阅读3分钟

组件化-文本溢出自动处理省略号

背景

像很多做B端,C端,G端的产品,在过多文字的展示上,一般的处理都是出现省略号,然后在用户鼠标悬浮时候,出现提示框。

像这种情况我们就很容易写出这样的css样式:

单行文本溢出情况:

.ellipsis-single-line {
  width: 200px;
  white-space: nowrap;        /* 禁止换行 */
  overflow: hidden;           /* 隐藏超出的文本 */
  text-overflow: ellipsis;    /* 超出的文本显示省略号 */
}
<div class="ellipsis-single-line">
   这是一段很长的文本,文本会被限制在三行显示,超出的部分将以省略号的形式显示。
   这里是第二行的内容,继续显示下去。即使文字非常长,超过三行,超出的部分也不会显示出来。
   这里是第三行。再多的内容也会被截断。
</div>

多行文本溢出情况:(只能基于webkit内核的浏览器)

.ellipsis-multi-line {
  width: 300px;
  display: -webkit-box;             /* 使用弹性盒子模型 */
  -webkit-box-orient: vertical;     /* 设置盒子方向为纵向 */
  -webkit-line-clamp: 3;            /* 限制显示的行数(这里是3行) */
  overflow: hidden;                 /* 隐藏多出的文本 */
  text-overflow: ellipsis;          /* 省略号 */
}

像不是webkit内核的浏览器上,多行的处理一般都是通过js代码实现,通过判断元素的滚动距离是否大于元素的原始大小

function truncateText(element) {
  if (element.scrollHeight > element.clientHeight) {
    element.style.textOverflow = 'ellipsis';
  }
}
​
const divElement = document.querySelector('.ellipsis-multi-line');
truncateText(divElement);

总结:这时候我们就能发现,在这样的场景下,针对单行还是多行上,我们都要写不同的代码,甚至是需要处理兼容性的问题,导致需要写的东西越来越多。那么有没一种可能,声明一个组件,这一系列的问题都交给组件内部处理的。

需求定义

我们现在希望有一个组件能接受content内容,无论是在单行还是多行下都能处理,而且不受浏览器内核影响,在过多文本出现时候,鼠标悬浮可出现提示框。

针对需求,我们可以总结下需要处理的内容如下:

1、因要不受浏览器内核影响,所以要使用js处理其中的逻辑

2、需要有个参数判断是要处理单行还是多行的溢出

3、需要有个参数判断在溢出情况下,是否要出现提示框,本项目使用ant-design-vue中的tooltip组件来显示提示框

相关设计

<template>
    <render />
</template><script lang="jsx" setup>
import { ref, useSlots, nextTick } from 'vue'const props = defineProps({
    // 溢出后是否展示tooltip
    showTooltip: {
        type: Boolean,
        default: true
    },
    // 最大显示行数
    multiLine: {
        type: [Number, String],
        default: 1
    }
})
​
const useCalcEllipsis = (props, wrapRef, contentRef) => {
    const single = props.multiLine === 1 // 是否单行溢出的处理
    const overflow = ref(false) // 判断是否溢出
    
    const calcContent = async () => {
        // 等待页面加载完毕
        await nextTick()
        if(single) {
            overflow.value = wrapRef.value.getBoundingClientRect().width < contentRef.value.getBoundingClientRect().width
        } else {
            overflow.value = contentRef.value.scrollHeight > contentRef.value.clientHeight 
        }
    }
    
    return {
        calcContent,
        overflow
    }
}
​
const wrapRef = ref(null)
const contentRef = ref(null)
const { overflow, calcContent, single }  = useCalcEllipsis(props, wrapRef, contentRef)
​
const useTooltip = (overflow) => {
    const openTooltip = ref(false)
    
    const changeToolTip = visiabled => {
        if(props.showTooltip && overflow.value) {
            openTooltip.value = visiabled
        } else {
            openTooltip.value = false
        }
    }
​
    return {
        openTooltip,
        changeToolTip
    }
}
const { openTooltip, changeToolTip } = useTooltip(overflow)
​
const slots = useSlots()
const render = () => {
    const { showTooltip, multiLine } = props
    const single = multiLine == 1
    const content = slots.default()
    showTooltip && calcContent()
​
    const cContent = (
        <p
            ref={el => single ? (wrapRef.value = el) : (contentRef.value = el) }
            class={single ? 'ellipsis-single' : 'ellipsis-mul'}
        >
            {
                single
                    ? <span ref={el => (contentRef.value = el)}>
                        {content}
                      </span>
                    : content
            }
        </p>
    )
​
    return (
        <a-tooltip open={openTooltip.value} onOpenChange={changeToolTip}>
            {{ title: () => content, default: () => cContent }}
        </a-tooltip>
    )
}
</script><style>
.ellipsis-mul {
    display: -webkit-box;          /* 使用弹性盒子模型 */
    -webkit-box-orient: vertical;  /* 设置盒子方向为纵向 */
    -webkit-line-clamp: v-bind(props.multiLine);         
    overflow: hidden;              /* 隐藏多出的文本 */
    text-overflow: ellipsis;       /* 省略号 */
}
    
.ellipsis-single {
    white-space: nowrap;        /* 禁止换行 */
    overflow: hidden;           /* 隐藏超出的文本 */
    text-overflow: ellipsis;    /* 超出的文本显示省略号 */
}
</style>

实现的效果如下展示

image.png

总结

github示例

本组件解决方案通过封装复杂的文本溢出处理逻辑,让使用者来说只需要引用组件,限制其宽度,至于是否会出现溢出就无需额外考虑。