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 type { PropType } from 'vue'
|
||||||
import { cloneVNode, defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { cloneVNode, defineComponent, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
@@ -21,27 +22,46 @@ export default defineComponent({
|
|||||||
|
|
||||||
const createTooltip = () => {
|
const createTooltip = () => {
|
||||||
const el = document.createElement('div')
|
const el = document.createElement('div')
|
||||||
el.className = `tooltip tooltip-${props.placement}` // 初始隐藏,不加 visible
|
el.className = `tooltip tooltip-${props.placement}`
|
||||||
el.innerHTML = `${props.content}<div class="tooltip-arrow"></div>`
|
|
||||||
|
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)
|
document.body.appendChild(el)
|
||||||
tooltipEl.value = 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 updatePosition = () => {
|
||||||
const target = targetEl.value
|
|
||||||
const tooltip = tooltipEl.value
|
const tooltip = tooltipEl.value
|
||||||
if (!target || !tooltip)
|
const target = getTargetDom()
|
||||||
|
if (!tooltip || !target)
|
||||||
return
|
return
|
||||||
|
|
||||||
let dom: HTMLElement | null = null
|
const rect = target.getBoundingClientRect()
|
||||||
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 tooltipRect = tooltip.getBoundingClientRect()
|
const tooltipRect = tooltip.getBoundingClientRect()
|
||||||
const scrollX = window.scrollX || window.pageXOffset
|
const scrollX = window.scrollX || window.pageXOffset
|
||||||
const scrollY = window.scrollY || window.pageYOffset
|
const scrollY = window.scrollY || window.pageYOffset
|
||||||
@@ -75,53 +95,51 @@ export default defineComponent({
|
|||||||
|
|
||||||
const show = () => {
|
const show = () => {
|
||||||
const tooltip = tooltipEl.value
|
const tooltip = tooltipEl.value
|
||||||
if (!tooltip || !targetEl.value)
|
if (!tooltip || !getTargetDom())
|
||||||
return
|
return
|
||||||
|
|
||||||
visible.value = true
|
tooltip.style.visibility = 'visible'
|
||||||
|
tooltip.style.pointerEvents = 'auto'
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
updatePosition()
|
updatePosition()
|
||||||
tooltip.classList.add('visible') // CSS 控制动画
|
tooltip.style.opacity = '1'
|
||||||
|
tooltip.style.transform = 'scale(1)'
|
||||||
})
|
})
|
||||||
|
visible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const hide = () => {
|
const hide = () => {
|
||||||
const tooltip = tooltipEl.value
|
const tooltip = tooltipEl.value
|
||||||
if (!tooltip)
|
if (!tooltip)
|
||||||
return
|
return
|
||||||
|
tooltip.style.opacity = '0'
|
||||||
|
tooltip.style.transform = 'scale(0.95)'
|
||||||
|
tooltip.style.pointerEvents = 'none'
|
||||||
|
tooltip.style.visibility = 'hidden'
|
||||||
visible.value = false
|
visible.value = false
|
||||||
tooltip.classList.remove('visible')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggle = () => {
|
const toggle = () => (visible.value ? hide() : show())
|
||||||
visible.value ? hide() : show()
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
createTooltip()
|
createTooltip()
|
||||||
window.addEventListener('scroll', () => {
|
|
||||||
if (visible.value)
|
|
||||||
updatePosition()
|
|
||||||
})
|
|
||||||
window.addEventListener('resize', () => {
|
|
||||||
if (visible.value)
|
|
||||||
updatePosition()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
const onUpdate = () => {
|
||||||
window.removeEventListener('scroll', () => {
|
|
||||||
if (visible.value)
|
if (visible.value)
|
||||||
updatePosition()
|
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 () => {
|
return () => {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
@use '../_styles/_variables.scss' as *;
|
@use '../_styles/_variables.scss' as *;
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: absolute;
|
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -9,13 +8,16 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
white-space: nowrap;
|
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);
|
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 {
|
&.visible {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
@@ -27,30 +29,30 @@
|
|||||||
height: 8px;
|
height: 8px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
transform: rotate(45deg);
|
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 {
|
&.tooltip-top .tooltip-arrow {
|
||||||
bottom: -4px;
|
bottom: -4px;
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) rotate(45deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tooltip-bottom .tooltip-arrow {
|
&.tooltip-bottom .tooltip-arrow {
|
||||||
top: -4px;
|
top: -4px;
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) rotate(45deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tooltip-left .tooltip-arrow {
|
&.tooltip-left .tooltip-arrow {
|
||||||
right: -4px;
|
right: -4px;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%) rotate(45deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tooltip-right .tooltip-arrow {
|
&.tooltip-right .tooltip-arrow {
|
||||||
left: -4px;
|
left: -4px;
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%) rotate(45deg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ function setLight() {
|
|||||||
白色主题
|
白色主题
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
<APopup content="2222222222222222222222" trigger="click">
|
<APopup content="123456" trigger="hover">
|
||||||
<AButton>测试</AButton>
|
<AButton>测试</AButton>
|
||||||
</APopup>
|
</APopup>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in new issue
Block a user