Improved tooltip display behavior; disabled for touch, less eager to activate on mouse, closer to cursor.
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
// Global activation state - shared across all instances
|
||||
let globalActive = false
|
||||
let globalDeactivateTimer: ReturnType<typeof setTimeout> | null = null
|
||||
// Track if we've seen real mouse movement (not touch-simulated)
|
||||
let hasRealMouse = false
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -23,44 +25,88 @@ const props = defineProps<{
|
||||
const visible = ref(false)
|
||||
const mouseX = ref(0)
|
||||
const mouseY = ref(0)
|
||||
let hoverTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let settleTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let lastMoveX = 0
|
||||
let lastMoveY = 0
|
||||
|
||||
// Movement threshold (pixels) - cursor must settle within this radius
|
||||
const SETTLE_THRESHOLD = 8
|
||||
|
||||
const tooltipStyle = computed(() => ({
|
||||
left: `${mouseX.value + 12}px`,
|
||||
top: `${mouseY.value + 12}px`,
|
||||
left: `${mouseX.value}px`,
|
||||
top: `${mouseY.value}px`,
|
||||
}))
|
||||
|
||||
const startHover = (e: MouseEvent) => {
|
||||
mouseX.value = e.clientX
|
||||
mouseY.value = e.clientY
|
||||
// Clear any pending deactivation
|
||||
// Check if the device likely has a real mouse (fine pointer)
|
||||
const hasFinePointer = () => window.matchMedia('(pointer: fine)').matches
|
||||
|
||||
const showTooltip = () => {
|
||||
visible.value = true
|
||||
globalActive = true
|
||||
}
|
||||
|
||||
const scheduleTooltip = () => {
|
||||
if (settleTimer) clearTimeout(settleTimer)
|
||||
if (globalDeactivateTimer) {
|
||||
clearTimeout(globalDeactivateTimer)
|
||||
globalDeactivateTimer = null
|
||||
}
|
||||
const delay = globalActive ? 0 : (props.delay ?? 800)
|
||||
hoverTimer = setTimeout(() => {
|
||||
visible.value = true
|
||||
globalActive = true
|
||||
}, delay)
|
||||
const delay = globalActive ? 0 : (props.delay ?? 900)
|
||||
settleTimer = setTimeout(showTooltip, delay)
|
||||
}
|
||||
|
||||
const startHover = (e: MouseEvent) => {
|
||||
// Ignore touch events (no fine pointer and no confirmed real mouse)
|
||||
if (!hasFinePointer() && !hasRealMouse) return
|
||||
|
||||
mouseX.value = e.clientX
|
||||
mouseY.value = e.clientY
|
||||
lastMoveX = e.clientX
|
||||
lastMoveY = e.clientY
|
||||
}
|
||||
|
||||
const updatePosition = (e: MouseEvent) => {
|
||||
// Detect real mouse via movement (touch events don't generate continuous mousemove)
|
||||
if (e.movementX !== 0 || e.movementY !== 0) hasRealMouse = true
|
||||
if (!hasFinePointer() && !hasRealMouse) return
|
||||
|
||||
mouseX.value = e.clientX
|
||||
mouseY.value = e.clientY
|
||||
|
||||
// If tooltip is already visible, just update position
|
||||
if (visible.value) return
|
||||
|
||||
const dx = e.clientX - lastMoveX
|
||||
const dy = e.clientY - lastMoveY
|
||||
const distance = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
// If cursor moved beyond threshold, reset settle timer
|
||||
if (distance > SETTLE_THRESHOLD) {
|
||||
lastMoveX = e.clientX
|
||||
lastMoveY = e.clientY
|
||||
if (settleTimer) {
|
||||
clearTimeout(settleTimer)
|
||||
settleTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule tooltip when cursor settles
|
||||
if (!settleTimer) {
|
||||
scheduleTooltip()
|
||||
}
|
||||
}
|
||||
|
||||
const endHover = () => {
|
||||
if (hoverTimer) {
|
||||
clearTimeout(hoverTimer)
|
||||
hoverTimer = null
|
||||
if (settleTimer) {
|
||||
clearTimeout(settleTimer)
|
||||
settleTimer = null
|
||||
}
|
||||
visible.value = false
|
||||
// Deactivate global state after a short delay if no new tooltip started
|
||||
if (globalDeactivateTimer) clearTimeout(globalDeactivateTimer)
|
||||
globalDeactivateTimer = setTimeout(() => {
|
||||
globalActive = false
|
||||
}, 500)
|
||||
}, 400)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
|
||||
Reference in New Issue
Block a user