在Element plus的基础上封装一个超出时有tip,未超出时无tip的组件
EllipsisTooltip.vue
<template>
<el-tooltip ref="tooltipRef" :content="content" :disabled="!isOverflow">
<template #content>
<slot name="content"/>
</template>
<div ref="triggerDom" class="tooltip-trigger" :class="{'single-line': lines === 1}">
<!-- 默认使用content作为内容和tip,大多数场景下它们都是相同的 -->
<span><slot>{{ content }}</slot></span>
</div>
</el-tooltip>
</template>
<script setup lang="ts">
import {onMounted, onUnmounted, ref} from "vue";
const props = withDefaults(defineProps<{
content?: string,
lines?: number, // 超出行数
}>(), {lines: 1})
const triggerDom = ref<HTMLDivElement>()
const isOverflow = ref(false)
// 使用ResizeObserver API观察尺寸变化,相比使用鼠标事件或window.resize的方式性能更好
const observer = new ResizeObserver(() => {
if (triggerDom.value) {
// 创建一个选区,等同于鼠标在页面上选择文本
const range = document.createRange()
range.setStart(triggerDom.value, 0)
range.setEnd(triggerDom.value, triggerDom.value.childNodes.length)
// 获得选区的尺寸
const rect = range.getBoundingClientRect()
if (props.lines === 1) {
// 单行看宽度
isOverflow.value = rect.width > triggerDom.value.offsetWidth
} else {
// 多行看高度
isOverflow.value = rect.height > triggerDom.value.offsetHeight
}
}
})
onMounted(() => {
// 观测dom的尺寸变化
observer.observe(triggerDom.value)
})
onUnmounted(() => {
// 取消所有观测
observer.disconnect()
})
</script>
<style scoped>
/* 为了方便,这里使用了最新的CSS嵌套语法,兼容性很不好,不要在生产使用。你可以使用scss或者直接展开它们 */
.tooltip-trigger {
text-overflow: ellipsis;
overflow: hidden;
&.single-line {
white-space: nowrap;
}
&:not(.single-line) {
display: -webkit-box;
-webkit-line-clamp: v-bind(lines);
-webkit-box-orient: vertical;
}
}
</style>
使用示例
App.vue
<script setup lang="ts">
import EllipsisTooltip from "./components/EllipsisTooltip.vue";
</script>
<template>
<div style="border: 1px solid red;width: 80px">
<div style="width: 80px">
<!-- 未超出 -->
<EllipsisTooltip content="一段文本"/>
</div>
<div style="width: 80px;margin-top: 24px">
<!-- 单行超出 -->
<EllipsisTooltip content="一段挺长挺长的文本"/>
</div>
<div style="width: 80px;margin-top: 24px">
<!-- 多行超出 -->
<EllipsisTooltip :lines="2" content="一段挺长挺长的文本一段挺长挺长的文本"/>
</div>
<div style="width: 80px;margin-top: 24px">
<!-- tip富文本 -->
<EllipsisTooltip :lines="2" content="一段挺长挺长的文本一段挺长挺长的文本">
<template #content>
<div>甚至<b>可以</b><span style="color: red">带点花样</span></div>
</template>
</EllipsisTooltip>
</div>
<div style="width: 80px;margin-top: 24px">
<!-- content富文本 -->
<EllipsisTooltip :lines="2" content="使用插槽也不会有什么问题">
<span>这段<b style="color: red;margin: 8px">文文本本</b>将会超出两行的长度</span>
</EllipsisTooltip>
</div>
</div>
</template>
效果:
最后
- 使用ResizeObserver API观测尺寸变化,减少计算频率。
- 当组件外部容器为flex/grid等会影响内容尺寸的布局时,计算可能出错,因为flex默认会撑满容器,这时需要添加css样式消除影响。
兼容性还行:caniuse.com -webkit-line-clamp