组件化-文本溢出自动处理省略号
背景
像很多做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>
实现的效果如下展示
总结
本组件解决方案通过封装复杂的文本溢出处理逻辑,让使用者来说只需要引用组件,限制其宽度,至于是否会出现溢出就无需额外考虑。