npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@avatijs/pointer

v0.1.1

Published

Pointer package part of Avati project

Downloads

61

Readme

Advanced Pointer Event Manager

Pointer is a robust and flexible TypeScript library designed to manage mouse and pointer events on HTML elements. It offers advanced features such as multiple pointer tracking, velocity and acceleration calculations, debouncing, initial position setting, asynchronous event handlers, and a publish-subscribe (pub-sub) system for efficient event management. This library is ideal for building interactive and responsive user interfaces, supporting complex interactions with ease.

Table of Contents

  1. Features
  2. Installation
  3. Getting Started
  4. Usage Examples
  5. API Reference
  6. Development
  7. Contributing
  8. License

Features

  • Unified Pointer Handling: Supports both pointer and mouse events seamlessly.
  • Multiple Pointer Support: Tracks multiple pointers (e.g., multi-touch) with individual states.
  • State Tracking: Monitors pointer positions, velocities, accelerations, and speeds.
  • Debounce Support: Prevents excessive event handler invocations with customizable debounce times.
  • Publish-Subscribe Pattern: Decouples event management, allowing multiple subscribers to handle events independently.
  • Flexible Event Targets: Listens to "pointermove" events on window, document, or the specific element.
  • Initial Position Setting: Programmatically set initial positions of pointers for simulations and testing.
  • Asynchronous Event Handlers: Supports asynchronous functions as event handlers for advanced operations.
  • Performance Optimizations: Utilizes requestAnimationFrame for efficient updates.
  • TypeScript Support: Strongly typed interfaces enhance developer experience and reduce runtime errors.
  • Separation of Concerns: Modular architecture ensures maintainability and ease of extension.

Installation

You can install Pointer via npm:

npm install @avatijs/pointer

Alternatively, you can include it directly in your project by downloading the compiled JavaScript files.


Getting Started

Importing the Library

If you're using ES modules, you can import the library as follows:

import { AdvancedPointerEventManager } from '@avatijs/pointer';

If you prefer using script tags, ensure that your project is set up to handle ES modules:

<script type="module">
  import { AdvancedPointerEventManager } from './dist/AdvancedPointerEventManager.js';
</script>

Usage Examples

Basic Usage

Below is a basic example demonstrating how to initialize the AdvancedPointerEventManager, subscribe to pointer events, and handle them.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AdvancedPointerEventManager Demo</title>
  <style>
    #interactive-area {
      width: 600px;
      height: 400px;
      background-color: #e0e0e0;
      border: 2px solid #333;
      position: relative;
      margin: 50px auto;
      user-select: none;
    }
    #pointer-info {
      position: absolute;
      top: 10px;
      left: 10px;
      background: rgba(255, 255, 255, 0.9);
      padding: 10px;
      border-radius: 5px;
      font-family: Arial, sans-serif;
      font-size: 14px;
      box-shadow: 0 0 5px rgba(0,0,0,0.3);
      white-space: pre-line; /* Preserve line breaks */
    }
  </style>
</head>
<body>
  <div id="interactive-area">
    <div id="pointer-info">Pointer Info</div>
  </div>

  <!-- Include the compiled library as an ES module -->
  <script type="module">
    import { AdvancedPointerEventManager } from './dist/AdvancedPointerEventManager.js';

    const interactiveArea = document.getElementById('interactive-area');
    const pointerInfo = document.getElementById('pointer-info');

    // Initialize the manager with default configurations
    const manager = new AdvancedPointerEventManager(interactiveArea, { debounceTime: 100, moveEventTarget: 'window' });

    // Handler for pointer down
    const handlePointerDown = (event, state) => {
      pointerInfo.textContent = `Pointer Down at (${event.clientX}, ${event.clientY})\nActive Pointers: ${state.size}`;
      interactiveArea.style.backgroundColor = '#c0ffc0';
    };
    manager.on('pointerdown', handlePointerDown);

    // Handler for pointer up
    const handlePointerUp = (event, state) => {
      pointerInfo.textContent = `Pointer Up at (${event.clientX}, ${event.clientY})\nActive Pointers: ${state.size}`;
      interactiveArea.style.backgroundColor = '#e0e0e0';
    };
    manager.on('pointerup', handlePointerUp);

    // Handler for pointer move
    const handlePointerMove = (event, state) => {
      let info = `<strong>Pointer Move</strong>\n`;
      state.forEach((pointer, id) => {
        info += `Pointer ${id}: (${pointer.position.x.toFixed(2)}, ${pointer.position.y.toFixed(2)}) | Speed: ${pointer.speed.toFixed(2)} px/s\n`;
      });
      pointerInfo.innerHTML = info;
    };
    manager.on('pointermove', handlePointerMove, 50); // Debounced by 50ms

    // Handler for click events
    const handleClick = (event) => {
      console.log('Element clicked at:', event.clientX, event.clientY);
    };
    manager.on('click', handleClick);

    // Handler for double-click events
    const handleDblClick = (event) => {
      console.log('Element double-clicked at:', event.clientX, event.clientY);
    };
    manager.on('dblclick', handleDblClick);

    // Handler for context menu events with custom behavior
    const handleContextMenu = (event) => {
      event.preventDefault(); // Prevent the default context menu
      alert(`Custom context menu at (${event.clientX}, ${event.clientY})`);
    };
    manager.on('contextmenu', handleContextMenu);

    // Periodically log the pointer state
    setInterval(() => {
      const state = manager.getState();
      console.log('Current Pointer State:', state);
    }, 5000);
  </script>
</body>
</html>

Advanced Usage with Async Handlers

This example demonstrates how to use asynchronous event handlers, allowing for operations like fetching data or awaiting user inputs within handlers.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AdvancedPointerEventManager Async Demo</title>
  <style>
    #interactive-area {
      width: 600px;
      height: 400px;
      background-color: #e0e0e0;
      border: 2px solid #333;
      position: relative;
      margin: 50px auto;
      user-select: none;
    }
    #pointer-info {
      position: absolute;
      top: 10px;
      left: 10px;
      background: rgba(255, 255, 255, 0.9);
      padding: 10px;
      border-radius: 5px;
      font-family: Arial, sans-serif;
      font-size: 14px;
      box-shadow: 0 0 5px rgba(0,0,0,0.3);
      white-space: pre-line; /* Preserve line breaks */
    }
  </style>
</head>
<body>
  <div id="interactive-area">
    <div id="pointer-info">Pointer Info</div>
  </div>

  <!-- Include the compiled library as an ES module -->
  <script type="module">
    import { AdvancedPointerEventManager } from './dist/AdvancedPointerEventManager.js';

    const interactiveArea = document.getElementById('interactive-area');
    const pointerInfo = document.getElementById('pointer-info');

    // Initialize the manager with default configurations
    const manager = new AdvancedPointerEventManager(interactiveArea, { debounceTime: 100, moveEventTarget: 'window' });

    // Asynchronous handler for pointer down
    const handlePointerDownAsync = async (event, state) => {
      // Simulate an asynchronous operation (e.g., fetching data)
      await new Promise(resolve => setTimeout(resolve, 100));
      pointerInfo.textContent = `Async Pointer Down at (${event.clientX}, ${event.clientY})\nActive Pointers: ${state.size}`;
      interactiveArea.style.backgroundColor = '#c0ffc0';
    };
    manager.on('pointerdown', handlePointerDownAsync);

    // Asynchronous handler for pointer move
    const handlePointerMoveAsync = async (event, state) => {
      // Simulate an asynchronous operation (e.g., sending data to a server)
      await fetch('https://api.example.com/log', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          pointerId: event.pointerId,
          position: { x: event.clientX, y: event.clientY },
          speed: state.get(event.pointerId)?.speed || 0,
        }),
      }).catch(err => console.error('Failed to log pointer move:', err));

      let info = `<strong>Async Pointer Move</strong>\n`;
      state.forEach((pointer, id) => {
        info += `Pointer ${id}: (${pointer.position.x.toFixed(2)}, ${pointer.position.y.toFixed(2)}) | Speed: ${pointer.speed.toFixed(2)} px/s\n`;
      });
      pointerInfo.innerHTML = info;
    };
    manager.on('pointermove', handlePointerMoveAsync, 50); // Debounced by 50ms

    // Asynchronous handler for click events
    const handleClickAsync = async (event) => {
      // Simulate an asynchronous operation
      await new Promise(resolve => setTimeout(resolve, 50));
      console.log('Async Element clicked at:', event.clientX, event.clientY);
    };
    manager.on('click', handleClickAsync);

    // Periodically log the pointer state
    setInterval(async () => {
      const state = manager.getState();
      console.log('Current Pointer State:', state);
    }, 5000);
  </script>
</body>
</html>

Setting Initial Positions

This example shows how to programmatically set initial positions for pointers, which is useful for simulations or testing scenarios.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>AdvancedPointerEventManager Initial Position Demo</title>
  <style>
    #interactive-area {
      width: 600px;
      height: 400px;
      background-color: #e0e0e0;
      border: 2px solid #333;
      position: relative;
      margin: 50px auto;
      user-select: none;
    }
    #pointer-info {
      position: absolute;
      top: 10px;
      left: 10px;
      background: rgba(255, 255, 255, 0.9);
      padding: 10px;
      border-radius: 5px;
      font-family: Arial, sans-serif;
      font-size: 14px;
      box-shadow: 0 0 5px rgba(0,0,0,0.3);
      white-space: pre-line; /* Preserve line breaks */
    }
  </style>
</head>
<body>
  <div id="interactive-area">
    <div id="pointer-info">Pointer Info</div>
  </div>

  <!-- Include the compiled library as an ES module -->
  <script type="module">
    import { AdvancedPointerEventManager } from './dist/AdvancedPointerEventManager.js';

    const interactiveArea = document.getElementById('interactive-area');
    const pointerInfo = document.getElementById('pointer-info');

    // Initialize the manager with default configurations
    const manager = new AdvancedPointerEventManager(interactiveArea, { debounceTime: 100, moveEventTarget: 'window' });

    // Set initial positions for two pointers
    manager.setInitialPointerPosition(1, { x: 150, y: 150 });
    manager.setInitialPointerPosition(2, { x: 300, y: 200 });

    // Handler for pointer down
    const handlePointerDown = (event, state) => {
      pointerInfo.textContent = `Pointer Down at (${event.clientX}, ${event.clientY})\nActive Pointers: ${state.size}`;
      interactiveArea.style.backgroundColor = '#c0ffc0';
    };
    manager.on('pointerdown', handlePointerDown);

    // Handler for pointer move
    const handlePointerMove = (event, state) => {
      let info = `<strong>Pointer Move</strong>\n`;
      state.forEach((pointer, id) => {
        info += `Pointer ${id}: (${pointer.position.x.toFixed(2)}, ${pointer.position.y.toFixed(2)}) | Speed: ${pointer.speed.toFixed(2)} px/s\n`;
      });
      pointerInfo.innerHTML = info;
    };
    manager.on('pointermove', handlePointerMove, 50); // Debounced by 50ms

    // Periodically log the pointer state
    setInterval(() => {
      const state = manager.getState();
      console.log('Current Pointer State:', state);
    }, 5000);
  </script>
</body>
</html>

API Reference

Classes

AdvancedPointerEventManager

Manages pointer and mouse events with advanced features such as multiple pointer tracking, velocity and acceleration calculations, debouncing, initial position setting, asynchronous event handlers, and a publish-subscribe system.

Constructor
constructor(element: HTMLElement, config?: PointerEventManagerConfig)
  • Parameters:
    • element: The HTML element to attach event listeners to.
    • config (optional): Configuration object.
      • debounceTime: Default debounce time in milliseconds for event handlers.
      • moveEventTarget: Target for "pointermove" events. Can be 'element', 'window', or 'document'. Defaults to 'element'.
Methods
  • on(eventType: PubSubEventType, handler: PubSubHandlers[K], debounceTime?: number): void

    Subscribes a handler to a specific event type. Handlers can be asynchronous functions.

    • Parameters:
      • eventType: Type of the event (e.g., 'pointermove').
      • handler: Callback function to execute when the event occurs. Can be synchronous or asynchronous.
      • debounceTime (optional): Debounce time in milliseconds. Overrides the default debounceTime set in the configuration for this handler.
  • off(eventType: PubSubEventType, handler: PubSubHandlers[K]): void

    Unsubscribes a handler from a specific event type.

    • Parameters:
      • eventType: Type of the event.
      • handler: The handler function to remove.
  • destroy(): void

    Removes all event listeners and cleans up resources.

  • getState(): PointerState

    Retrieves the current state of all active pointers, including their positions, velocities, accelerations, and speeds.

    • Returns:
      PointerState object containing a Map of active pointers keyed by pointerId.
  • setInitialPointerPosition(pointerId: number, position: { x: number; y: number }): void

    Programmatically sets the initial position of a pointer.

    • Parameters:
      • pointerId: The identifier of the pointer.
      • position: An object containing x and y coordinates.

Interfaces

PointerEventManagerConfig

Configuration options for the AdvancedPointerEventManager.

interface PointerEventManagerConfig {
  debounceTime?: number; // Default debounce time in milliseconds
  moveEventTarget?: 'element' | 'window' | 'document'; // Target for pointermove events
}
  • Properties:
    • debounceTime (optional): Sets the default debounce time in milliseconds for event handlers.
    • moveEventTarget (optional): Specifies the target for "pointermove" events. Can be 'element', 'window', or 'document'. Defaults to 'element'.

PointerState

Represents the current state of all active pointers.

interface PointerState {
  pointers: Map<number, SinglePointerState>;
}
  • Properties:
    • pointers: A Map where each key is a pointerId and the value is a SinglePointerState.

SinglePointerState

Represents the state of a single pointer.

interface SinglePointerState {
  isDown: boolean;
  position: { x: number; y: number };
  velocity: { vx: number; vy: number };
  acceleration: { ax: number; ay: number };
  speed: number;
}
  • Properties:
    • isDown: Indicates if the pointer is currently pressed.
    • position: Current x and y coordinates of the pointer.
    • velocity: Current velocity components (vx, vy) in pixels per second.
    • acceleration: Current acceleration components (ax, ay) in pixels per second squared.
    • speed: Current speed of the pointer in pixels per second.

PubSubHandlers

Defines the handler functions for each event type in the pub-sub system.

interface PubSubHandlers {
  pointerdown?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  pointerup?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  pointermove?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  pointerenter?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  pointerleave?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  pointercancel?: (event: PointerEvent, state: Map<number, any>) => void | Promise<void>;
  click?: (event: MouseEvent) => void | Promise<void>;
  dblclick?: (event: MouseEvent) => void | Promise<void>;
  contextmenu?: (event: MouseEvent) => void | Promise<void>;
}
  • Handlers:
    • Each event type has an optional handler that can be synchronous or asynchronous (Promise<void>).

Types

PointerEventType

Enumerates the supported pointer and mouse event types.

type PointerEventType =
  | 'pointerdown'
  | 'pointerup'
  | 'pointermove'
  | 'pointerenter'
  | 'pointerleave'
  | 'pointercancel'
  | 'click'
  | 'dblclick'
  | 'contextmenu';

PubSubEventType

Enumerates the event types supported by the pub-sub system.

type PubSubEventType =
  | 'pointerdown'
  | 'pointerup'
  | 'pointermove'
  | 'pointerenter'
  | 'pointerleave'
  | 'pointercancel'
  | 'click'
  | 'dblclick'
  | 'contextmenu';

Contributing

Contributions are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request.

Steps to Contribute

  1. Fork the Repository

  2. Create a New Branch

    git checkout -b feature/YourFeatureName
  3. Make Your Changes

  4. Commit Your Changes

    git commit -m "Add Your Feature"
  5. Push to the Branch

    git push origin feature/YourFeatureName
  6. Open a Pull Request


Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

I welcome contributions from developers of all experience levels. If you have an idea, found a bug, or want to improve something, I encourage you to get involved!

How to Contribute

  1. Read Contributing Guide for details on how to get started.
  2. Fork the repository and make your changes.
  3. Submit a pull request, and we’ll review it as soon as possible.

License

MIT License

Avati is open-source and distributed under the MIT License.


Follow on Twitter Follow on LinkedIn Follow on Medium Made with ❤️ Star on GitHub Follow on GitHub