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
|
// Global activation state - shared across all instances
|
||||||
let globalActive = false
|
let globalActive = false
|
||||||
let globalDeactivateTimer: ReturnType<typeof setTimeout> | null = null
|
let globalDeactivateTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
|
// Track if we've seen real mouse movement (not touch-simulated)
|
||||||
|
let hasRealMouse = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -23,44 +25,88 @@ const props = defineProps<{
|
|||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
const mouseX = ref(0)
|
const mouseX = ref(0)
|
||||||
const mouseY = 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(() => ({
|
const tooltipStyle = computed(() => ({
|
||||||
left: `${mouseX.value + 12}px`,
|
left: `${mouseX.value}px`,
|
||||||
top: `${mouseY.value + 12}px`,
|
top: `${mouseY.value}px`,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const startHover = (e: MouseEvent) => {
|
// Check if the device likely has a real mouse (fine pointer)
|
||||||
mouseX.value = e.clientX
|
const hasFinePointer = () => window.matchMedia('(pointer: fine)').matches
|
||||||
mouseY.value = e.clientY
|
|
||||||
// Clear any pending deactivation
|
const showTooltip = () => {
|
||||||
|
visible.value = true
|
||||||
|
globalActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const scheduleTooltip = () => {
|
||||||
|
if (settleTimer) clearTimeout(settleTimer)
|
||||||
if (globalDeactivateTimer) {
|
if (globalDeactivateTimer) {
|
||||||
clearTimeout(globalDeactivateTimer)
|
clearTimeout(globalDeactivateTimer)
|
||||||
globalDeactivateTimer = null
|
globalDeactivateTimer = null
|
||||||
}
|
}
|
||||||
const delay = globalActive ? 0 : (props.delay ?? 800)
|
const delay = globalActive ? 0 : (props.delay ?? 900)
|
||||||
hoverTimer = setTimeout(() => {
|
settleTimer = setTimeout(showTooltip, delay)
|
||||||
visible.value = true
|
}
|
||||||
globalActive = true
|
|
||||||
}, 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) => {
|
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
|
mouseX.value = e.clientX
|
||||||
mouseY.value = e.clientY
|
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 = () => {
|
const endHover = () => {
|
||||||
if (hoverTimer) {
|
if (settleTimer) {
|
||||||
clearTimeout(hoverTimer)
|
clearTimeout(settleTimer)
|
||||||
hoverTimer = null
|
settleTimer = null
|
||||||
}
|
}
|
||||||
visible.value = false
|
visible.value = false
|
||||||
// Deactivate global state after a short delay if no new tooltip started
|
// Deactivate global state after a short delay if no new tooltip started
|
||||||
if (globalDeactivateTimer) clearTimeout(globalDeactivateTimer)
|
if (globalDeactivateTimer) clearTimeout(globalDeactivateTimer)
|
||||||
globalDeactivateTimer = setTimeout(() => {
|
globalDeactivateTimer = setTimeout(() => {
|
||||||
globalActive = false
|
globalActive = false
|
||||||
}, 500)
|
}, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
Reference in New Issue
Block a user