VUE动态tooltip指令
当元素被缩略显示时,鼠标悬浮才显示tip
支持单行和多行缩略,支持设置方向,超出屏幕自动偏移
import Vue from 'vue'
Vue.directive('title', {
bind(el, binding) {
let showTimeout = null
if (!binding.value) {
console.warn('请设置要显示的tip')
}
// 创建tooltip元素并设置样式
const tooltip = document.createElement('div')
tooltip.className = 'v-title'
tooltip.style.position = 'absolute'
tooltip.style.opacity = '0' // 设为0开始时不可见
tooltip.style.transition = 'opacity 0.3s ease-in-out' // 添加过渡效果
tooltip.style.backgroundColor = '#3b3744e6'
tooltip.style.color = 'white'
tooltip.style.padding = '4px 8px'
tooltip.style.borderRadius = '4px'
tooltip.style.whiteSpace = 'nowrap'
tooltip.style.fontSize = '14px'
tooltip.style.zIndex = '1000' // 确保tooltip在最上层
tooltip.innerText = binding.value?.text || binding.value // 使用传入的title文本
const directions = ['top', 'bottom', 'left', 'right'] // 支持的方向
const defaultDirection = 'top' // 默认方向
// 设置的方向
const direction = directions.includes(binding.value?.direction) ? binding.value.direction : defaultDirection
// 将tooltip作为自定义属性存储在元素上
el.__vue_tooltip__ = tooltip
el.__vue_showTimeout__ = showTimeout
document.body.appendChild(tooltip)
// 定义一个方法,检查是否需要显示tooltip
const shouldShowTooltip = () => {
// 创建一个临时span来计算完整内容的宽度
// const range = document.createRange()
// range.selectNodeContents(el)
// const contentWidth = range.getBoundingClientRect().width
// range.detach()
// // 获取元素的computed style
// const computedStyle = window.getComputedStyle(el)
// // 从computed style中提取padding值
// const paddingLeft = parseInt(computedStyle.paddingLeft, 10)
// const paddingRight = parseInt(computedStyle.paddingRight, 10)
// // 计算元素的内容区域宽度,减去padding
// const contentAreaWidth = el.offsetWidth - paddingLeft - paddingRight
// // 比较内容宽度和元素的可视宽度
// return contentWidth > contentAreaWidth
const overflow = el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;
// const overflow = el.scrollWidth > el.clientWidth
return overflow
}
// 鼠标进入时可能显示tooltip
el.addEventListener('mouseenter', function () {
// debugger
if (!shouldShowTooltip()) {
// 如果不需要显示tooltip,什么也不做
return
}
// 取消之前的延时(如果有)
clearTimeout(el.__vue_showTimeout__)
// 延迟显示tooltip
el.__vue_showTimeout__ = setTimeout(() => {
const elRect = el.getBoundingClientRect()
const tooltipRect = tooltip.getBoundingClientRect()
let leftPos, topPos
const margin = 10 // Tooltip与目标元素的间距
switch (direction) {
case 'top':
leftPos = elRect.left + elRect.width / 2 - tooltipRect.width / 2
topPos = window.scrollY + elRect.top - tooltipRect.height - margin
break
case 'bottom':
leftPos = elRect.left + elRect.width / 2 - tooltipRect.width / 2
topPos = window.scrollY + elRect.bottom + margin
break
case 'left':
leftPos = elRect.left - tooltipRect.width - margin
topPos = window.scrollY + elRect.top + elRect.height / 2 - tooltipRect.height / 2
break
case 'right':
leftPos = elRect.right + margin
topPos = window.scrollY + elRect.top + elRect.height / 2 - tooltipRect.height / 2
break
}
// 确保tooltip在屏幕范围内
const minLeft = 10
const maxRight = window.innerWidth - tooltipRect.width - 10
if (leftPos < minLeft) {
// 如果tooltip超出屏幕左侧
leftPos = minLeft
} else if (leftPos > maxRight) {
// 如果tooltip超出屏幕右侧
leftPos = maxRight
}
tooltip.style.left = `${leftPos}px`
tooltip.style.top = `${topPos}px`
tooltip.style.opacity = '1'
}, 500) // 延迟显示tooltip,例如500毫秒延迟
})
// 鼠标离开时隐藏tooltip
el.addEventListener('mouseleave', function () {
clearTimeout(el.__vue_showTimeout__)
tooltip.style.opacity = '0'
})
},
update(el, binding) {
// 更新tooltip的文本
const tooltip = el.__vue_tooltip__
if (tooltip) {
tooltip.innerText = binding.value
}
},
unbind(el) {
// 清除
const tooltip = el.__vue_tooltip__
const showTimeout = el.__vue_showTimeout__
if (tooltip) {
document.body.removeChild(tooltip)
delete el.__vue_tooltip__
}
if (showTimeout) {
clearTimeout(showTimeout)
delete el.__vue_showTimeout__
}
},
})