From eac7d09244e9c9eb06f8988a6dfdaf774e0e3555 Mon Sep 17 00:00:00 2001 From: Neko Ayaka Date: Thu, 23 Oct 2025 22:00:33 +0800 Subject: [PATCH] feat(stage-tamagotchi): even better fade on hover --- .../composables/electron-vueuse/index.ts | 1 + .../use-electron-mouse-in-element/index.ts | 26 +--------- .../use-electron-mouse-in-window/index.ts | 9 ++++ .../src/renderer/pages/index.vue | 51 ++++++++++--------- 4 files changed, 39 insertions(+), 48 deletions(-) create mode 100644 apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-window/index.ts diff --git a/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/index.ts b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/index.ts index e60e4a557..e1064cf72 100644 --- a/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/index.ts +++ b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/index.ts @@ -3,5 +3,6 @@ export { useElectronEventaContext, useElectronEventaInvoke } from './use-electro export { useElectronMouse, useElectronMouseEventTarget } from './use-electron-mouse' export type { UseMouseInElementReturn } from './use-electron-mouse-in-element' export { useElectronMouseInElement } from './use-electron-mouse-in-element' +export { useElectronMouseInWindow } from './use-electron-mouse-in-window' export { useElectronRelativeMouse } from './use-electron-relative-mouse' export { useElectronWindowBounds } from './use-electron-window-bounds' diff --git a/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-element/index.ts b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-element/index.ts index e9507a9b6..f2ad0bf73 100644 --- a/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-element/index.ts +++ b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-element/index.ts @@ -1,34 +1,10 @@ -import type { MaybeElementRef, UseMouseOptions } from '@vueuse/core' +import type { MaybeElementRef, MouseInElementOptions } from '@vueuse/core' import { defaultWindow, tryOnMounted, unrefElement, useEventListener, useMutationObserver, useResizeObserver } from '@vueuse/core' import { shallowRef, watch } from 'vue' import { useElectronRelativeMouse } from '../use-electron-relative-mouse' -export interface MouseInElementOptions extends UseMouseOptions { - /** - * Whether to handle mouse events when the cursor is outside the target element. - * When enabled, mouse position will continue to be tracked even when outside the element bounds. - * - * @default true - */ - handleOutside?: boolean - - /** - * Listen to window resize event - * - * @default true - */ - windowScroll?: boolean - - /** - * Listen to window scroll event - * - * @default true - */ - windowResize?: boolean -} - /** * Reactive mouse position related to an element. * diff --git a/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-window/index.ts b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-window/index.ts new file mode 100644 index 000000000..42482feaf --- /dev/null +++ b/apps/stage-tamagotchi/src/renderer/composables/electron-vueuse/use-electron-mouse-in-window/index.ts @@ -0,0 +1,9 @@ +import type { MouseInElementOptions } from '@vueuse/core' + +import { useElectronMouseInElement } from '../use-electron-mouse-in-element' + +export function useElectronMouseInWindow( + options: MouseInElementOptions = {}, +) { + return useElectronMouseInElement(undefined, options) +} diff --git a/apps/stage-tamagotchi/src/renderer/pages/index.vue b/apps/stage-tamagotchi/src/renderer/pages/index.vue index 865a93912..69a795046 100644 --- a/apps/stage-tamagotchi/src/renderer/pages/index.vue +++ b/apps/stage-tamagotchi/src/renderer/pages/index.vue @@ -10,50 +10,55 @@ import ControlsIsland from '../components/Widgets/ControlsIsland/index.vue' import ResourceStatusIsland from '../components/Widgets/ResourceStatusIsland/index.vue' import { electron } from '../../shared/electron' -import { useElectronEventaInvoke, useElectronMouseInElement, useElectronRelativeMouse } from '../composables/electron-vueuse' +import { + useElectronEventaInvoke, + useElectronMouseInElement, + useElectronMouseInWindow, + useElectronRelativeMouse, +} from '../composables/electron-vueuse' import { useWindowStore } from '../stores/window' const resourceStatusIslandRef = ref>() const controlsIslandRef = ref>() const widgetStageRef = ref<{ canvasElement: () => HTMLCanvasElement }>() const stageCanvas = toRef(() => widgetStageRef.value?.canvasElement()) -const isClickThrough = ref(false) -const isPassingThrough = ref(false) -const isLoading = ref(true) const componentStateStage = ref<'pending' | 'loading' | 'mounted'>('pending') -const { x: relativeMouseX, y: relativeMouseY } = useElectronRelativeMouse() -const isTransparent = useCanvasPixelIsTransparentAtPoint(stageCanvas, relativeMouseX, relativeMouseY) -const setIgnoreMouseEvents = useElectronEventaInvoke(electron.window.setIgnoreMouseEvents) +const isLoading = ref(true) + +const isIgnoringMouseEvents = ref(false) +const shouldFadeOnCursorWithin = ref(false) + +const { isOutside: isOutsideWindow } = useElectronMouseInWindow() const { isOutside } = useElectronMouseInElement(controlsIslandRef) const isOutsideFor250Ms = debouncedRef(isOutside, 250) +const { x: relativeMouseX, y: relativeMouseY } = useElectronRelativeMouse() +const isTransparent = useCanvasPixelIsTransparentAtPoint(stageCanvas, relativeMouseX, relativeMouseY) + +const setIgnoreMouseEvents = useElectronEventaInvoke(electron.window.setIgnoreMouseEvents) const { scale, positionInPercentageString } = storeToRefs(useLive2d()) const { live2dLookAtX, live2dLookAtY } = storeToRefs(useWindowStore()) watch(componentStateStage, () => isLoading.value = componentStateStage.value !== 'mounted', { immediate: true }) + const { pause, resume } = watchPausable(isTransparent, (transparent) => { - isClickThrough.value = transparent - isPassingThrough.value = !transparent + shouldFadeOnCursorWithin.value = !transparent +}, { immediate: true }) - if (isPassingThrough.value) { - setIgnoreMouseEvents([true, { forward: true }]) - } - else { - setIgnoreMouseEvents([false, { forward: true }]) - } -}) - -watch(isOutsideFor250Ms, () => { +watch([isOutsideFor250Ms, isOutsideWindow], () => { if (!isOutsideFor250Ms.value) { - isClickThrough.value = false - isPassingThrough.value = false + isIgnoringMouseEvents.value = false + shouldFadeOnCursorWithin.value = false setIgnoreMouseEvents([false, { forward: true }]) pause() } else { - isClickThrough.value = true - isPassingThrough.value = true + isIgnoringMouseEvents.value = true + if (!isOutsideWindow.value && !isTransparent.value) { + shouldFadeOnCursorWithin.value = true + } + setIgnoreMouseEvents([true, { forward: true }]) resume() } @@ -77,7 +82,7 @@ watch(isOutsideFor250Ms, () => { >