useDataInterpretationTrigger.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { reactive } from 'vue';
  2. export interface TriggerState {
  3. visible: boolean;
  4. x: number;
  5. y: number;
  6. }
  7. export interface TriggerHandle {
  8. state: TriggerState;
  9. onEnter: (e: MouseEvent) => void;
  10. onMove: (e: MouseEvent) => void;
  11. onLeave: () => void;
  12. onBtnEnter: () => void;
  13. onBtnLeave: () => void;
  14. destroy: () => void;
  15. }
  16. export interface TriggerOptions {
  17. /** 鼠标停顿多久后显示按钮 (ms),默认 400 */
  18. pauseDelay?: number;
  19. /** 按钮显示后多久自动消失 (ms),默认 3000 */
  20. hideDelay?: number;
  21. }
  22. /**
  23. * 创建一个悬浮按钮触发器实例。
  24. * 每个需要数据解读的 DOM 元素(卡片行、表格行、3D 标记等)对应一个 trigger。
  25. *
  26. * 使用方式:
  27. * 1. 在 v-for 中为每行数据 createTrigger()
  28. * 2. 将 onEnter/onMove/onLeave 绑定到容器元素的 mouseenter/mousemove/mouseleave
  29. * 3. 将 DataInterpretationBtn 放在容器内,绑定 state 和 onBtnEnter/onBtnLeave
  30. */
  31. export function createTrigger(options?: TriggerOptions): TriggerHandle {
  32. const PAUSE_DELAY = options?.pauseDelay ?? 400;
  33. const HIDE_DELAY = options?.hideDelay ?? 3000;
  34. const state = reactive<TriggerState>({
  35. visible: false,
  36. x: 0,
  37. y: 0,
  38. });
  39. let pauseTimer: ReturnType<typeof setTimeout> | null = null;
  40. let hideTimer: ReturnType<typeof setTimeout> | null = null;
  41. function trackMouse(e: MouseEvent) {
  42. const target = e.currentTarget as HTMLElement;
  43. const rect = target.getBoundingClientRect();
  44. state.x = e.clientX - rect.left - 36;
  45. state.y = e.clientY - rect.top - 34;
  46. }
  47. function startPauseTimer() {
  48. pauseTimer = setTimeout(() => {
  49. state.visible = true;
  50. startHideTimer();
  51. }, PAUSE_DELAY);
  52. }
  53. function resetPauseTimer() {
  54. if (pauseTimer) {
  55. clearTimeout(pauseTimer);
  56. pauseTimer = null;
  57. }
  58. }
  59. function startHideTimer() {
  60. clearHideTimer();
  61. hideTimer = setTimeout(() => {
  62. state.visible = false;
  63. }, HIDE_DELAY);
  64. }
  65. function clearHideTimer() {
  66. if (hideTimer) {
  67. clearTimeout(hideTimer);
  68. hideTimer = null;
  69. }
  70. }
  71. function onEnter(e: MouseEvent) {
  72. clearHideTimer();
  73. trackMouse(e);
  74. startPauseTimer();
  75. }
  76. function onMove(e: MouseEvent) {
  77. if (state.visible) return;
  78. trackMouse(e);
  79. resetPauseTimer();
  80. startPauseTimer();
  81. }
  82. function onLeave() {
  83. resetPauseTimer();
  84. clearHideTimer();
  85. state.visible = false;
  86. }
  87. function onBtnEnter() {
  88. clearHideTimer();
  89. }
  90. function onBtnLeave() {
  91. startHideTimer();
  92. }
  93. function destroy() {
  94. resetPauseTimer();
  95. clearHideTimer();
  96. }
  97. return {
  98. state,
  99. onEnter,
  100. onMove,
  101. onLeave,
  102. onBtnEnter,
  103. onBtnLeave,
  104. destroy,
  105. };
  106. }