u
This commit is contained in:
1 parent
1a33497c5d
commit
48dac36685
3 files changed
+78
-58
No files matched your search
@@ -1,3 +1,4 @@
|
||||
// Tooltip.tsx
|
||||
import type { PropType } from 'vue'
|
||||
import { cloneVNode, defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
@@ -21,27 +22,46 @@ export default defineComponent({
|
||||
|
||||
const createTooltip = () => {
|
||||
const el = document.createElement('div')
|
||||
el.className = `tooltip tooltip-${props.placement}` // 初始隐藏,不加 visible
|
||||
el.innerHTML = `${props.content}<div class="tooltip-arrow"></div>`
|
||||
el.className = `tooltip tooltip-${props.placement}`
|
||||
|
||||
const contentEl = document.createElement('div')
|
||||
contentEl.className = 'tooltip-content'
|
||||
contentEl.textContent = props.content
|
||||
|
||||
const arrowEl = document.createElement('div')
|
||||
arrowEl.className = 'tooltip-arrow'
|
||||
|
||||
el.appendChild(contentEl)
|
||||
el.appendChild(arrowEl)
|
||||
|
||||
// 初始状态
|
||||
el.style.position = 'absolute'
|
||||
el.style.top = '0'
|
||||
el.style.left = '0'
|
||||
el.style.opacity = '0'
|
||||
el.style.visibility = 'hidden'
|
||||
el.style.pointerEvents = 'none'
|
||||
|
||||
document.body.appendChild(el)
|
||||
tooltipEl.value = el
|
||||
}
|
||||
|
||||
const getTargetDom = (): HTMLElement | null => {
|
||||
if (!targetEl.value)
|
||||
return null
|
||||
let dom = targetEl.value
|
||||
if ('$el' in dom)
|
||||
dom = (dom as any).$el
|
||||
return dom instanceof HTMLElement ? dom : null
|
||||
}
|
||||
|
||||
const updatePosition = () => {
|
||||
const target = targetEl.value
|
||||
const tooltip = tooltipEl.value
|
||||
if (!target || !tooltip)
|
||||
const target = getTargetDom()
|
||||
if (!tooltip || !target)
|
||||
return
|
||||
|
||||
let dom: HTMLElement | null = null
|
||||
if (target instanceof HTMLElement)
|
||||
dom = target
|
||||
else if ((target as any)?.$el instanceof HTMLElement)
|
||||
dom = (target as any).$el
|
||||
if (!dom)
|
||||
return
|
||||
|
||||
const rect = dom.getBoundingClientRect()
|
||||
const rect = target.getBoundingClientRect()
|
||||
const tooltipRect = tooltip.getBoundingClientRect()
|
||||
const scrollX = window.scrollX || window.pageXOffset
|
||||
const scrollY = window.scrollY || window.pageYOffset
|
||||
@@ -75,53 +95,51 @@ export default defineComponent({
|
||||
|
||||
const show = () => {
|
||||
const tooltip = tooltipEl.value
|
||||
if (!tooltip || !targetEl.value)
|
||||
if (!tooltip || !getTargetDom())
|
||||
return
|
||||
|
||||
visible.value = true
|
||||
tooltip.style.visibility = 'visible'
|
||||
tooltip.style.pointerEvents = 'auto'
|
||||
|
||||
nextTick(() => {
|
||||
updatePosition()
|
||||
tooltip.classList.add('visible') // CSS 控制动画
|
||||
tooltip.style.opacity = '1'
|
||||
tooltip.style.transform = 'scale(1)'
|
||||
})
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const hide = () => {
|
||||
const tooltip = tooltipEl.value
|
||||
if (!tooltip)
|
||||
return
|
||||
|
||||
tooltip.style.opacity = '0'
|
||||
tooltip.style.transform = 'scale(0.95)'
|
||||
tooltip.style.pointerEvents = 'none'
|
||||
tooltip.style.visibility = 'hidden'
|
||||
visible.value = false
|
||||
tooltip.classList.remove('visible')
|
||||
}
|
||||
|
||||
const toggle = () => {
|
||||
visible.value ? hide() : show()
|
||||
}
|
||||
const toggle = () => (visible.value ? hide() : show())
|
||||
|
||||
onMounted(() => {
|
||||
createTooltip()
|
||||
window.addEventListener('scroll', () => {
|
||||
if (visible.value)
|
||||
updatePosition()
|
||||
})
|
||||
window.addEventListener('resize', () => {
|
||||
if (visible.value)
|
||||
updatePosition()
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', () => {
|
||||
const onUpdate = () => {
|
||||
if (visible.value)
|
||||
updatePosition()
|
||||
})
|
||||
window.removeEventListener('resize', () => {
|
||||
if (visible.value)
|
||||
updatePosition()
|
||||
})
|
||||
if (tooltipEl.value && tooltipEl.value.parentNode) {
|
||||
tooltipEl.value.parentNode.removeChild(tooltipEl.value)
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', onUpdate)
|
||||
window.addEventListener('resize', onUpdate)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('scroll', onUpdate)
|
||||
window.removeEventListener('resize', onUpdate)
|
||||
if (tooltipEl.value && tooltipEl.value.parentNode) {
|
||||
tooltipEl.value.parentNode.removeChild(tooltipEl.value)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@use '../_styles/_variables.scss' as *;
|
||||
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
padding: 6px 10px;
|
||||
background: #fff;
|
||||
@@ -9,13 +8,16 @@
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
|
||||
/* ---------------- 动画 ---------------- */
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
transform 0.2s ease;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
visibility: hidden;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
@@ -27,30 +29,30 @@
|
||||
height: 8px;
|
||||
background: #fff;
|
||||
transform: rotate(45deg);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
&.tooltip-top .tooltip-arrow,
|
||||
&.tooltip-bottom .tooltip-arrow {
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.tooltip-left .tooltip-arrow,
|
||||
&.tooltip-right .tooltip-arrow {
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.tooltip-top .tooltip-arrow {
|
||||
bottom: -4px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.tooltip-bottom .tooltip-arrow {
|
||||
top: -4px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.tooltip-left .tooltip-arrow {
|
||||
right: -4px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(45deg);
|
||||
}
|
||||
|
||||
&.tooltip-right .tooltip-arrow {
|
||||
left: -4px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%) rotate(45deg);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user