# Pointeract Pointeract is a lightweight, modular JavaScript/TypeScript library for handling user interactions with DOM elements. It provides robust gesture detection for multitouch, touchpad, and mouse interactions with a core bundle size of only 1KB minified + gzipped. Built on the SynthKernel architecture, it offers a highly modular, extensible, and efficient approach to interaction handling. The library excels at complex gestures where most interaction libraries fail, supporting mouse, mouse wheel, touch, and touchpad input. It is fully tree-shakable, meaning the fewer modules you use, the smaller your bundle becomes. Pointeract supports all modern browsers (IE11+) and works with any JavaScript framework through its ESM module format. ## Installation Install Pointeract via npm, pnpm, yarn, or bun, or include it directly from a CDN. ```bash # npm npm add pointeract # pnpm pnpm add pointeract # yarn yarn add pointeract # bun bun add pointeract ``` ```html ``` ## Pointeract Class - Core Instance The main orchestrator class that manages modules, events, and lifecycle. Pass a DOM element and optional modules to create an instance. ```typescript import { Pointeract, Drag, PreventDefault } from 'pointeract'; // Get a DOM element to attach interactions to const app = document.getElementById('app') as HTMLDivElement; // Define options with coordinate output format const options = { element: app, coordinateOutput: 'relative', // 'absolute' | 'relative' | 'relativeFraction' }; // Create instance with modules const pointeract = new Pointeract(options, [Drag, PreventDefault]); // Start monitoring interactions pointeract.start(); // Subscribe to drag events pointeract.on('drag', (e) => { console.log(`Dragged to (${e.x}, ${e.y}) with delta (${e.deltaX}, ${e.deltaY})`); }); // Output: Dragged to (150, 200) with delta (5, 3) ``` ## on() - Subscribe to Events Subscribe to interaction events with full TypeScript support. Returns the instance for method chaining. ```typescript import { Pointeract, Drag, Click, type Events } from 'pointeract'; const pointeract = new Pointeract({ element: app }, [Drag, Click]).start(); // Type-safe event handler const dragHandler = (e: Events<[typeof Drag]>['drag']) => { console.log(`Position: (${e.x}, ${e.y})`); console.log(`Delta: (${e.deltaX}, ${e.deltaY})`); }; // Chain multiple subscriptions pointeract .on('drag', dragHandler) .on('trueClick', (e) => { console.log(`Clicked at (${e.x}, ${e.y}), streak: ${e.streak}`); if (e.streak === 2) console.log('Double click detected!'); }); ``` ## off() - Unsubscribe from Events Remove event listeners by passing the same handler function reference. ```typescript import { Pointeract, Drag, type Events } from 'pointeract'; const pointeract = new Pointeract({ element: app }, [Drag]).start(); const handler = (e: Events<[typeof Drag]>['drag']) => { console.log(`Drag event: (${e.x}, ${e.y})`); }; // Subscribe pointeract.on('drag', handler); // Later, unsubscribe pointeract.off('drag', handler); ``` ## start() and stop() - Lifecycle Control Control the instance and individual module lifecycles. Stopping pauses event monitoring; starting resumes it. ```typescript import { Pointeract, Drag, PreventDefault } from 'pointeract'; const pointeract = new Pointeract({ element: app }, [Drag, PreventDefault]); // Start monitoring pointeract.start(); // Pause all interactions pointeract.stop(); // Resume monitoring pointeract.start(); // Stop specific modules only (PreventDefault disabled, Drag still active) pointeract.stop([PreventDefault]); // Re-enable specific modules pointeract.start([PreventDefault]); ``` ## dispose() - Cleanup Resources Completely dispose of the Pointeract instance, removing all listeners and cleaning up resources. ```typescript import { Pointeract, Drag } from 'pointeract'; const pointeract = new Pointeract({ element: app }, [Drag]) .start() .on('drag', (e) => console.log(e)); // When done, dispose completely (no need to call stop() or off() first) pointeract.dispose(); ``` ## Drag Module Handles single-pointer drag interactions. Dispatches `drag` events when mouse or touch is pressed and moved. ```typescript import { Pointeract, Drag } from 'pointeract'; const app = document.getElementById('canvas') as HTMLDivElement; let offsetX = 0; let offsetY = 0; const pointeract = new Pointeract({ element: app }, [Drag]) .start() .on('drag', (e) => { // e.deltaX, e.deltaY: movement since last event // e.x, e.y: current pointer position offsetX += e.deltaX; offsetY += e.deltaY; app.style.transform = `translate(${offsetX}px, ${offsetY}px)`; }); ``` ## Click Module Detects true clicks by tracking pointer movement. Only fires when movement is below threshold, unlike native click events. ```typescript import { Pointeract, Click } from 'pointeract'; const pointeract = new Pointeract({ element: app, clickStreakWindow: 400, // ms between clicks for streak detection clickMoveThreshold: 5, // max px movement to count as click }, [Click]) .start() .on('trueClick', (e) => { console.log(`Click at (${e.x}, ${e.y})`); console.log(`Target: ${e.target}`); // Detect multi-clicks if (e.streak === 1) console.log('Single click'); if (e.streak === 2) console.log('Double click'); if (e.streak === 3) console.log('Triple click'); }); ``` ## Swipe Module Detects swipe gestures in configurable directions with velocity and streak tracking. ```typescript import { Pointeract, Swipe } from 'pointeract'; const pointeract = new Pointeract({ element: app, swipeMinDistance: 20, // minimum px to trigger swipe swipeMinVelocity: 0, // minimum px/s velocity swipeStreakWindow: 400, // ms for grouping multi-finger swipes }, [Swipe]) .start() .on('swipe', (e) => { console.log(`Swipe ${e.direction}`); // 'left' | 'right' | 'up' | 'down' console.log(`Velocity: ${e.velocity} px/ms`); console.log(`Angle: ${e.angle} radians`); console.log(`Duration: ${e.duration} ms`); console.log(`Fingers: ${e.streak}`); // multi-finger swipe count }); // Custom 8-direction swipe detection const eightDirectionMap = { left: -(Math.PI / 8) * 7, 'down-left': -(Math.PI / 8) * 5, down: -(Math.PI / 8) * 3, 'down-right': -Math.PI / 8, right: Math.PI / 8, 'up-right': (Math.PI / 8) * 3, up: (Math.PI / 8) * 5, 'up-left': (Math.PI / 8) * 7, }; new Pointeract({ element: app, swipeDirectionMap: eightDirectionMap, }, [Swipe]).start(); ``` ## WheelPanZoom Module Handles mouse wheel and touchpad gestures for pan and zoom interactions with automatic control schema detection. ```typescript import { Pointeract, WheelPanZoom } from 'pointeract'; let scale = 1; let panX = 0; let panY = 0; const pointeract = new Pointeract({ element: app, proControlSchema: false, // true: ctrl+wheel=zoom, shift+wheel=pan-x, wheel=pan-y lockControlSchema: false, // prevent auto-detection zoomFactor: 0.1, // zoom speed multiplier }, [WheelPanZoom]) .start() .on('zoom', (e) => { // e.factor < 1: zoom out, e.factor > 1: zoom in // e.x, e.y: zoom origin coordinates scale *= e.factor; console.log(`Zoom to ${scale}x at (${e.x}, ${e.y})`); }) .on('pan', (e) => { panX += e.deltaX; panY += e.deltaY; console.log(`Pan to (${panX}, ${panY})`); }); ``` ## MultitouchPanZoom Module Handles two-finger touch gestures for pan (midpoint movement) and pinch-to-zoom (distance change). ```typescript import { Pointeract, MultitouchPanZoom } from 'pointeract'; let scale = 1; let panX = 0; let panY = 0; const pointeract = new Pointeract({ element: app }, [MultitouchPanZoom]) .start() .on('pan', (e) => { // Triggered when midpoint between two touches moves panX += e.deltaX; panY += e.deltaY; app.style.transform = `translate(${panX}px, ${panY}px) scale(${scale})`; }) .on('zoom', (e) => { // Triggered when distance between two touches changes scale *= e.factor; app.style.transform = `translate(${panX}px, ${panY}px) scale(${scale})`; }); ``` ## PreventDefault Module Disables default browser behaviors for touch/mouse/wheel events on the target element. ```typescript import { Pointeract, Drag, PreventDefault } from 'pointeract'; // Prevent browser scroll, text selection, and other defaults const pointeract = new Pointeract({ element: app }, [Drag, PreventDefault]) .start() .on('drag', (e) => { // Drag without triggering page scroll or text selection console.log(`Drag: (${e.deltaX}, ${e.deltaY})`); }); // Temporarily allow default behavior pointeract.stop([PreventDefault]); // Re-enable prevention pointeract.start([PreventDefault]); ``` ## Lubricator Module - Smooth Animations Intercepts and smoothifies events through interpolation for fluid animations. ```typescript import { Pointeract, WheelPanZoom, Drag, Lubricator, panPreset as pan, zoomPreset as zoom, dragPreset as drag } from 'pointeract'; // Using presets for quick configuration const pointeract = new Pointeract({ element: app, lubricator: { pan, zoom, drag }, }, [WheelPanZoom, Drag, Lubricator]) .start() .on('zoom', (e) => { // Smooth, interpolated zoom events console.log(`Smooth zoom factor: ${e.factor}`); }); // Custom lubricator configuration new Pointeract({ element: app, lubricator: { zoom: { decayFactor: 0.25, // lower = smoother (more lag) fields: { factor: { countType: 'product', // aggregate product equals raw diminishBoundary: 0.01, // stop when delta < 0.01 }, }, }, pan: { decayFactor: 0.3, fields: { deltaX: { countType: 'sum', diminishBoundary: 0.5 }, deltaY: { countType: 'sum', diminishBoundary: 0.5 }, }, }, }, }, [WheelPanZoom, Lubricator]).start(); ``` ## Options - Reactive Configuration Options are passed by reference and can be updated reactively at runtime. ```typescript import { Pointeract, WheelPanZoom, type Options } from 'pointeract'; const options: Options<[typeof WheelPanZoom]> = { element: app, coordinateOutput: 'absolute', zoomFactor: 0.1, }; const pointeract = new Pointeract(options, [WheelPanZoom]).start(); // Reactively update options at runtime options.coordinateOutput = 'relative'; // immediately takes effect options.zoomFactor = 0.2; // zoom speed changes instantly ``` ## TypeScript Types - Full Type Safety Access orchestrated types for options, events, and instance interfaces. ```typescript import { Pointeract, Drag, Click, type Options, type Events, type PointeractInterface } from 'pointeract'; // Type-safe options type MyOptions = Options<[typeof Drag, typeof Click]>; const options: MyOptions = { element: app, coordinateOutput: 'relative', clickStreakWindow: 400, }; // Type-safe event handlers type MyEvents = Events<[typeof Drag, typeof Click]>; const onDrag = (e: MyEvents['drag']) => console.log(e.x, e.y); const onClick = (e: MyEvents['trueClick']) => console.log(e.streak); // Type-safe instance type MyInstance = PointeractInterface<[typeof Drag, typeof Click]>; const instance: MyInstance = new Pointeract(options, [Drag, Click]); ``` ## Custom Modules - Extending Pointeract Create custom modules by extending BaseModule with hooks for pointer and wheel events. ```typescript import { BaseModule, BaseOptions, StdEvents, type BaseArgs } from 'pointeract'; // Define custom options interface CustomOptions extends BaseOptions { holdThreshold?: number; } // Define custom events interface CustomEvents extends StdEvents { hold: { x: number; y: number; duration: number }; } // Define augmentation (methods added to Pointeract instance) interface CustomAugmentation { getHoldCount: () => number; } class HoldModule extends BaseModule { private holdCount = 0; private startTime = 0; constructor(...args: BaseArgs) { super(...args); this.augment({ getHoldCount: () => this.holdCount }); } onPointerDown = (e: PointerEvent) => { this.startTime = Date.now(); }; onPointerUp = (e: PointerEvent, pointer: any) => { const duration = Date.now() - this.startTime; const threshold = this.options.holdThreshold ?? 500; if (duration >= threshold) { this.holdCount++; const coords = this.toTargetCoords({ x: e.clientX, y: e.clientY }); this.dispatch('hold', { x: coords.x, y: coords.y, duration }); } }; dispose = () => { this.holdCount = 0; }; } // Usage const pointeract = new Pointeract({ element: app, holdThreshold: 1000, }, [HoldModule]) .start() .on('hold', (e) => console.log(`Held for ${e.duration}ms at (${e.x}, ${e.y})`)); // Access augmented method console.log(`Total holds: ${pointeract.getHoldCount()}`); ``` ## Full Application Example Complete example combining multiple modules for a canvas-like interaction experience. ```typescript import { Pointeract, Drag, Click, WheelPanZoom, MultitouchPanZoom, PreventDefault, Lubricator, panPreset as pan, zoomPreset as zoom, type Events } from 'pointeract'; const canvas = document.getElementById('canvas') as HTMLDivElement; // State let scale = 1; let panX = 0; let panY = 0; // Options with smooth animations const options = { element: canvas, coordinateOutput: 'relative' as const, proControlSchema: true, lubricator: { pan, zoom }, }; // Create instance with all modules const pointeract = new Pointeract(options, [ Drag, Click, WheelPanZoom, MultitouchPanZoom, PreventDefault, Lubricator, ]) .start() .on('pan', (e) => { panX += e.deltaX; panY += e.deltaY; updateTransform(); }) .on('zoom', (e) => { scale *= e.factor; scale = Math.max(0.1, Math.min(10, scale)); // clamp scale updateTransform(); }) .on('trueClick', (e) => { if (e.streak === 2) { // Double-click to reset scale = 1; panX = 0; panY = 0; updateTransform(); } }); function updateTransform() { canvas.style.transform = `translate(${panX}px, ${panY}px) scale(${scale})`; } // Cleanup on page unload window.addEventListener('beforeunload', () => pointeract.dispose()); ``` ## Summary Pointeract provides a comprehensive solution for handling complex user interactions in web applications. Its modular architecture allows developers to include only the functionality they need, keeping bundle sizes minimal. The library is ideal for building canvas applications, image viewers, map interfaces, interactive diagrams, and any UI requiring sophisticated gesture handling. Key use cases include drag-and-drop interfaces, pinch-to-zoom galleries, swipe-based navigation, and smooth pan/zoom experiences. Integration with existing projects is straightforward through the standard module pattern. Pointeract works seamlessly with React, Vue, Angular, Svelte, and vanilla JavaScript applications. The reactive options system enables dynamic configuration changes, while the event-based API integrates naturally with state management solutions. For advanced use cases, the custom module API allows developers to extend functionality while maintaining full TypeScript support and the library's performance characteristics.