|
@@ -2,6 +2,8 @@
|
|
|
|
|
|
|
|
let tooltipEl = null
|
|
let tooltipEl = null
|
|
|
let currentTarget = null
|
|
let currentTarget = null
|
|
|
|
|
+let observer = null
|
|
|
|
|
+
|
|
|
let isInitialized = false
|
|
let isInitialized = false
|
|
|
|
|
|
|
|
let moveHandler = null
|
|
let moveHandler = null
|
|
@@ -27,6 +29,7 @@ export function useMouseTooltip() {
|
|
|
if (tooltipEl) return
|
|
if (tooltipEl) return
|
|
|
|
|
|
|
|
tooltipEl = document.createElement('div')
|
|
tooltipEl = document.createElement('div')
|
|
|
|
|
+
|
|
|
tooltipEl.className = 'cursor-pointer-tooltip'
|
|
tooltipEl.className = 'cursor-pointer-tooltip'
|
|
|
|
|
|
|
|
tooltipEl.style.cssText = `
|
|
tooltipEl.style.cssText = `
|
|
@@ -110,8 +113,44 @@ export function useMouseTooltip() {
|
|
|
tooltipEl.style.top = `${y}px`
|
|
tooltipEl.style.top = `${y}px`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ function disconnectObserver() {
|
|
|
|
|
+ if (observer) {
|
|
|
|
|
+ observer.disconnect()
|
|
|
|
|
+ observer = null
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function watchTooltip(target) {
|
|
|
|
|
+ disconnectObserver()
|
|
|
|
|
+
|
|
|
|
|
+ observer = new MutationObserver(() => {
|
|
|
|
|
+ if (
|
|
|
|
|
+ !tooltipEl ||
|
|
|
|
|
+ currentTarget !== target
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const text =
|
|
|
|
|
+ target.dataset.tooltip || ''
|
|
|
|
|
+
|
|
|
|
|
+ tooltipEl.textContent = text
|
|
|
|
|
+
|
|
|
|
|
+ updatePosition(
|
|
|
|
|
+ lastMouseX,
|
|
|
|
|
+ lastMouseY
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ observer.observe(target, {
|
|
|
|
|
+ attributes: true,
|
|
|
|
|
+ attributeFilter: ['data-tooltip']
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
function showTooltip(e, target) {
|
|
function showTooltip(e, target) {
|
|
|
- const text = target.dataset.tooltip
|
|
|
|
|
|
|
+ const text =
|
|
|
|
|
+ target.dataset.tooltip || ''
|
|
|
|
|
|
|
|
if (!text) return
|
|
if (!text) return
|
|
|
|
|
|
|
@@ -129,11 +168,16 @@ export function useMouseTooltip() {
|
|
|
|
|
|
|
|
currentTarget = target
|
|
currentTarget = target
|
|
|
|
|
|
|
|
|
|
+ lastMouseX = e.clientX
|
|
|
|
|
+ lastMouseY = e.clientY
|
|
|
|
|
+
|
|
|
tooltipEl.textContent = text
|
|
tooltipEl.textContent = text
|
|
|
|
|
|
|
|
|
|
+ watchTooltip(target)
|
|
|
|
|
+
|
|
|
updatePosition(
|
|
updatePosition(
|
|
|
- e.clientX,
|
|
|
|
|
- e.clientY
|
|
|
|
|
|
|
+ lastMouseX,
|
|
|
|
|
+ lastMouseY
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
tooltipEl.style.opacity = '0'
|
|
tooltipEl.style.opacity = '0'
|
|
@@ -141,13 +185,17 @@ export function useMouseTooltip() {
|
|
|
tooltipEl.style.transform = 'scale(.85)'
|
|
tooltipEl.style.transform = 'scale(.85)'
|
|
|
|
|
|
|
|
showTimer = setTimeout(() => {
|
|
showTimer = setTimeout(() => {
|
|
|
- if (currentTarget !== target) return
|
|
|
|
|
|
|
+ if (currentTarget !== target) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- tooltipEl.style.visibility = 'visible'
|
|
|
|
|
|
|
+ tooltipEl.style.visibility =
|
|
|
|
|
+ 'visible'
|
|
|
|
|
|
|
|
requestAnimationFrame(() => {
|
|
requestAnimationFrame(() => {
|
|
|
tooltipEl.style.opacity = '1'
|
|
tooltipEl.style.opacity = '1'
|
|
|
- tooltipEl.style.transform = 'scale(1)'
|
|
|
|
|
|
|
+ tooltipEl.style.transform =
|
|
|
|
|
+ 'scale(1)'
|
|
|
})
|
|
})
|
|
|
}, SHOW_DELAY)
|
|
}, SHOW_DELAY)
|
|
|
}
|
|
}
|
|
@@ -155,6 +203,8 @@ export function useMouseTooltip() {
|
|
|
function hideTooltip() {
|
|
function hideTooltip() {
|
|
|
if (!tooltipEl) return
|
|
if (!tooltipEl) return
|
|
|
|
|
|
|
|
|
|
+ disconnectObserver()
|
|
|
|
|
+
|
|
|
if (showTimer) {
|
|
if (showTimer) {
|
|
|
clearTimeout(showTimer)
|
|
clearTimeout(showTimer)
|
|
|
showTimer = null
|
|
showTimer = null
|
|
@@ -166,12 +216,19 @@ export function useMouseTooltip() {
|
|
|
|
|
|
|
|
hideTimer = setTimeout(() => {
|
|
hideTimer = setTimeout(() => {
|
|
|
tooltipEl.style.opacity = '0'
|
|
tooltipEl.style.opacity = '0'
|
|
|
- tooltipEl.style.transform = 'scale(.85)'
|
|
|
|
|
|
|
+ tooltipEl.style.transform =
|
|
|
|
|
+ 'scale(.85)'
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
- if (!tooltipEl) return
|
|
|
|
|
-
|
|
|
|
|
- tooltipEl.style.visibility = 'hidden'
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ !tooltipEl ||
|
|
|
|
|
+ currentTarget
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ tooltipEl.style.visibility =
|
|
|
|
|
+ 'hidden'
|
|
|
}, 180)
|
|
}, 180)
|
|
|
|
|
|
|
|
currentTarget = null
|
|
currentTarget = null
|
|
@@ -184,44 +241,66 @@ export function useMouseTooltip() {
|
|
|
isInitialized = true
|
|
isInitialized = true
|
|
|
|
|
|
|
|
moveHandler = (e) => {
|
|
moveHandler = (e) => {
|
|
|
- if (!currentTarget) return
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ !currentTarget ||
|
|
|
|
|
+ !tooltipEl
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
lastMouseX = e.clientX
|
|
lastMouseX = e.clientX
|
|
|
lastMouseY = e.clientY
|
|
lastMouseY = e.clientY
|
|
|
|
|
|
|
|
if (rafId) return
|
|
if (rafId) return
|
|
|
|
|
|
|
|
- rafId = requestAnimationFrame(() => {
|
|
|
|
|
- rafId = null
|
|
|
|
|
|
|
+ rafId =
|
|
|
|
|
+ requestAnimationFrame(() => {
|
|
|
|
|
+ rafId = null
|
|
|
|
|
|
|
|
- updatePosition(
|
|
|
|
|
- lastMouseX,
|
|
|
|
|
- lastMouseY
|
|
|
|
|
- )
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ updatePosition(
|
|
|
|
|
+ lastMouseX,
|
|
|
|
|
+ lastMouseY
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
overHandler = (e) => {
|
|
overHandler = (e) => {
|
|
|
- const target = e.target.closest('.cursor-pointer')
|
|
|
|
|
|
|
+ const target =
|
|
|
|
|
+ e.target.closest(
|
|
|
|
|
+ '.cursor-pointer'
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
if (!target) return
|
|
if (!target) return
|
|
|
|
|
|
|
|
- if (target === currentTarget) return
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ target === currentTarget
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
showTooltip(e, target)
|
|
showTooltip(e, target)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
outHandler = (e) => {
|
|
outHandler = (e) => {
|
|
|
- const target = e.target.closest('.cursor-pointer')
|
|
|
|
|
|
|
+ const target =
|
|
|
|
|
+ e.target.closest(
|
|
|
|
|
+ '.cursor-pointer'
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
if (!target) return
|
|
if (!target) return
|
|
|
|
|
|
|
|
- if (target !== currentTarget) return
|
|
|
|
|
|
|
+ if (
|
|
|
|
|
+ target !== currentTarget
|
|
|
|
|
+ ) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 在当前元素内部移动
|
|
|
|
|
|
|
+ // 子元素内部移动
|
|
|
if (
|
|
if (
|
|
|
e.relatedTarget &&
|
|
e.relatedTarget &&
|
|
|
- target.contains(e.relatedTarget)
|
|
|
|
|
|
|
+ target.contains(
|
|
|
|
|
+ e.relatedTarget
|
|
|
|
|
+ )
|
|
|
) {
|
|
) {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
@@ -249,6 +328,8 @@ export function useMouseTooltip() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function cleanup() {
|
|
function cleanup() {
|
|
|
|
|
+ disconnectObserver()
|
|
|
|
|
+
|
|
|
if (rafId) {
|
|
if (rafId) {
|
|
|
cancelAnimationFrame(rafId)
|
|
cancelAnimationFrame(rafId)
|
|
|
rafId = null
|
|
rafId = null
|
|
@@ -273,6 +354,7 @@ export function useMouseTooltip() {
|
|
|
true
|
|
true
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (overHandler) {
|
|
if (overHandler) {
|
|
|
document.removeEventListener(
|
|
document.removeEventListener(
|
|
|
'mouseover',
|
|
'mouseover',
|
|
@@ -280,6 +362,7 @@ export function useMouseTooltip() {
|
|
|
true
|
|
true
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (outHandler) {
|
|
if (outHandler) {
|
|
|
document.removeEventListener(
|
|
document.removeEventListener(
|
|
|
'mouseout',
|
|
'mouseout',
|
|
@@ -287,12 +370,15 @@ export function useMouseTooltip() {
|
|
|
true
|
|
true
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
moveHandler = null
|
|
moveHandler = null
|
|
|
overHandler = null
|
|
overHandler = null
|
|
|
outHandler = null
|
|
outHandler = null
|
|
|
|
|
|
|
|
isInitialized = false
|
|
isInitialized = false
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
init()
|
|
init()
|
|
|
|
|
+
|
|
|
return cleanup
|
|
return cleanup
|
|
|
}
|
|
}
|