Hone logo
Hone
Problems

Vue UseMouse: Track Mouse Coordinates with a Custom Hook

This challenge is about building a reusable Vue composable function, useMouse, that tracks the user's mouse coordinates on the screen. This is a common utility in interactive web applications, enabling features like parallax effects, custom cursor tracking, or dynamic UI element positioning based on mouse movement.

Problem Description

Your task is to create a composable function named useMouse using Vue 3's Composition API with TypeScript. This function should:

  • Track Mouse Position: Listen for mousemove events on the window object and update reactive state with the current X and Y coordinates of the mouse.
  • Return Reactive State: Expose the mouse coordinates as reactive ref objects so that they can be easily used and updated in any Vue component.
  • Clean Up Event Listeners: Ensure that the mousemove event listener is properly removed when the component using the composable is unmounted to prevent memory leaks.

Key Requirements:

  • The function must be written in TypeScript.
  • It should export a single composable function named useMouse.
  • The useMouse function should return an object containing two ref properties: x and y, representing the mouse's horizontal and vertical coordinates respectively.
  • The x and y refs should be initialized to 0 or null before the first mouse movement.
  • The event listener should be attached to the window object.
  • A beforeUnmount hook (or equivalent lifecycle management) should be used to remove the event listener.

Expected Behavior:

When useMouse is called within a Vue component's setup function, the x and y refs will start with their initial values. As the user moves their mouse over the browser window, the x and y refs will update in real-time to reflect the mouse's current position. When the component using useMouse is no longer rendered, the associated event listener will be detached from the window.

Edge Cases to Consider:

  • Initial State: How should the x and y coordinates be represented before the first mousemove event occurs?
  • No Mouse Movement: If the user never moves the mouse, the coordinates should reflect their initial state.

Examples

Example 1:

Input (in a Vue component's setup):

<template>
  <p>Mouse X: {{ mouseX }}</p>
  <p>Mouse Y: {{ mouseY }}</p>
</template>

<script setup lang="ts">
import { useMouse } from './composables/useMouse'; // Assuming useMouse is in this path

const { x: mouseX, y: mouseY } = useMouse();
</script>

Output (when the mouse is at client coordinates 150, 200):

<p>Mouse X: 150</p>
<p>Mouse Y: 200</p>

Explanation:

The useMouse composable is imported and used. The returned x and y refs are destructured and aliased to mouseX and mouseY for clarity in the template. As the mouse moves, mouseX and mouseY update, and the template displays the current coordinates.

Example 2: Initial State

Input (in a Vue component's setup):

<template>
  <p>Mouse X: {{ mouseX }}</p>
  <p>Mouse Y: {{ mouseY }}</p>
</template>

<script setup lang="ts">
import { useMouse } from './composables/useMouse';

// Assume useMouse initializes x and y to 0
const { x: mouseX, y: mouseY } = useMouse();
</script>

Output (when the component is mounted but before any mouse movement):

<p>Mouse X: 0</p>
<p>Mouse Y: 0</p>

Explanation:

The useMouse composable initializes its internal x and y refs to 0. The template displays these initial values until a mousemove event occurs.

Constraints

  • The solution must be implemented using Vue 3 Composition API and TypeScript.
  • The useMouse function should return an object with x and y properties, where each is a Ref<number>.
  • Event listeners should be managed to prevent memory leaks.
  • The mousemove event listener should be attached to the window object.

Notes

  • Consider using ref from vue to create reactive variables.
  • You will need to use Vue's lifecycle hooks to manage the event listener. The onUnmounted hook is particularly relevant for cleanup.
  • The MouseEvent object has clientX and clientY properties that provide the coordinates relative to the viewport.
  • Think about how you want to handle the initial state of the mouse coordinates. Returning 0 is a common approach, but you might consider null as an alternative if that makes more sense for your application's logic.
Loading editor...
typescript