需求背景
需求是实现类似el-table-column的show-tooltip-overflow的功能,文本在内容超出后需要能够hover展示全部
组件设计
组件的基本的dom结构是,一个容器wrap和一个文本text
<div ref="wrapDom" class="show-tooltip-overflow-index">
<div ref="textDom"class="no-overflow-text" :class="lineClamp == 1 ? 'single' : 'multi'" v-if="!isOverflow">{{ text }}</div>
<a-tooltip v-else>
<template #title>{{ text }}</template>
<span :class="lineClamp == 1 ? 'single-overflow-text' : 'multi-overflow-text'">{{ text }} </span>
</a-tooltip>
</div>
组件需要能够实现单行和多行两种类型的文字,因此组件需要传入lineClamp属性,用来设置几行溢出显示。
- 单行实现,将wrap和text的宽度进行比对, 如果text的宽度的大于wrap的宽度,则说明文字宽度大于容器宽度。
isOverflow.value = textDom.value?.scrollWidth > wrapDom.value?.clientWidth
- 多行实现,通过js代码去计算行数,判断行数是否大于lineClamp
const countLines = (target) => {
var style = window.getComputedStyle(target, null)
var height = parseInt(style.getPropertyValue("height"))
var font_size = parseInt(style.getPropertyValue("font-size"))
var line_height = parseInt(style.getPropertyValue("line-height"))
var box_sizing = style.getPropertyValue("box-sizing")
if (isNaN(line_height)) line_height = font_size * 1.2
if (box_sizing == "border-box") {
var padding_top = parseInt(style.getPropertyValue("padding-top"))
var padding_bottom = parseInt(style.getPropertyValue("padding-bottom"))
var border_top = parseInt(style.getPropertyValue("border-top-width"))
var border_bottom = parseInt(style.getPropertyValue("border-bottom-width"))
height = height - padding_top - padding_bottom - border_top - border_bottom
}
var lines = height / line_height
return lines
}
完整代码
<template>
<div ref="wrapDom" class="show-tooltip-overflow-index">
<div ref="textDom" class="no-overflow-text" :class="lineClamp == 1 ? 'single' : 'multi'" v-if="!isOverflow">{{ text
}}</div>
<a-tooltip v-else>
<template #title>{{ text }}</template>
<span :class="lineClamp == 1 ? 'single-overflow-text' : 'multi-overflow-text'">{{ text }}</span>
</a-tooltip>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const props = defineProps({
text: {
type: String,
required: true
},
lineClamp: {
type: Number,
default: 1
}
})
const textDom = ref();
const wrapDom = ref();
const isOverflow = ref(false);
onMounted(() => {
if (props.lineClamp == 1) {
isOverflow.value = textDom.value?.scrollWidth > wrapDom.value?.clientWidth;
} else {
isOverflow.value = countLines(textDom.value) > props.lineClamp;
}
})
const countLines = (target) => {
var style = window.getComputedStyle(target, null)
var height = parseInt(style.getPropertyValue("height"))
var font_size = parseInt(style.getPropertyValue("font-size"))
var line_height = parseInt(style.getPropertyValue("line-height"))
var box_sizing = style.getPropertyValue("box-sizing")
if (isNaN(line_height)) line_height = font_size * 1.2
if (box_sizing == "border-box") {
var padding_top = parseInt(style.getPropertyValue("padding-top"))
var padding_bottom = parseInt(style.getPropertyValue("padding-bottom"))
var border_top = parseInt(style.getPropertyValue("border-top-width"))
var border_bottom = parseInt(style.getPropertyValue("border-bottom-width"))
height = height - padding_top - padding_bottom - border_top - border_bottom
}
var lines = height / line_height
return lines
}
</script>
<style lang="less" scoped>
.show-tooltip-overflow-index {
.no-overflow-text.single {
white-space: nowrap;
}
.multi-overflow-text {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: v-bind('lineClamp');
overflow: hidden;
}
.single-overflow-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
width: 100%;
}
}
</style>