This commit is contained in:
oneao committed 2025-09-04 21:44:55 +08:00
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);
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ function setLight() {
白色主题
</AButton>
</div>
<APopup content="2222222222222222222222" trigger="click">
<APopup content="123456" trigger="hover">
<AButton>测试</AButton>
</APopup>
</div>