From eac2902d9b651fba69427539a360637ff9a77773 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen <78434827+TCOTC@users.noreply.github.com> Date: Wed, 4 Dec 2024 08:53:53 +0800 Subject: [PATCH] Improve tooltip (#13326) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 减少元素更新 * 改进悬浮提示元素位置计算 messageElement.innerHTML = message; 会更新元素的内容,元素此时的 left 属性会对元素的宽度产生影响,导致更新后的元素(比如元素内的文本意外换行了)与直接新建的元素(元素内的文本不会换行)宽度不一致。messageElement.clientWidth 会获取到不符合预期的宽度,进而导致 left 计算错误。 * fix https://github.com/siyuan-note/siyuan/issues/12680 * 隐藏悬浮提示增加少许延迟,避免在相邻的元素间移动时悬浮提示频繁闪烁 * 频率高的放前面 --- app/src/dialog/tooltip.ts | 66 ++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/app/src/dialog/tooltip.ts b/app/src/dialog/tooltip.ts index a547cd138..97ac0f546 100644 --- a/app/src/dialog/tooltip.ts +++ b/app/src/dialog/tooltip.ts @@ -1,5 +1,7 @@ import {isMobile} from "../util/functions"; +let hideTooltipTimeout: NodeJS.Timeout | null = null; + export const showTooltip = (message: string, target: Element, tooltipClass?: string) => { if (isMobile()) { return; @@ -9,17 +11,33 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str hideTooltip(); return; } - let messageElement = document.getElementById("tooltip"); - if (!messageElement) { - document.body.insertAdjacentHTML("beforeend", `
${message}
`); - messageElement = document.getElementById("tooltip"); - } else { - messageElement.className = "tooltip"; - messageElement.innerHTML = message; + + // 清除 hideTooltip 的定时器 + if (hideTooltipTimeout) { + clearTimeout(hideTooltipTimeout); + hideTooltipTimeout = null; } - if (tooltipClass) { - messageElement.classList.add("tooltip--" + tooltipClass); + let messageElement = document.getElementById("tooltip"); + if (!messageElement) { + document.body.insertAdjacentHTML("beforeend", `
${message}
`); + messageElement = document.getElementById("tooltip"); + } else { + const currentClassName = messageElement.className; + const currentMessage = messageElement.textContent; + + let newClassName = "tooltip"; + if (tooltipClass) { + newClassName += " tooltip--" + tooltipClass; + } + // 避免不必要的更新 + if (currentClassName !== newClassName) { + messageElement.className = newClassName; + } + if (currentMessage !== message) { + // 鼠标在按钮等多层结构的元素上小幅移动时会频繁更新 + messageElement.innerHTML = message; + } } let left = targetRect.left; @@ -32,12 +50,7 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str left = targetRect.right - messageElement.clientWidth; } - if (position?.endsWith("bottom")) { - top += parseInt(position.replace("right", "").replace("left", "")); - } else if (position?.endsWith("top")) { - // 编辑器动态滚动条 - top = targetRect.top - messageElement.clientHeight; - } else if (position === "parentE") { + if (position === "parentE") { // file tree and outline、backlink top = parentRect.top; left = parentRect.right + 8; @@ -45,6 +58,11 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str // 数据库属性视图 top = parentRect.top + (parseInt(position) || 8); left = parentRect.left - messageElement.clientWidth; + } else if (position?.endsWith("bottom")) { + top += parseInt(position.replace("right", "").replace("left", "")); + } else if (position?.endsWith("top")) { + // 编辑器动态滚动条 + top = targetRect.top - messageElement.clientHeight; } const topHeight = position === "parentE" ? top : targetRect.top; @@ -52,6 +70,10 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str messageElement.style.maxHeight = Math.max(topHeight, bottomHeight) + "px"; + // 避免原本的 top 和 left 影响计算 + messageElement.style.top = "0px"; + messageElement.style.left = "0px"; + if (top + messageElement.clientHeight > window.innerHeight && topHeight > bottomHeight) { messageElement.style.top = ((position === "parentE" ? parentRect.bottom : targetRect.top) - messageElement.clientHeight) + "px"; } else { @@ -62,7 +84,7 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str if (position === "parentE") { messageElement.style.left = (parentRect.left - 8 - messageElement.clientWidth) + "px"; } else { - messageElement.style.left = (window.innerWidth - messageElement.clientWidth) + "px"; + messageElement.style.left = (window.innerWidth - 1 - messageElement.clientWidth) + "px"; } } else { messageElement.style.left = Math.max(0, left) + "px"; @@ -70,8 +92,14 @@ export const showTooltip = (message: string, target: Element, tooltipClass?: str }; export const hideTooltip = () => { - const messageElement = document.getElementById("tooltip"); - if (messageElement) { - messageElement.remove(); + if (hideTooltipTimeout) { + clearTimeout(hideTooltipTimeout); } + hideTooltipTimeout = setTimeout(() => { + const messageElement = document.getElementById("tooltip"); + if (messageElement) { + messageElement.remove(); + } + hideTooltipTimeout = null; + }, 50); };