1.0.1 â€ĸ Published 6 months ago

@aniruddha1806/use-mouse-move v1.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
6 months ago

React useMouseMove Hook

A powerful React hook for tracking mouse position with support for element-specific coordinates, performance optimization through throttling and debouncing, and boundary detection.

Installation

npm install @aniruddha1806/use-mouse-move

Features

  • đŸ–ąī¸ Global Mouse Tracking: Track mouse position across the entire viewport
  • đŸŽ¯ Element-Specific Tracking: Get mouse coordinates relative to any element
  • ⚡ Performance Optimized: Built-in throttling and debouncing options
  • 🔍 Boundary Detection: Know when mouse is within element bounds
  • 📱 Responsive: Works with dynamic element sizes and positions
  • 🎨 Flexible: Use for hover effects, drag operations, cursor followers
  • đŸĒļ Lightweight: Zero dependencies beyond React
  • 🔧 Easy Integration: Simple API with sensible defaults

Quick Start

import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function App() {
  const { x, y } = useMouseMove();
  
  return (
    <div>
      <h1>Mouse Tracker</h1>
      <p>Mouse position: ({x}, {y})</p>
    </div>
  );
}

API

Parameters

The hook accepts an options object with the following properties:

OptionTypeDefaultDescription
elementRefObject<HTMLElement>nullElement to track mouse position within
throttlenumber0Throttle updates in milliseconds (0 = disabled)
debouncenumber0Debounce updates in milliseconds (0 = disabled)

Returns

The hook returns an object with the following properties:

PropertyTypeDescription
xnumberGlobal mouse X coordinate
ynumberGlobal mouse Y coordinate
elementXnumber \| nullMouse X coordinate relative to element
elementYnumber \| nullMouse Y coordinate relative to element
isInBoundsbooleanWhether mouse is within element boundaries

Examples

Basic Global Mouse Tracking

Track mouse position across the entire viewport:

import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function GlobalMouseTracker() {
  const { x, y } = useMouseMove();
  
  return (
    <div style={{ height: '100vh', padding: '20px' }}>
      <h2>Global Mouse Position</h2>
      <p>X: {x}px</p>
      <p>Y: {y}px</p>
      
      {/* Visual indicator */}
      <div
        style={{
          position: 'fixed',
          left: x - 5,
          top: y - 5,
          width: '10px',
          height: '10px',
          backgroundColor: 'red',
          borderRadius: '50%',
          pointerEvents: 'none',
          zIndex: 9999,
        }}
      />
    </div>
  );
}

Element-Specific Mouse Tracking

Track mouse position relative to a specific element:

import { useRef } from 'react';
import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function ElementMouseTracker() {
  const boxRef = useRef(null);
  const { x, y, elementX, elementY, isInBounds } = useMouseMove({
    element: boxRef
  });
  
  return (
    <div style={{ padding: '20px' }}>
      <h2>Element Mouse Tracking</h2>
      
      <div
        ref={boxRef}
        style={{
          width: '400px',
          height: '300px',
          border: '2px solid #333',
          backgroundColor: isInBounds ? '#e8f5e8' : '#f5f5f5',
          position: 'relative',
          margin: '20px 0',
        }}
      >
        <div style={{ padding: '10px' }}>
          <p>Global: ({x}, {y})</p>
          <p>Element: ({elementX}, {elementY})</p>
          <p>In bounds: {isInBounds ? 'Yes' : 'No'}</p>
        </div>
        
        {/* Show mouse position within element */}
        {isInBounds && (
          <div
            style={{
              position: 'absolute',
              left: elementX - 5,
              top: elementY - 5,
              width: '10px',
              height: '10px',
              backgroundColor: 'blue',
              borderRadius: '50%',
              pointerEvents: 'none',
            }}
          />
        )}
      </div>
    </div>
  );
}

Performance Optimization with Throttling

Use throttling to limit update frequency for better performance:

import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function ThrottledMouseTracker() {
  // Update position at most every 16ms (~60fps)
  const { x, y } = useMouseMove({ throttle: 16 });
  
  return (
    <div style={{ height: '100vh', padding: '20px' }}>
      <h2>Throttled Mouse Tracking (60fps)</h2>
      <p>Position: ({x}, {y})</p>
      <p>Updates are throttled to ~60fps for smooth performance</p>
      
      {/* Smooth following cursor */}
      <div
        style={{
          position: 'fixed',
          left: x - 25,
          top: y - 25,
          width: '50px',
          height: '50px',
          backgroundColor: 'rgba(0, 123, 255, 0.7)',
          borderRadius: '50%',
          pointerEvents: 'none',
          transition: 'transform 0.1s ease-out',
          transform: 'scale(1)',
        }}
      />
    </div>
  );
}

Debounced Mouse Tracking

Use debouncing to delay updates until mouse stops moving:

import { useState } from 'react';
import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function DebouncedMouseTracker() {
  const [lastStoppedPosition, setLastStoppedPosition] = useState({ x: 0, y: 0 });
  
  // Update position only after mouse stops moving for 300ms
  const { x, y } = useMouseMove({ debounce: 300 });
  
  // Update stopped position when debounced position changes
  React.useEffect(() => {
    setLastStoppedPosition({ x, y });
  }, [x, y]);
  
  return (
    <div style={{ height: '100vh', padding: '20px' }}>
      <h2>Debounced Mouse Tracking</h2>
      <p>Move your mouse around and stop...</p>
      <p>Last stopped position: ({lastStoppedPosition.x}, {lastStoppedPosition.y})</p>
      
      {/* Show marker where mouse last stopped */}
      <div
        style={{
          position: 'fixed',
          left: lastStoppedPosition.x - 10,
          top: lastStoppedPosition.y - 10,
          width: '20px',
          height: '20px',
          backgroundColor: 'green',
          borderRadius: '50%',
          pointerEvents: 'none',
        }}
      />
    </div>
  );
}

Interactive Hover Effects

Create interactive hover effects using element tracking:

import { useRef } from 'react';
import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function InteractiveCard() {
  const cardRef = useRef(null);
  const { elementX, elementY, isInBounds } = useMouseMove({
    element: cardRef,
    throttle: 16
  });
  
  // Calculate rotation based on mouse position
  const getRotation = () => {
    if (!isInBounds || !cardRef.current) return { rotateX: 0, rotateY: 0 };
    
    const { clientWidth, clientHeight } = cardRef.current;
    const centerX = clientWidth / 2;
    const centerY = clientHeight / 2;
    
    const rotateX = ((elementY - centerY) / centerY) * -10;
    const rotateY = ((elementX - centerX) / centerX) * 10;
    
    return { rotateX, rotateY };
  };
  
  const { rotateX, rotateY } = getRotation();
  
  return (
    <div style={{ padding: '50px', display: 'flex', justifyContent: 'center' }}>
      <div
        ref={cardRef}
        style={{
          width: '300px',
          height: '200px',
          backgroundColor: '#fff',
          borderRadius: '12px',
          boxShadow: '0 10px 30px rgba(0,0,0,0.2)',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          cursor: 'pointer',
          transform: `perspective(1000px) rotateX(\${rotateX}deg) rotateY(\${rotateY}deg) scale(\${isInBounds ? 1.05 : 1})`,
          transition: isInBounds ? 'none' : 'transform 0.3s ease-out',
        }}
      >
        <div style={{ textAlign: 'center' }}>
          <h3>Interactive Card</h3>
          <p>Hover to see 3D effect</p>
          {isInBounds && (
            <p style={{ fontSize: '12px', color: '#666' }}>
              Mouse: ({Math.round(elementX)}, {Math.round(elementY)})
            </p>
          )}
        </div>
      </div>
    </div>
  );
}

Cursor Follower

Create a custom cursor that follows the mouse:

import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function CursorFollower() {
  const { x, y } = useMouseMove({ throttle: 8 });
  
  return (
    <div style={{ height: '100vh', cursor: 'none' }}>
      <div style={{ padding: '20px' }}>
        <h2>Custom Cursor Follower</h2>
        <p>Move your mouse around to see the custom cursor</p>
        <button style={{ margin: '10px', padding: '10px 20px' }}>
          Hover me
        </button>
        <button style={{ margin: '10px', padding: '10px 20px' }}>
          And me
        </button>
      </div>
      
      {/* Custom cursor */}
      <div
        style={{
          position: 'fixed',
          left: x,
          top: y,
          width: '20px',
          height: '20px',
          backgroundColor: 'rgba(255, 0, 150, 0.8)',
          borderRadius: '50%',
          pointerEvents: 'none',
          zIndex: 9999,
          transform: 'translate(-50%, -50%)',
          transition: 'transform 0.1s ease-out',
        }}
      />
      
      {/* Cursor trail */}
      <div
        style={{
          position: 'fixed',
          left: x,
          top: y,
          width: '40px',
          height: '40px',
          border: '2px solid rgba(255, 0, 150, 0.3)',
          borderRadius: '50%',
          pointerEvents: 'none',
          zIndex: 9998,
          transform: 'translate(-50%, -50%)',
          transition: 'all 0.2s ease-out',
        }}
      />
    </div>
  );
}

Drawing Canvas

Use mouse tracking for drawing applications:

import { useRef, useState, useEffect } from 'react';
import { useMouseMove } from '@aniruddha1806/use-mouse-move';

function DrawingCanvas() {
  const canvasRef = useRef(null);
  const [isDrawing, setIsDrawing] = useState(false);
  const [lastPosition, setLastPosition] = useState({ x: 0, y: 0 });
  
  const { elementX, elementY, isInBounds } = useMouseMove({
    element: canvasRef,
    throttle: 8
  });
  
  // Handle drawing
  useEffect(() => {
    if (!isDrawing || !isInBounds || !canvasRef.current) return;
    
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    ctx.beginPath();
    ctx.moveTo(lastPosition.x, lastPosition.y);
    ctx.lineTo(elementX, elementY);
    ctx.stroke();
    
    setLastPosition({ x: elementX, y: elementY });
  }, [elementX, elementY, isDrawing, isInBounds, lastPosition]);
  
  const startDrawing = (e) => {
    setIsDrawing(true);
    setLastPosition({ x: elementX, y: elementY });
  };
  
  const stopDrawing = () => {
    setIsDrawing(false);
  };
  
  const clearCanvas = () => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h2>Drawing Canvas</h2>
      <p>Click and drag to draw</p>
      
      <div style={{ marginBottom: '10px' }}>
        <button onClick={clearCanvas}>Clear Canvas</button>
      </div>
      
      <canvas
        ref={canvasRef}
        width={600}
        height={400}
        onMouseDown={startDrawing}
        onMouseUp={stopDrawing}
        onMouseLeave={stopDrawing}
        style={{
          border: '2px solid #333',
          cursor: 'crosshair',
          backgroundColor: 'white',
        }}
      />
      
      <p style={{ marginTop: '10px', fontSize: '14px', color: '#666' }}>
        Mouse: ({Math.round(elementX || 0)}, {Math.round(elementY || 0)}) | 
        Drawing: {isDrawing ? 'Yes' : 'No'}
      </p>
    </div>
  );
}

TypeScript Usage

The hook provides full TypeScript support:

import { useRef } from 'react';
import { useMouseMove } from '@aniruddha1806/use-mouse-move';

interface MousePosition {
  x: number;
  y: number;
  elementX: number | null;
  elementY: number | null;
  isInBounds: boolean;
}

interface UseMouseMoveOptions {
  element?: React.RefObject<HTMLElement> | null;
  throttle?: number;
  debounce?: number;
}

function TypedMouseTracker() {
  const elementRef = useRef<HTMLDivElement>(null);
  
  // Hook with typed options
  const options: UseMouseMoveOptions = {
    element: elementRef,
    throttle: 16,
    debounce: 0
  };
  
  // Destructure with proper typing
  const mouseData: MousePosition = useMouseMove(options);
  const { x, y, elementX, elementY, isInBounds } = mouseData;
  
  return (
    <div>
      <div ref={elementRef} style={{ width: 300, height: 200, border: '1px solid #ccc' }}>
        <p>Global: ({x}, {y})</p>
        <p>Element: ({elementX}, {elementY})</p>
        <p>In bounds: {isInBounds.toString()}</p>
      </div>
    </div>
  );
}

// Custom hook using useMouseMove
function useMouseDistance(elementRef: React.RefObject<HTMLElement>) {
  const { elementX, elementY, isInBounds } = useMouseMove({
    element: elementRef,
    throttle: 16
  });
  
  const distance = React.useMemo(() => {
    if (!isInBounds || elementX === null || elementY === null || !elementRef.current) {
      return 0;
    }
    
    const centerX = elementRef.current.clientWidth / 2;
    const centerY = elementRef.current.clientHeight / 2;
    
    return Math.sqrt(
      Math.pow(elementX - centerX, 2) + Math.pow(elementY - centerY, 2)
    );
  }, [elementX, elementY, isInBounds, elementRef]);
  
  return { distance, isInBounds };
}

Performance Tips

1. Use Throttling for Smooth Animations

For smooth 60fps animations, use a 16ms throttle:

const { x, y } = useMouseMove({ throttle: 16 });

2. Use Debouncing for Expensive Operations

For operations that don't need real-time updates:

const { x, y } = useMouseMove({ debounce: 100 });

3. Combine with useMemo for Complex Calculations

const { elementX, elementY } = useMouseMove({ element: ref });

const expensiveCalculation = useMemo(() => {
  // Expensive calculation based on mouse position
  return complexFunction(elementX, elementY);
}, [elementX, elementY]);
1.0.1

6 months ago

1.0.0

8 months ago