效果
vue2
<template>
<div class="cy-text" ref="cyText" :style="textStyle" @mouseenter="mouseenter" @mouseleave="mouseleave">{{value}}
</div>
</template>
<script>
import { getScrollContainer } from '@/utils/dom'
export default {
// 显示文字组件,可以设置最多显示几行,超过后会隐藏并且鼠标hover显示全部信息(需要给组件设置宽度)
name: 'CyText',
props: {
value: {
type: String,
default: ''
},
row: { //最多显示几行,超过后会...隐藏 为0时不隐藏
type: [Number, String],
default: 0
}
},
computed: {
cyText() {
return this.$refs.cyText
},
scrollWrap() {
return getScrollContainer(this.$refs.cyText, true)
}
},
data() {
return {
isShowHover: false,
textStyle: {},
div: null
}
},
watch: {
'row': function(val) {
this.init()
},
'value': function() {
this.isShowHover = false
this.textStyle = {
cursor: 'text'
}
this.$nextTick(() => {
this.getStyle(this.row - 0)
})
}
},
mounted() {
this.init()
},
beforeDestroy() {
},
methods: {
init() {
this.div = document.querySelector('.cy-hover')
if (!this.div && this.row - 0) {
this.div = document.createElement('div')
this.div.className = 'cy-hover'
this.div.style.top = 0
this.div.style.left = 0
document.body.append(this.div)
}
if (this.div && this.row - 0) {
this.getStyle(this.row - 0)
}
},
getStyle(val) {
let lineHeight = getComputedStyle(this.cyText).lineHeight.replace('px', '') - 0 || 20
let height = getComputedStyle(this.cyText).height.replace('px', '') - 0
if (height > lineHeight * val) {
this.isShowHover = true
this.textStyle = {
height: `${lineHeight * val}px`,
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
webkitLineClamp: val,
webkitBoxOrient: 'vertical',
cursor: 'pointer'
}
} else {
this.isShowHover = false
this.textStyle = {
cursor: 'text'
}
}
},
mouseenter(e) {
if (!this.isShowHover) {
return
}
this.div.innerHTML = this.value
let box = e.target.getBoundingClientRect()
let divRect = this.div.getBoundingClientRect()
let windowHeight = innerHeight || document.documentElement.clientHeight
let top = ''
let left = ''
left = box.left + (box.width - divRect.width) / 2 + 'px'
if(box.bottom + divRect.height - windowHeight > 10) {
top = box.top - divRect.height + 'px'
} else{
top = box.top + box.height + 'px'
}
this.div.style.top = top
this.div.style.left = left
this.div.classList.add('active')
this.scrollWrap.addEventListener('scroll', this.scrollFun)
},
mouseleave(e) {
if (!this.isShowHover) {
return
}
if (e.relatedTarget !== this.div) {
this.scrollFun()
}
this.div.onmouseleave = () => {
this.scrollFun()
}
},
// 最近的dom滚动时,关闭弹窗
scrollFun() {
this.div.style.top = 0
this.div.style.left = 0
this.div.classList.remove('active')
this.scrollWrap.removeEventListener('scroll', this.scrollFun)
}
}
}
</script>
<style scoped lang="scss">
.cy-text {
position: relative;
}
</style>
<style lang="scss">
.cy-hover {
position: absolute;
background-color: #303133;
color: #fff;
font-size: 14px;
line-height: 1.4;
padding: 6px 10px;
border-radius: 5px;
border: 1px solid #eee;
max-width: 400px;
transition: opacity 0.3s linear;
z-index: -1;
opacity: 0;
&.active {
z-index: 1999;
opacity: 1;
}
}
</style>
vue3
<template>
<div class="cy-text" ref="cyTextRef" :style="textStyle" @mouseenter="mouseenter" @mouseleave="mouseleave">{{props.tipText}}
</div>
</template>
<script setup lang="ts" name="VueText">
import { ref, nextTick, onMounted ,watch} from 'vue'
import { getScrollContainer } from './dom'
const props = defineProps({
tipText:{
type:String,
default:''
},
row: { //最多显示几行,超过后会...隐藏 为0时不隐藏
type: [Number, String],
default: 2
}
})
const cyTextRef = ref<Nullable<HTMLElement | any>>(null)
var isShowHover:Boolean = false
const textStyle:any = ref({})
var div:any = null
watch(() => props.row,(val) => {
init()
})
watch(() => props.tipText,(val) => {
isShowHover = false
textStyle.value = {
cursor: 'text'
}
nextTick(() => {
getStyle(props.row - 0)
})
})
onMounted(()=>{
init()
})
function init() {
div = document.querySelector('.cy-hover')
if (!div && props.row - 0) {
div = document.createElement('div')
div.className = 'cy-hover'
div.style.top = 0
div.style.left = 0
document.body.append(div)
}
if (div && props.row - 0) {
getStyle(props.row - 0)
}
}
function getStyle(val) {
let lineHeight = getComputedStyle(cyTextRef.value).lineHeight.replace('px', '') - 0 || 20
let height = getComputedStyle(cyTextRef.value).height.replace('px', '') - 0
if (height > lineHeight * val) {
isShowHover = true
textStyle.value = {
height: `${lineHeight * val}px`,
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
webkitLineClamp: val,
webkitBoxOrient: 'vertical',
cursor: 'pointer'
}
} else {
isShowHover = false
textStyle.value = {
cursor: 'text'
}
}
}
function mouseenter(e) {
if (!isShowHover) {
return
}
div.innerHTML = props.tipText
let box = e.target.getBoundingClientRect()
let divRect = div.getBoundingClientRect()
let windowHeight = innerHeight || document.documentElement.clientHeight
let top = ''
let left = ''
left = box.left + (box.width - divRect.width) / 2 + 'px'
if(box.bottom + divRect.height - windowHeight > 10) {
top = box.top - divRect.height + 'px'
} else{
top = box.top + box.height + 'px'
}
div.style.top = top
div.style.left = left
div.classList.add('active')
getScrollContainer(cyTextRef.value, true).addEventListener('scroll', scrollFun)
}
function mouseleave(e) {
if (!isShowHover) {
return
}
if (e.relatedTarget !== div) {
scrollFun()
}
div.onmouseleave = () => {
scrollFun()
}
}
// 最近的dom滚动时,关闭弹窗
function scrollFun() {
div.style.top = 0
div.style.left = 0
div.classList.remove('active')
getScrollContainer(cyTextRef.value, true).removeEventListener('scroll', scrollFun)
}
</script>
<style scoped lang="scss">
.cy-text {
position: relative;
}
</style>
<style lang="scss">
.cy-hover {
position: absolute;
background-color: #303133;
color: #fff;
font-size: 14px;
line-height: 1.4;
padding: 6px 10px;
border-radius: 5px;
border: 1px solid #eee;
max-width: 400px;
transition: opacity 0.3s linear;
z-index: -1;
opacity: 0;
&.active {
z-index: 1999;
opacity: 1;
}
}
</style>
dom.js
// 获取滚动的dom
export function getScrollContainer(el, vertical) {
let parent = el;
while (parent) {
if ([window, document, document.documentElement].includes(parent)) {
return window;
}
if (isScroll(parent, vertical)) {
return parent;
}
parent = parent.parentNode;
}
return parent;
}
export function isScroll(el, vertical) {
const determinedDirection = vertical !== null || vertical !== undefined;
const overflow = determinedDirection ?
vertical ?
getComputedStyle(el)['overflow-y'] :
getComputedStyle(el)['overflow-x'] :
getComputedStyle(el)['overflow'];
return overflow.match(/(scroll|auto)/);
}