diff --git a/app/components/NDS.vue b/app/components/NDS.vue index 8d06992..0d9d1c1 100644 --- a/app/components/NDS.vue +++ b/app/components/NDS.vue @@ -274,12 +274,7 @@ const pressButton = (button: string) => { window.dispatchEvent(new KeyboardEvent("keydown", { key: `NDS_${button}` })); }; -const handleClick = (event: MouseEvent) => { - if (!hasAnimated.value) { - animateIntro(); - return; - } - +const raycast = (event: MouseEvent) => { const domElement = renderer.instance.domElement; const rect = domElement.getBoundingClientRect(); @@ -293,32 +288,50 @@ const handleClick = (event: MouseEvent) => { ); const intersects = raycaster.intersectObjects(model.children, true); - const intersection = intersects[0]; + return intersects[0]; +}; + +const getScreenCanvas = (name: string) => { + if (name === TOP_SCREEN) return props.topScreenCanvas; + if (name === BOTTOM_SCREEN) return props.bottomScreenCanvas; + return null; +}; + +const dispatchScreenEvent = ( + type: string, + canvas: HTMLCanvasElement, + intersection: THREE.Intersection, +) => { + if (!intersection.uv) return; + + const logicalX = (1 - intersection.uv.x) * LOGICAL_WIDTH; + const logicalY = (1 - intersection.uv.y) * LOGICAL_HEIGHT; + + const rect = canvas.getBoundingClientRect(); + canvas.dispatchEvent( + new MouseEvent(type, { + bubbles: true, + cancelable: true, + clientX: (logicalX / LOGICAL_WIDTH) * rect.width + rect.left, + clientY: (logicalY / LOGICAL_HEIGHT) * rect.height + rect.top, + }), + ); +}; + +const handleMouseDown = (event: MouseEvent) => { + if (!hasAnimated.value) { + animateIntro(); + return; + } + + const intersection = raycast(event); if (!intersection?.uv) return; switch (intersection.object.name) { case TOP_SCREEN: case BOTTOM_SCREEN: { - const canvas = - intersection.object.name === TOP_SCREEN - ? props.topScreenCanvas - : props.bottomScreenCanvas; - - if (!canvas) break; - - const logicalX = (1 - intersection.uv.x) * LOGICAL_WIDTH; - const logicalY = (1 - intersection.uv.y) * LOGICAL_HEIGHT; - - const rect = canvas.getBoundingClientRect(); - canvas.dispatchEvent( - new MouseEvent("click", { - bubbles: true, - cancelable: true, - clientX: (logicalX / LOGICAL_WIDTH) * rect.width + rect.left, - clientY: (logicalY / LOGICAL_HEIGHT) * rect.height + rect.top, - }), - ); - + const canvas = getScreenCanvas(intersection.object.name); + if (canvas) dispatchScreenEvent("mousedown", canvas, intersection); break; } @@ -361,6 +374,16 @@ const handleClick = (event: MouseEvent) => { } }; +const handleClick = (event: MouseEvent) => { + if (!hasAnimated.value) return; + + const intersection = raycast(event); + if (!intersection?.uv) return; + + const canvas = getScreenCanvas(intersection.object.name); + if (canvas) dispatchScreenEvent("click", canvas, intersection); +}; + const handleMouseUp = () => { if (mousePressedButton) { physicalButtonsDown.delete(mousePressedButton); @@ -377,14 +400,19 @@ onMounted(() => { app.ready = true; if (renderer) { - renderer.instance.domElement.addEventListener("mousedown", handleClick); + renderer.instance.domElement.addEventListener("mousedown", handleMouseDown); + renderer.instance.domElement.addEventListener("click", handleClick); renderer.instance.domElement.addEventListener("mouseup", handleMouseUp); } }); onUnmounted(() => { if (renderer) { - renderer.instance.domElement.removeEventListener("mousedown", handleClick); + renderer.instance.domElement.removeEventListener( + "mousedown", + handleMouseDown, + ); + renderer.instance.domElement.removeEventListener("click", handleClick); renderer.instance.domElement.removeEventListener("mouseup", handleMouseUp); } topScreenTexture?.dispose();