@use-gesture/core
Advanced tools
Comparing version 10.0.0-beta.1 to 10.0.0-beta.2
import { EventStore } from './EventStore'; | ||
import { TimeoutStore } from './TimeoutStore'; | ||
import { GestureKey, InternalConfig, InternalHandlers, NativeHandlers, State, UserGestureConfig } from './types'; | ||
interface ControllerConstructor { | ||
new (handlers: InternalHandlers): Controller; | ||
} | ||
export interface Controller { | ||
export declare class Controller { | ||
/** | ||
* The list of gestures handled by the Controller. | ||
*/ | ||
_gestures: Set<GestureKey>; | ||
gestures: Set<GestureKey>; | ||
/** | ||
* The event store that keeps track of the config.target listeners. | ||
*/ | ||
_targetEventStore: EventStore; | ||
private _targetEventStore; | ||
/** | ||
* Object that keeps track of all gesture event listeners. | ||
*/ | ||
_gestureEventStores: { | ||
gestureEventStores: { | ||
[key in GestureKey]?: EventStore; | ||
}; | ||
/** | ||
* Object that keeps track of all gesture timeouts. | ||
*/ | ||
_gestureTimeoutStores: { | ||
gestureTimeoutStores: { | ||
[key in GestureKey]?: TimeoutStore; | ||
}; | ||
/** | ||
* Gesture handlers. | ||
*/ | ||
_handlers: InternalHandlers; | ||
/** | ||
* Native event handlers. | ||
*/ | ||
_nativeHandlers?: NativeHandlers; | ||
/** | ||
* Computed configuration. | ||
*/ | ||
_config: InternalConfig; | ||
/** | ||
* Pointer ids active on the target. | ||
*/ | ||
_pointerIds: Set<number>; | ||
/** | ||
* Touch identifiers active on the target. | ||
*/ | ||
_touchIds: Set<number>; | ||
/** | ||
* The Controller state reflecting the state of all gestures. | ||
*/ | ||
handlers: InternalHandlers; | ||
private nativeHandlers?; | ||
config: InternalConfig; | ||
pointerIds: Set<number>; | ||
touchIds: Set<number>; | ||
state: State; | ||
constructor(handlers: InternalHandlers); | ||
/** | ||
@@ -56,3 +33,3 @@ * Sets pointer or touch ids based on the event. | ||
*/ | ||
setEventIds(this: Controller, event: TouchEvent | PointerEvent): void; | ||
setEventIds(event: TouchEvent | PointerEvent): void; | ||
/** | ||
@@ -63,3 +40,3 @@ * Attaches handlers to the controller. | ||
*/ | ||
applyHandlers(this: Controller, handlers: InternalHandlers, nativeHandlers?: NativeHandlers): void; | ||
applyHandlers(handlers: InternalHandlers, nativeHandlers?: NativeHandlers): void; | ||
/** | ||
@@ -70,3 +47,3 @@ * Compute and attaches a config to the controller. | ||
*/ | ||
applyConfig(this: Controller, config: UserGestureConfig, gestureKey?: GestureKey): void; | ||
applyConfig(config: UserGestureConfig, gestureKey?: GestureKey): void; | ||
/** | ||
@@ -76,3 +53,3 @@ * Cleans all side effects (listeners, timeouts). When the gesture is | ||
*/ | ||
clean(this: Controller): void; | ||
clean(): void; | ||
/** | ||
@@ -82,3 +59,3 @@ * Executes side effects (attaching listeneds to a `config.target`). Ran on | ||
*/ | ||
effect(this: Controller): void; | ||
effect(): () => void; | ||
/** | ||
@@ -89,5 +66,3 @@ * The bind function that can be returned by the gesture handler (a hook in | ||
*/ | ||
bind(this: Controller, ...args: any[]): NativeHandlers | void; | ||
bind(...args: any[]): any; | ||
} | ||
export declare const Controller: ControllerConstructor; | ||
export {}; |
import { Engine } from './Engine'; | ||
import type { Controller } from '../Controller'; | ||
import { CoordinatesKey, GestureKey } from '../types'; | ||
export interface CoordinatesEngineConstructor { | ||
new <Key extends CoordinatesKey>(ctrl: Controller, args: any[], key: Key): CoordinatesEngine<Key>; | ||
import { CoordinatesKey, Vector2 } from '../types'; | ||
export declare abstract class CoordinatesEngine<Key extends CoordinatesKey> extends Engine<Key> { | ||
reset(): void; | ||
init(): void; | ||
computeOffset(): void; | ||
computeMovement(): void; | ||
intent(v: Vector2): void; | ||
} | ||
export interface CoordinatesEngine<Key extends GestureKey = CoordinatesKey> extends Engine<Key> { | ||
} | ||
export declare const CoordinatesEngine: CoordinatesEngineConstructor; |
import { Controller } from '../Controller'; | ||
import { EventStore } from '../EventStore'; | ||
import { TimeoutStore } from '../TimeoutStore'; | ||
import { GestureKey, Handler, IngKey, InternalConfig, State, Vector2 } from '../types'; | ||
export interface EngineConstructor { | ||
new <Key extends GestureKey>(ctrl: Controller, args: any[], key: Key): Engine<Key>; | ||
import { GestureKey, IngKey, State, Vector2 } from '../types'; | ||
export interface Engine<Key extends GestureKey> { | ||
/** | ||
* Function that some gestures can use to add initilization | ||
* properties to the state when it is created. | ||
*/ | ||
init?(): void; | ||
/** | ||
* Setup function that some gestures can use to set additional properties of | ||
* the state when the gesture starts. | ||
*/ | ||
setup?(): void; | ||
/** | ||
* Function used by some gestures to determine the intentionality of a | ||
* a movement depending on thresholds. The intent function can change the | ||
* `state._active` or `state._blocked` flags if the gesture isn't intentional. | ||
* @param movement | ||
*/ | ||
intent?(movement: Vector2): void; | ||
} | ||
export interface Engine<Key extends GestureKey = GestureKey> { | ||
export declare abstract class Engine<Key extends GestureKey> { | ||
/** | ||
@@ -16,3 +30,3 @@ * The Controller handling state. | ||
*/ | ||
key: Key; | ||
readonly key: Key; | ||
/** | ||
@@ -22,3 +36,3 @@ * The key representing the active state of the gesture in the shared state. | ||
*/ | ||
ingKey: IngKey; | ||
abstract readonly ingKey: IngKey; | ||
/** | ||
@@ -28,56 +42,68 @@ * The arguments passed to the `bind` function. | ||
args: any[]; | ||
constructor(ctrl: Controller, args: any[], key: Key); | ||
/** | ||
* Shortcut to the gesture state read from the Controller. | ||
* Function implemented by gestures that compute the offset from the state | ||
* movement. | ||
*/ | ||
state: NonNullable<State[Key]>; | ||
abstract computeOffset(): void; | ||
/** | ||
* Shortcut to the shared state read from the Controller | ||
* Function implemented by the gestures that compute the movement from the | ||
* corrected offset (after bounds and potential rubberbanding). | ||
*/ | ||
shared: State['shared']; | ||
abstract computeMovement(): void; | ||
/** | ||
* Shortcut to the gesture config read from the Controller. | ||
* Executes the bind function so that listeners are properly set by the | ||
* Controller. | ||
* @param bindFunction | ||
*/ | ||
config: NonNullable<InternalConfig[Key]>; | ||
abstract bind(bindFunction: (device: string, action: string, handler: (event: any) => void, options?: AddEventListenerOptions) => void): void; | ||
/** | ||
* Shortcut to the shared config read from the Controller. | ||
* Shortcut to the gesture state read from the Controller. | ||
*/ | ||
sharedConfig: InternalConfig['shared']; | ||
get state(): NonNullable<State[Key]>; | ||
set state(state: NonNullable<State[Key]>); | ||
/** | ||
* Shortcut to the shared state read from the Controller | ||
*/ | ||
get shared(): import("../types").SharedGestureState; | ||
/** | ||
* Shortcut to the gesture event store read from the Controller. | ||
*/ | ||
eventStore: EventStore; | ||
get eventStore(): NonNullable<{ | ||
drag?: import("../EventStore").EventStore | undefined; | ||
wheel?: import("../EventStore").EventStore | undefined; | ||
scroll?: import("../EventStore").EventStore | undefined; | ||
move?: import("../EventStore").EventStore | undefined; | ||
hover?: import("../EventStore").EventStore | undefined; | ||
pinch?: import("../EventStore").EventStore | undefined; | ||
}[Key]>; | ||
/** | ||
* Shortcut to the gesture timeout store read from the Controller. | ||
*/ | ||
timeoutStore: TimeoutStore; | ||
get timeoutStore(): NonNullable<{ | ||
drag?: import("../TimeoutStore").TimeoutStore | undefined; | ||
wheel?: import("../TimeoutStore").TimeoutStore | undefined; | ||
scroll?: import("../TimeoutStore").TimeoutStore | undefined; | ||
move?: import("../TimeoutStore").TimeoutStore | undefined; | ||
hover?: import("../TimeoutStore").TimeoutStore | undefined; | ||
pinch?: import("../TimeoutStore").TimeoutStore | undefined; | ||
}[Key]>; | ||
/** | ||
* Shortcut to the gesture handler read from the Controller. | ||
* Shortcut to the gesture config read from the Controller. | ||
*/ | ||
handler: Handler<Key>; | ||
get config(): NonNullable<import("../types").InternalConfig[Key]>; | ||
/** | ||
* Function that some gestures can use to add initilization | ||
* properties to the state when it is created. | ||
* Shortcut to the shared config read from the Controller. | ||
*/ | ||
init?(this: Engine<Key>): void; | ||
get sharedConfig(): import("../types").InternalGenericOptions; | ||
/** | ||
* Setup function that some gestures can use to set additional properties of | ||
* the state when the gesture starts. | ||
* Shortcut to the gesture handler read from the Controller. | ||
*/ | ||
setup?(this: Engine<Key>): void; | ||
get handler(): NonNullable<import("../types").InternalHandlers[Key]>; | ||
reset(): void; | ||
/** | ||
* Function used by some gestures to determine the intentionality of a | ||
* a movement depending on thresholds. The intent function can change the | ||
* `state._active` or `state._blocked` flags if the gesture isn't intentional. | ||
* @param movement | ||
*/ | ||
intent?(this: Engine<Key>, movement: Vector2): void; | ||
/** | ||
* Function that resets the state. | ||
*/ | ||
reset(this: Engine<Key>): void; | ||
/** | ||
* Function ran at the start of the gesture. | ||
* @param event | ||
*/ | ||
start(this: Engine<Key>, event: NonNullable<State[Key]>['event']): void; | ||
start(event: NonNullable<State[Key]>['event']): void; | ||
/** | ||
@@ -87,28 +113,11 @@ * Computes all sorts of state attributes, including kinematics. | ||
*/ | ||
compute(this: Engine<Key>, event?: NonNullable<State[Key]>['event']): void; | ||
compute(event?: NonNullable<State[Key]>['event']): void; | ||
/** | ||
* Function implemented by gestures that compute the offset from the state | ||
* movement. | ||
*/ | ||
computeOffset(this: Engine<Key>): void; | ||
/** | ||
* Function implemented by the gestures that compute the movement from the | ||
* corrected offset (after bounds and potential rubberbanding). | ||
*/ | ||
computeMovement(this: Engine<Key>): void; | ||
/** | ||
* Fires the gesture handler. | ||
*/ | ||
emit(this: Engine<Key>): void; | ||
emit(): void; | ||
/** | ||
* Cleans the gesture timeouts and event listeners. | ||
*/ | ||
clean(this: Engine<Key>): void; | ||
/** | ||
* Executes the bind function so that listeners are properly set by the | ||
* Controller. | ||
* @param bindFunction | ||
*/ | ||
bind(this: Engine<Key>, bindFunction: (device: string, action: string, handler: (event: any) => void, options?: AddEventListenerOptions) => void): void; | ||
clean(): void; | ||
} | ||
export declare const Engine: EngineConstructor; |
@@ -1,11 +0,8 @@ | ||
interface EventStoreConstructor { | ||
new (ctrl: any): EventStore; | ||
import type { Controller } from './Controller'; | ||
export declare class EventStore { | ||
private _listeners; | ||
private _ctrl; | ||
constructor(ctrl: Controller); | ||
add(element: EventTarget, device: string, action: string, handler: (event: any) => void, options?: AddEventListenerOptions): void; | ||
clean(): void; | ||
} | ||
export interface EventStore { | ||
_ctrl: any; | ||
_listeners: (() => void)[]; | ||
clean(this: EventStore): void; | ||
add(this: EventStore, element: EventTarget, device: string, action: string, handler: (event: any) => void, options?: AddEventListenerOptions): void; | ||
} | ||
export declare const EventStore: EventStoreConstructor; | ||
export {}; |
import { ResolverMap } from './config/resolver'; | ||
import { DragEngineConstructor } from './engines/DragEngine/DragEngineCore'; | ||
import { HoverEngineConstructor } from './engines/HoverEngine/HoverEngineCore'; | ||
import { MoveEngineConstructor } from './engines/MoveEngine/MoveEngineCore'; | ||
import { PinchEngineConstructor } from './engines/PinchEngine/PinchEngineCore'; | ||
import { ScrollEngineConstructor } from './engines/ScrollEngine/ScrollEngineCore'; | ||
import { WheelEngineConstructor } from './engines/WheelEngine/WheelEngineCore'; | ||
import type { Controller } from './Controller'; | ||
import type { Engine } from './engines/Engine'; | ||
import { GestureHandlers, GestureKey, UserGestureConfig } from './types'; | ||
declare type GestureEngineConstuctor = DragEngineConstructor | ScrollEngineConstructor | WheelEngineConstructor | PinchEngineConstructor | HoverEngineConstructor | MoveEngineConstructor; | ||
export declare const EngineMap: Map<GestureKey, GestureEngineConstuctor>; | ||
export declare type EngineClass<Key extends GestureKey> = { | ||
new (controller: Controller, args: any[], key: Key): Engine<Key>; | ||
}; | ||
export declare const EngineMap: Map<GestureKey, EngineClass<any>>; | ||
export declare const ConfigResolverMap: Map<GestureKey, ResolverMap>; | ||
export declare function registerEngine(action: GestureKey, Engine: GestureEngineConstuctor): void; | ||
export declare function registerEngine<Key extends GestureKey>(action: Key, Engine: EngineClass<Key>): void; | ||
export declare function parseMergedHandlers(mergedHandlers: GestureHandlers, mergedConfig: UserGestureConfig): { | ||
@@ -18,2 +16,1 @@ handlers: {}; | ||
}; | ||
export {}; |
@@ -1,11 +0,6 @@ | ||
interface TimeoutStoreConstructor { | ||
new (): TimeoutStore; | ||
export declare class TimeoutStore { | ||
private _timeouts; | ||
add<FunctionType extends (...args: any) => any>(key: string, callback: FunctionType, ms?: number, ...args: Parameters<FunctionType>): void; | ||
remove(key: string): void; | ||
clean(): void; | ||
} | ||
export interface TimeoutStore { | ||
_timeouts: Map<string, number>; | ||
add<FunctionType extends (...args: any) => any>(this: TimeoutStore, key: string, callback: FunctionType, ms?: number, ...args: Parameters<FunctionType>): void; | ||
remove(this: TimeoutStore, key: string): void; | ||
clean(this: TimeoutStore): void; | ||
} | ||
export declare const TimeoutStore: TimeoutStoreConstructor; | ||
export {}; |
@@ -427,43 +427,50 @@ 'use strict'; | ||
const EventStore = function EventStore(ctrl) { | ||
this._ctrl = ctrl; | ||
this._listeners = []; | ||
}; | ||
class EventStore { | ||
constructor(ctrl) { | ||
_defineProperty(this, "_listeners", []); | ||
EventStore.prototype.add = function (element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl._config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
this._ctrl = ctrl; | ||
} | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
}; | ||
add(element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl.config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
EventStore.prototype.clean = function () { | ||
this._listeners.forEach(remove => remove()); | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
} | ||
this._listeners = []; | ||
}; | ||
clean() { | ||
this._listeners.forEach(remove => remove()); | ||
const TimeoutStore = function TimeoutStore() { | ||
this._timeouts = new Map(); | ||
}; | ||
this._listeners = []; | ||
} | ||
TimeoutStore.prototype.add = function (key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
} | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
}; | ||
class TimeoutStore { | ||
constructor() { | ||
_defineProperty(this, "_timeouts", new Map()); | ||
} | ||
TimeoutStore.prototype.remove = function (key) { | ||
const timeout = this._timeouts.get(key); | ||
add(key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
if (timeout) window.clearTimeout(timeout); | ||
}; | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
} | ||
TimeoutStore.prototype.clean = function () { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
remove(key) { | ||
const timeout = this._timeouts.get(key); | ||
this._timeouts.clear(); | ||
}; | ||
if (timeout) window.clearTimeout(timeout); | ||
} | ||
clean() { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
this._timeouts.clear(); | ||
} | ||
} | ||
function call(v, ...args) { | ||
@@ -494,91 +501,100 @@ if (typeof v === 'function') { | ||
const Controller = function Controller(handlers) { | ||
this._gestures = new Set(); | ||
this._targetEventStore = new EventStore(this); | ||
this._gestureEventStores = {}; | ||
this._gestureTimeoutStores = {}; | ||
this._handlers = {}; | ||
this._nativeHandlers = {}; | ||
this._config = {}; | ||
this._pointerIds = new Set(); | ||
this._touchIds = new Set(); | ||
this.state = { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}; | ||
resolveGestures(this, handlers); | ||
}; | ||
class Controller { | ||
constructor(handlers) { | ||
_defineProperty(this, "gestures", new Set()); | ||
Controller.prototype.setEventIds = function (event) { | ||
if (isTouch(event)) { | ||
this._touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this._pointerIds.delete(event.pointerId);else this._pointerIds.add(event.pointerId); | ||
_defineProperty(this, "_targetEventStore", new EventStore(this)); | ||
_defineProperty(this, "gestureEventStores", {}); | ||
_defineProperty(this, "gestureTimeoutStores", {}); | ||
_defineProperty(this, "handlers", {}); | ||
_defineProperty(this, "config", {}); | ||
_defineProperty(this, "pointerIds", new Set()); | ||
_defineProperty(this, "touchIds", new Set()); | ||
_defineProperty(this, "state", { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}); | ||
resolveGestures(this, handlers); | ||
} | ||
}; | ||
Controller.prototype.applyHandlers = function (handlers, nativeHandlers) { | ||
this._handlers = handlers; | ||
this._nativeHandlers = nativeHandlers; | ||
}; | ||
setEventIds(event) { | ||
if (isTouch(event)) { | ||
this.touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this.pointerIds.delete(event.pointerId);else this.pointerIds.add(event.pointerId); | ||
} | ||
} | ||
Controller.prototype.applyConfig = function (config, gestureKey) { | ||
this._config = parse(config, gestureKey); | ||
}; | ||
applyHandlers(handlers, nativeHandlers) { | ||
this.handlers = handlers; | ||
this.nativeHandlers = nativeHandlers; | ||
} | ||
Controller.prototype.clean = function () { | ||
this._targetEventStore.clean(); | ||
applyConfig(config, gestureKey) { | ||
this.config = parse(config, gestureKey); | ||
} | ||
for (const key of this._gestures) { | ||
this._gestureEventStores[key].clean(); | ||
clean() { | ||
this._targetEventStore.clean(); | ||
this._gestureTimeoutStores[key].clean(); | ||
for (const key of this.gestures) { | ||
this.gestureEventStores[key].clean(); | ||
this.gestureTimeoutStores[key].clean(); | ||
} | ||
} | ||
}; | ||
Controller.prototype.effect = function () { | ||
if (this._config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
}; | ||
effect() { | ||
if (this.config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
} | ||
Controller.prototype.bind = function (...args) { | ||
const sharedConfig = this._config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
bind(...args) { | ||
const sharedConfig = this.config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this._nativeHandlers) { | ||
bindFunction(eventKey, '', event => this._nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this.nativeHandlers) { | ||
bindFunction(eventKey, '', event => this.nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
} | ||
for (const gestureKey of this.gestures) { | ||
if (this.config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args, gestureKey).bind(bindFunction); | ||
} | ||
} | ||
} | ||
for (const gestureKey of this._gestures) { | ||
if (this._config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args).bind(bindFunction); | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
return props; | ||
} | ||
} | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
} | ||
return props; | ||
} | ||
}; | ||
function setupGesture(ctrl, gestureKey) { | ||
ctrl._gestures.add(gestureKey); | ||
ctrl._gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl._gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
ctrl.gestures.add(gestureKey); | ||
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
} | ||
@@ -654,240 +670,239 @@ | ||
const Engine = function Engine(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.key = key; | ||
this.args = args; | ||
class Engine { | ||
constructor(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.args = args; | ||
this.key = key; | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
} | ||
} | ||
}; | ||
Engine.prototype = { | ||
get state() { | ||
return this.ctrl.state[this.key]; | ||
}, | ||
} | ||
set state(state) { | ||
this.ctrl.state[this.key] = state; | ||
}, | ||
} | ||
get shared() { | ||
return this.ctrl.state.shared; | ||
}, | ||
} | ||
get eventStore() { | ||
return this.ctrl._gestureEventStores[this.key]; | ||
}, | ||
return this.ctrl.gestureEventStores[this.key]; | ||
} | ||
get timeoutStore() { | ||
return this.ctrl._gestureTimeoutStores[this.key]; | ||
}, | ||
return this.ctrl.gestureTimeoutStores[this.key]; | ||
} | ||
get config() { | ||
return this.ctrl._config[this.key]; | ||
}, | ||
return this.ctrl.config[this.key]; | ||
} | ||
get sharedConfig() { | ||
return this.ctrl._config.shared; | ||
}, | ||
return this.ctrl.config.shared; | ||
} | ||
get handler() { | ||
return this.ctrl._handlers[this.key]; | ||
return this.ctrl.handlers[this.key]; | ||
} | ||
}; | ||
reset() { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
} | ||
Engine.prototype.reset = function () { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
}; | ||
start(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
Engine.prototype.start = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
} | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
} | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
}; | ||
compute(event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
Engine.prototype.compute = function (event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl.pointerIds.size || this.ctrl.touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl._pointerIds.size || this.ctrl._touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
} | ||
} | ||
} | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
}; | ||
emit() { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
} | ||
Engine.prototype.emit = function () { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
}; | ||
clean() { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
} | ||
Engine.prototype.clean = function () { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
}; | ||
} | ||
const CoordinatesEngine = function CoordinatesEngine(ctrl, args, key) { | ||
Engine.call(this, ctrl, args, key); | ||
}; | ||
CoordinatesEngine.prototype = Object.create(Engine.prototype); | ||
class CoordinatesEngine extends Engine { | ||
reset() { | ||
super.reset(); | ||
this.state.axis = undefined; | ||
} | ||
CoordinatesEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
this.state.axis = undefined; | ||
}; | ||
init() { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
} | ||
CoordinatesEngine.prototype.init = function () { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
}; | ||
computeOffset() { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
} | ||
CoordinatesEngine.prototype.computeOffset = function () { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
}; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
} | ||
CoordinatesEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
CoordinatesEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
const axis = state.axis; | ||
const axis = state.axis; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
}; | ||
} | ||
const DEFAULT_RUBBERBAND = 0.15; | ||
@@ -1090,285 +1105,315 @@ const commonConfigResolver = { | ||
ConfigResolverMap.set('drag', dragConfigResolver); | ||
const DragEngine = function DragEngine(ctrl, args) { | ||
this.ingKey = 'dragging'; | ||
CoordinatesEngine.call(this, ctrl, args, 'drag'); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class DragEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
DragEngine.prototype.reset = function () { | ||
CoordinatesEngine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
}; | ||
_defineProperty(this, "ingKey", 'dragging'); | ||
} | ||
DragEngine.prototype.setup = function () { | ||
const state = this.state; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
} | ||
}; | ||
DragEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
setup() { | ||
const state = this.state; | ||
DragEngine.prototype.setActive = function ({ | ||
pointer, | ||
keyboard | ||
} = {}) { | ||
this.state._active = (pointer !== null && pointer !== void 0 ? pointer : this.state._pointerActive) || (keyboard !== null && keyboard !== void 0 ? keyboard : this.state._keyboardActive); | ||
}; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
DragEngine.prototype.clean = function () { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
CoordinatesEngine.prototype.clean.call(this); | ||
}; | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
} | ||
} | ||
DragEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
setActive() { | ||
this.state._active = this.state._pointerActive || this.state._keyboardActive; | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
clean() { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
super.clean(); | ||
} | ||
}; | ||
DragEngine.prototype.pointerDown = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
pointerDown(event) { | ||
this.ctrl.setEventIds(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
}; | ||
if (this.config.pointerCapture) { | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
DragEngine.prototype.startPointerDrag = function (event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
DragEngine.prototype.pointerMove = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
startPointerDrag(event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
pointerMove(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
return; | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerUp(event) { | ||
this.ctrl.setEventIds(event); | ||
DragEngine.prototype.pointerUp = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.setActive({ | ||
pointer: false | ||
}); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
try { | ||
if (this.config.pointerCapture && event.target.hasPointerCapture(event.pointerId)) { | ||
; | ||
event.target.releasePointerCapture(event.pointerId); | ||
} | ||
} catch (_unused) { | ||
if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: If you see this message, it's likely that you're using an outdated version of \`@react-three/fiber\`. \n\nPlease upgrade to the latest version.`); | ||
} | ||
} | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.state._pointerActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
} | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerClick(event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
} | ||
DragEngine.prototype.pointerClick = function (event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
}; | ||
setupPointer(event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
DragEngine.prototype.setupPointer = function (event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
if (process.env.NODE_ENV === 'development') { | ||
try { | ||
if (device === 'pointer') { | ||
const _currentTarget = this.sharedConfig.r3f ? event.sourceEvent.currentTarget : event.currentTarget; | ||
if (process.env.NODE_ENV === 'development') { | ||
try { | ||
if (device === 'pointer') { | ||
const _currentTarget = this.sharedConfig.r3f ? event.sourceEvent.currentTarget : event.currentTarget; | ||
const style = window.getComputedStyle(_currentTarget); | ||
const style = window.getComputedStyle(_currentTarget); | ||
if (style.touchAction === 'auto') { | ||
console.warn(`[@use-gesture]: The drag target has its \`touch-action\` style property set to \`auto\`. It is recommended to add \`touch-action: 'none'\` so that the drag gesture behaves correctly on touch-enabled devices. For more information read this: https://use-gesture.netlify.app/docs/extras/#touch-action.\n\nThis message will only show in development mode. It won't appear in production. If this is intended, you can ignore it.`, _currentTarget); | ||
} | ||
} | ||
} catch (_unused2) {} | ||
} | ||
if (style.touchAction === 'auto') { | ||
console.warn(`[@use-gesture]: The drag target has its \`touch-action\` style property set to \`auto\`. It is recommended to add \`touch-action: 'none'\` so that the drag gesture behaves correctly on touch-enabled devices. For more information read this: https://use-gesture.netlify.app/docs/extras/#touch-action.\n\nThis message will only show in development mode. It won't appear in production. If this is intended, you can ignore it.`, _currentTarget); | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
if (process.env.NODE_ENV === 'development') { | ||
if (event.uv) { | ||
console.warn(`[@use-gesture]: You're probably using \`use-gesture\` on with \`@react-three/fiber\` without setting the drag config option \`r3f: true\`. The gesture will now probably fail.`); | ||
} | ||
} | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
} | ||
} catch (_unused) {} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
} | ||
} | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
pointerClean() { | ||
const state = this.state; | ||
if (!state._pointerActive) return; | ||
if (config.pointerCapture) { | ||
target.setPointerCapture(event.pointerId); | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
} | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
if (process.env.NODE_ENV === 'development') { | ||
if (event.uv) { | ||
console.warn(`[@use-gesture]: You're probably using \`use-gesture\` on with \`@react-three/fiber\` without setting the drag config option \`r3f: true\`. The gesture will now probably fail.`); | ||
} | ||
} | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
preventScroll(event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
} | ||
}; | ||
DragEngine.prototype.pointerClean = function () { | ||
const state = this.state; | ||
const target = state.target; | ||
if (!state._pointerActive) return; | ||
const event = state.event; | ||
setupScrollPrevention(event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
} | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
setupDelayTrigger(event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
} | ||
try { | ||
if (this.config.pointerCapture && target.hasPointerCapture(event.pointerId)) { | ||
target.releasePointerCapture(event.pointerId); | ||
keyDown(event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} catch (_unused2) { | ||
if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: If you see this message, it's likely that you're using an outdated version of \`@react-three/fiber\`. \n\nPlease upgrade to the latest version.`); | ||
} | ||
} | ||
}; | ||
DragEngine.prototype.preventScroll = function (event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
keyUp(event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.state._keyboardActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.setupScrollPrevention = function (event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
DragEngine.prototype.setupDelayTrigger = function (event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
}; | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
} | ||
} | ||
} | ||
function persistEvent(event) { | ||
@@ -1378,34 +1423,2 @@ 'persist' in event && typeof event.persist === 'function' && event.persist(); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype.keyDown = function (event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.keyUp = function (event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.setActive({ | ||
keyboard: false | ||
}); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const pinchConfigResolver = _objectSpread2(_objectSpread2({}, commonConfigResolver), {}, { | ||
@@ -1469,254 +1482,254 @@ useTouch(_v, _k, { | ||
const SCALE_ANGLE_RATIO_INTENT_RAD = SCALE_ANGLE_RATIO_INTENT_DEG / 180 * Math.PI; | ||
const PinchEngine = function PinchEngine(ctrl, args) { | ||
this.ingKey = 'pinching'; | ||
Engine.call(this, ctrl, args, 'pinch'); | ||
}; | ||
PinchEngine.prototype = Object.create(Engine.prototype); | ||
const PINCH_WHEEL_RATIO = 60; | ||
class PinchEngine extends Engine { | ||
constructor(...args) { | ||
super(...args); | ||
PinchEngine.prototype.init = function () { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
}; | ||
_defineProperty(this, "ingKey", 'pinching'); | ||
} | ||
PinchEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
}; | ||
init() { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
} | ||
PinchEngine.prototype.computeOffset = function () { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
}; | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
} | ||
PinchEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
}; | ||
PinchEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
computeOffset() { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
} | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
} | ||
}; | ||
PinchEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
PinchEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
} | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
} | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
PinchEngine.prototype.touchStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl._touchIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
}; | ||
touchStart(event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl.touchIds; | ||
PinchEngine.prototype.pointerStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl._pointerIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
} | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
} | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
pointerStart(event) { | ||
this.ctrl.setEventIds(event); | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl.pointerIds; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
}; | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
} | ||
PinchEngine.prototype.pinchStart = function (event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchMove = function (event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
}; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
} | ||
PinchEngine.prototype.pointerMove = function (event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
pinchStart(event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
touchMove(event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
} | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
}; | ||
pointerMove(event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
PinchEngine.prototype.pinchMove = function (event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchEnd = function (event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
} | ||
if (this.state._touchIds.some(id => !this.ctrl._touchIds.has(id))) { | ||
this.state._active = false; | ||
pinchMove(event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
PinchEngine.prototype.pointerEnd = function (event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
touchEnd(event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
if (this.state._touchIds.some(id => !this.ctrl.touchIds.has(id))) { | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
pointerEnd(event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
try { | ||
event.target.releasePointerCapture(event.pointerId); | ||
} catch (_unused) {} | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
} | ||
if (!state._active) return; | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
if (!state._active) return; | ||
gestureStart(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
gestureMove(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
const PINCH_WHEEL_RATIO = 60; | ||
gestureEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheel = function (event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
wheel(event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
PinchEngine.prototype.wheelStart = function (event) { | ||
if (event.cancelable) event.preventDefault();else if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: To properly support zoom on trackpads, try using the \`target\` option and \`config.eventOptions.passive\` set to \`false\`. This message will only appear in development mode.`, event.currentTarget); | ||
wheelStart(event) { | ||
if (event.cancelable) event.preventDefault();else if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: To properly support zoom on trackpads, try using the \`target\` option and \`config.eventOptions.passive\` set to \`false\`. This message will only appear in development mode.`, event.currentTarget); | ||
} | ||
this.start(event); | ||
this.wheelChange(event); | ||
} | ||
this.start(event); | ||
this.wheelChange(event); | ||
}; | ||
PinchEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.gestureStart = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
PinchEngine.prototype.gestureMove = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
PinchEngine.prototype.gestureEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
@@ -1726,150 +1739,162 @@ const wheelConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('wheel', wheelConfigResolver); | ||
const WheelEngine = function WheelEngine(ctrl, args) { | ||
this.ingKey = 'wheeling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'wheel'); | ||
}; | ||
WheelEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class WheelEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
WheelEngine.prototype.wheel = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'wheeling'); | ||
} | ||
WheelEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheel(event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
WheelEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
WheelEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
} | ||
const scrollConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('scroll', scrollConfigResolver); | ||
const ScrollEngine = function ScrollEngine(ctrl, args) { | ||
this.ingKey = 'scrolling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'scroll'); | ||
}; | ||
ScrollEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class ScrollEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
ScrollEngine.prototype.scroll = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'scrolling'); | ||
} | ||
ScrollEngine.prototype.scrollChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
scroll(event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
} | ||
ScrollEngine.prototype.scrollEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
scrollChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
ScrollEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
}; | ||
scrollEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
} | ||
} | ||
const moveConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('move', moveConfigResolver); | ||
const MoveEngine = function MoveEngine(ctrl, args) { | ||
this.ingKey = 'moving'; | ||
CoordinatesEngine.call(this, ctrl, args, 'move'); | ||
}; | ||
MoveEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class MoveEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
MoveEngine.prototype.move = function (event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'moving'); | ||
} | ||
MoveEngine.prototype.moveStart = function (event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
}; | ||
move(event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
} | ||
MoveEngine.prototype.moveChange = function (event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveStart(event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.moveEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveChange(event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
}; | ||
moveEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
} | ||
} | ||
const hoverConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('hover', hoverConfigResolver); | ||
const HoverEngine = function HoverEngine(ctrl, args) { | ||
this.ingKey = 'hovering'; | ||
CoordinatesEngine.call(this, ctrl, args, 'hover'); | ||
}; | ||
HoverEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class HoverEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
HoverEngine.prototype.enter = function (event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
_defineProperty(this, "ingKey", 'hovering'); | ||
} | ||
HoverEngine.prototype.leave = function (event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
}; | ||
enter(event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
HoverEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
}; | ||
leave(event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
} | ||
} | ||
exports.Controller = Controller; | ||
@@ -1876,0 +1901,0 @@ exports.DragEngine = DragEngine; |
@@ -427,43 +427,50 @@ 'use strict'; | ||
const EventStore = function EventStore(ctrl) { | ||
this._ctrl = ctrl; | ||
this._listeners = []; | ||
}; | ||
class EventStore { | ||
constructor(ctrl) { | ||
_defineProperty(this, "_listeners", []); | ||
EventStore.prototype.add = function (element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl._config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
this._ctrl = ctrl; | ||
} | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
}; | ||
add(element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl.config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
EventStore.prototype.clean = function () { | ||
this._listeners.forEach(remove => remove()); | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
} | ||
this._listeners = []; | ||
}; | ||
clean() { | ||
this._listeners.forEach(remove => remove()); | ||
const TimeoutStore = function TimeoutStore() { | ||
this._timeouts = new Map(); | ||
}; | ||
this._listeners = []; | ||
} | ||
TimeoutStore.prototype.add = function (key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
} | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
}; | ||
class TimeoutStore { | ||
constructor() { | ||
_defineProperty(this, "_timeouts", new Map()); | ||
} | ||
TimeoutStore.prototype.remove = function (key) { | ||
const timeout = this._timeouts.get(key); | ||
add(key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
if (timeout) window.clearTimeout(timeout); | ||
}; | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
} | ||
TimeoutStore.prototype.clean = function () { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
remove(key) { | ||
const timeout = this._timeouts.get(key); | ||
this._timeouts.clear(); | ||
}; | ||
if (timeout) window.clearTimeout(timeout); | ||
} | ||
clean() { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
this._timeouts.clear(); | ||
} | ||
} | ||
function call(v, ...args) { | ||
@@ -494,91 +501,100 @@ if (typeof v === 'function') { | ||
const Controller = function Controller(handlers) { | ||
this._gestures = new Set(); | ||
this._targetEventStore = new EventStore(this); | ||
this._gestureEventStores = {}; | ||
this._gestureTimeoutStores = {}; | ||
this._handlers = {}; | ||
this._nativeHandlers = {}; | ||
this._config = {}; | ||
this._pointerIds = new Set(); | ||
this._touchIds = new Set(); | ||
this.state = { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}; | ||
resolveGestures(this, handlers); | ||
}; | ||
class Controller { | ||
constructor(handlers) { | ||
_defineProperty(this, "gestures", new Set()); | ||
Controller.prototype.setEventIds = function (event) { | ||
if (isTouch(event)) { | ||
this._touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this._pointerIds.delete(event.pointerId);else this._pointerIds.add(event.pointerId); | ||
_defineProperty(this, "_targetEventStore", new EventStore(this)); | ||
_defineProperty(this, "gestureEventStores", {}); | ||
_defineProperty(this, "gestureTimeoutStores", {}); | ||
_defineProperty(this, "handlers", {}); | ||
_defineProperty(this, "config", {}); | ||
_defineProperty(this, "pointerIds", new Set()); | ||
_defineProperty(this, "touchIds", new Set()); | ||
_defineProperty(this, "state", { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}); | ||
resolveGestures(this, handlers); | ||
} | ||
}; | ||
Controller.prototype.applyHandlers = function (handlers, nativeHandlers) { | ||
this._handlers = handlers; | ||
this._nativeHandlers = nativeHandlers; | ||
}; | ||
setEventIds(event) { | ||
if (isTouch(event)) { | ||
this.touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this.pointerIds.delete(event.pointerId);else this.pointerIds.add(event.pointerId); | ||
} | ||
} | ||
Controller.prototype.applyConfig = function (config, gestureKey) { | ||
this._config = parse(config, gestureKey); | ||
}; | ||
applyHandlers(handlers, nativeHandlers) { | ||
this.handlers = handlers; | ||
this.nativeHandlers = nativeHandlers; | ||
} | ||
Controller.prototype.clean = function () { | ||
this._targetEventStore.clean(); | ||
applyConfig(config, gestureKey) { | ||
this.config = parse(config, gestureKey); | ||
} | ||
for (const key of this._gestures) { | ||
this._gestureEventStores[key].clean(); | ||
clean() { | ||
this._targetEventStore.clean(); | ||
this._gestureTimeoutStores[key].clean(); | ||
for (const key of this.gestures) { | ||
this.gestureEventStores[key].clean(); | ||
this.gestureTimeoutStores[key].clean(); | ||
} | ||
} | ||
}; | ||
Controller.prototype.effect = function () { | ||
if (this._config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
}; | ||
effect() { | ||
if (this.config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
} | ||
Controller.prototype.bind = function (...args) { | ||
const sharedConfig = this._config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
bind(...args) { | ||
const sharedConfig = this.config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this._nativeHandlers) { | ||
bindFunction(eventKey, '', event => this._nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this.nativeHandlers) { | ||
bindFunction(eventKey, '', event => this.nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
} | ||
for (const gestureKey of this.gestures) { | ||
if (this.config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args, gestureKey).bind(bindFunction); | ||
} | ||
} | ||
} | ||
for (const gestureKey of this._gestures) { | ||
if (this._config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args).bind(bindFunction); | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
return props; | ||
} | ||
} | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
} | ||
return props; | ||
} | ||
}; | ||
function setupGesture(ctrl, gestureKey) { | ||
ctrl._gestures.add(gestureKey); | ||
ctrl._gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl._gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
ctrl.gestures.add(gestureKey); | ||
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
} | ||
@@ -654,240 +670,239 @@ | ||
const Engine = function Engine(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.key = key; | ||
this.args = args; | ||
class Engine { | ||
constructor(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.args = args; | ||
this.key = key; | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
} | ||
} | ||
}; | ||
Engine.prototype = { | ||
get state() { | ||
return this.ctrl.state[this.key]; | ||
}, | ||
} | ||
set state(state) { | ||
this.ctrl.state[this.key] = state; | ||
}, | ||
} | ||
get shared() { | ||
return this.ctrl.state.shared; | ||
}, | ||
} | ||
get eventStore() { | ||
return this.ctrl._gestureEventStores[this.key]; | ||
}, | ||
return this.ctrl.gestureEventStores[this.key]; | ||
} | ||
get timeoutStore() { | ||
return this.ctrl._gestureTimeoutStores[this.key]; | ||
}, | ||
return this.ctrl.gestureTimeoutStores[this.key]; | ||
} | ||
get config() { | ||
return this.ctrl._config[this.key]; | ||
}, | ||
return this.ctrl.config[this.key]; | ||
} | ||
get sharedConfig() { | ||
return this.ctrl._config.shared; | ||
}, | ||
return this.ctrl.config.shared; | ||
} | ||
get handler() { | ||
return this.ctrl._handlers[this.key]; | ||
return this.ctrl.handlers[this.key]; | ||
} | ||
}; | ||
reset() { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
} | ||
Engine.prototype.reset = function () { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
}; | ||
start(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
Engine.prototype.start = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
} | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
} | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
}; | ||
compute(event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
Engine.prototype.compute = function (event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl.pointerIds.size || this.ctrl.touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl._pointerIds.size || this.ctrl._touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
} | ||
} | ||
} | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
}; | ||
emit() { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
} | ||
Engine.prototype.emit = function () { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
}; | ||
clean() { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
} | ||
Engine.prototype.clean = function () { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
}; | ||
} | ||
const CoordinatesEngine = function CoordinatesEngine(ctrl, args, key) { | ||
Engine.call(this, ctrl, args, key); | ||
}; | ||
CoordinatesEngine.prototype = Object.create(Engine.prototype); | ||
class CoordinatesEngine extends Engine { | ||
reset() { | ||
super.reset(); | ||
this.state.axis = undefined; | ||
} | ||
CoordinatesEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
this.state.axis = undefined; | ||
}; | ||
init() { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
} | ||
CoordinatesEngine.prototype.init = function () { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
}; | ||
computeOffset() { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
} | ||
CoordinatesEngine.prototype.computeOffset = function () { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
}; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
} | ||
CoordinatesEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
CoordinatesEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
const axis = state.axis; | ||
const axis = state.axis; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
}; | ||
} | ||
const DEFAULT_RUBBERBAND = 0.15; | ||
@@ -1032,263 +1047,293 @@ const commonConfigResolver = { | ||
ConfigResolverMap.set('drag', dragConfigResolver); | ||
const DragEngine = function DragEngine(ctrl, args) { | ||
this.ingKey = 'dragging'; | ||
CoordinatesEngine.call(this, ctrl, args, 'drag'); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class DragEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
DragEngine.prototype.reset = function () { | ||
CoordinatesEngine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
}; | ||
_defineProperty(this, "ingKey", 'dragging'); | ||
} | ||
DragEngine.prototype.setup = function () { | ||
const state = this.state; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
} | ||
}; | ||
DragEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
setup() { | ||
const state = this.state; | ||
DragEngine.prototype.setActive = function ({ | ||
pointer, | ||
keyboard | ||
} = {}) { | ||
this.state._active = (pointer !== null && pointer !== void 0 ? pointer : this.state._pointerActive) || (keyboard !== null && keyboard !== void 0 ? keyboard : this.state._keyboardActive); | ||
}; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
DragEngine.prototype.clean = function () { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
CoordinatesEngine.prototype.clean.call(this); | ||
}; | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
} | ||
} | ||
DragEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
setActive() { | ||
this.state._active = this.state._pointerActive || this.state._keyboardActive; | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
clean() { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
super.clean(); | ||
} | ||
}; | ||
DragEngine.prototype.pointerDown = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
pointerDown(event) { | ||
this.ctrl.setEventIds(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
}; | ||
if (this.config.pointerCapture) { | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
DragEngine.prototype.startPointerDrag = function (event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
DragEngine.prototype.pointerMove = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
startPointerDrag(event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
pointerMove(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
return; | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerUp(event) { | ||
this.ctrl.setEventIds(event); | ||
DragEngine.prototype.pointerUp = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.setActive({ | ||
pointer: false | ||
}); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
try { | ||
if (this.config.pointerCapture && event.target.hasPointerCapture(event.pointerId)) { | ||
; | ||
event.target.releasePointerCapture(event.pointerId); | ||
} | ||
} catch (_unused) { | ||
} | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.state._pointerActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
} | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerClick(event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
} | ||
DragEngine.prototype.pointerClick = function (event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
}; | ||
setupPointer(event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
DragEngine.prototype.setupPointer = function (event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
if (config.pointerCapture) { | ||
target.setPointerCapture(event.pointerId); | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
} | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
pointerClean() { | ||
const state = this.state; | ||
if (!state._pointerActive) return; | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
preventScroll(event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
} | ||
} | ||
}; | ||
DragEngine.prototype.pointerClean = function () { | ||
const state = this.state; | ||
const target = state.target; | ||
if (!state._pointerActive) return; | ||
const event = state.event; | ||
setupScrollPrevention(event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
} | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
setupDelayTrigger(event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
} | ||
try { | ||
if (this.config.pointerCapture && target.hasPointerCapture(event.pointerId)) { | ||
target.releasePointerCapture(event.pointerId); | ||
keyDown(event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} catch (_unused2) { | ||
} | ||
}; | ||
DragEngine.prototype.preventScroll = function (event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
keyUp(event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.state._keyboardActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.setupScrollPrevention = function (event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
DragEngine.prototype.setupDelayTrigger = function (event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
}; | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
} | ||
} | ||
} | ||
function persistEvent(event) { | ||
@@ -1298,34 +1343,2 @@ 'persist' in event && typeof event.persist === 'function' && event.persist(); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype.keyDown = function (event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.keyUp = function (event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.setActive({ | ||
keyboard: false | ||
}); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const pinchConfigResolver = _objectSpread2(_objectSpread2({}, commonConfigResolver), {}, { | ||
@@ -1389,252 +1402,252 @@ useTouch(_v, _k, { | ||
const SCALE_ANGLE_RATIO_INTENT_RAD = SCALE_ANGLE_RATIO_INTENT_DEG / 180 * Math.PI; | ||
const PinchEngine = function PinchEngine(ctrl, args) { | ||
this.ingKey = 'pinching'; | ||
Engine.call(this, ctrl, args, 'pinch'); | ||
}; | ||
PinchEngine.prototype = Object.create(Engine.prototype); | ||
const PINCH_WHEEL_RATIO = 60; | ||
class PinchEngine extends Engine { | ||
constructor(...args) { | ||
super(...args); | ||
PinchEngine.prototype.init = function () { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
}; | ||
_defineProperty(this, "ingKey", 'pinching'); | ||
} | ||
PinchEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
}; | ||
init() { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
} | ||
PinchEngine.prototype.computeOffset = function () { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
}; | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
} | ||
PinchEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
}; | ||
PinchEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
computeOffset() { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
} | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
} | ||
}; | ||
PinchEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
PinchEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
} | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
} | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
PinchEngine.prototype.touchStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl._touchIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
}; | ||
touchStart(event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl.touchIds; | ||
PinchEngine.prototype.pointerStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl._pointerIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
} | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
} | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
pointerStart(event) { | ||
this.ctrl.setEventIds(event); | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl.pointerIds; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
}; | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
} | ||
PinchEngine.prototype.pinchStart = function (event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchMove = function (event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
}; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
} | ||
PinchEngine.prototype.pointerMove = function (event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
pinchStart(event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
touchMove(event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
} | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
}; | ||
pointerMove(event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
PinchEngine.prototype.pinchMove = function (event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchEnd = function (event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
} | ||
if (this.state._touchIds.some(id => !this.ctrl._touchIds.has(id))) { | ||
this.state._active = false; | ||
pinchMove(event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
PinchEngine.prototype.pointerEnd = function (event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
touchEnd(event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
if (this.state._touchIds.some(id => !this.ctrl.touchIds.has(id))) { | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
pointerEnd(event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
try { | ||
event.target.releasePointerCapture(event.pointerId); | ||
} catch (_unused) {} | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
} | ||
if (!state._active) return; | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
if (!state._active) return; | ||
gestureStart(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
gestureMove(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
const PINCH_WHEEL_RATIO = 60; | ||
gestureEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheel = function (event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
wheel(event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
PinchEngine.prototype.wheelStart = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
this.start(event); | ||
this.wheelChange(event); | ||
}; | ||
wheelStart(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
this.start(event); | ||
this.wheelChange(event); | ||
} | ||
PinchEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.gestureStart = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
PinchEngine.prototype.gestureMove = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
PinchEngine.prototype.gestureEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
@@ -1644,150 +1657,162 @@ const wheelConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('wheel', wheelConfigResolver); | ||
const WheelEngine = function WheelEngine(ctrl, args) { | ||
this.ingKey = 'wheeling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'wheel'); | ||
}; | ||
WheelEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class WheelEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
WheelEngine.prototype.wheel = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'wheeling'); | ||
} | ||
WheelEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheel(event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
WheelEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
WheelEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
} | ||
const scrollConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('scroll', scrollConfigResolver); | ||
const ScrollEngine = function ScrollEngine(ctrl, args) { | ||
this.ingKey = 'scrolling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'scroll'); | ||
}; | ||
ScrollEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class ScrollEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
ScrollEngine.prototype.scroll = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'scrolling'); | ||
} | ||
ScrollEngine.prototype.scrollChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
scroll(event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
} | ||
ScrollEngine.prototype.scrollEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
scrollChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
ScrollEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
}; | ||
scrollEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
} | ||
} | ||
const moveConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('move', moveConfigResolver); | ||
const MoveEngine = function MoveEngine(ctrl, args) { | ||
this.ingKey = 'moving'; | ||
CoordinatesEngine.call(this, ctrl, args, 'move'); | ||
}; | ||
MoveEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class MoveEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
MoveEngine.prototype.move = function (event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'moving'); | ||
} | ||
MoveEngine.prototype.moveStart = function (event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
}; | ||
move(event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
} | ||
MoveEngine.prototype.moveChange = function (event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveStart(event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.moveEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveChange(event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
}; | ||
moveEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
} | ||
} | ||
const hoverConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('hover', hoverConfigResolver); | ||
const HoverEngine = function HoverEngine(ctrl, args) { | ||
this.ingKey = 'hovering'; | ||
CoordinatesEngine.call(this, ctrl, args, 'hover'); | ||
}; | ||
HoverEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class HoverEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
HoverEngine.prototype.enter = function (event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
_defineProperty(this, "ingKey", 'hovering'); | ||
} | ||
HoverEngine.prototype.leave = function (event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
}; | ||
enter(event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
HoverEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
}; | ||
leave(event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
} | ||
} | ||
exports.Controller = Controller; | ||
@@ -1794,0 +1819,0 @@ exports.DragEngine = DragEngine; |
@@ -423,43 +423,50 @@ function _defineProperty(obj, key, value) { | ||
const EventStore = function EventStore(ctrl) { | ||
this._ctrl = ctrl; | ||
this._listeners = []; | ||
}; | ||
class EventStore { | ||
constructor(ctrl) { | ||
_defineProperty(this, "_listeners", []); | ||
EventStore.prototype.add = function (element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl._config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
this._ctrl = ctrl; | ||
} | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
}; | ||
add(element, device, action, handler, options) { | ||
const type = toDomEventType(device, action); | ||
const eventOptions = options || this._ctrl.config.shared.eventOptions; | ||
element.addEventListener(type, handler, eventOptions); | ||
EventStore.prototype.clean = function () { | ||
this._listeners.forEach(remove => remove()); | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)); | ||
} | ||
this._listeners = []; | ||
}; | ||
clean() { | ||
this._listeners.forEach(remove => remove()); | ||
const TimeoutStore = function TimeoutStore() { | ||
this._timeouts = new Map(); | ||
}; | ||
this._listeners = []; | ||
} | ||
TimeoutStore.prototype.add = function (key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
} | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
}; | ||
class TimeoutStore { | ||
constructor() { | ||
_defineProperty(this, "_timeouts", new Map()); | ||
} | ||
TimeoutStore.prototype.remove = function (key) { | ||
const timeout = this._timeouts.get(key); | ||
add(key, callback, ms = 140, ...args) { | ||
this.remove(key); | ||
if (timeout) window.clearTimeout(timeout); | ||
}; | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)); | ||
} | ||
TimeoutStore.prototype.clean = function () { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
remove(key) { | ||
const timeout = this._timeouts.get(key); | ||
this._timeouts.clear(); | ||
}; | ||
if (timeout) window.clearTimeout(timeout); | ||
} | ||
clean() { | ||
this._timeouts.forEach(timeout => void window.clearTimeout(timeout)); | ||
this._timeouts.clear(); | ||
} | ||
} | ||
function call(v, ...args) { | ||
@@ -490,91 +497,100 @@ if (typeof v === 'function') { | ||
const Controller = function Controller(handlers) { | ||
this._gestures = new Set(); | ||
this._targetEventStore = new EventStore(this); | ||
this._gestureEventStores = {}; | ||
this._gestureTimeoutStores = {}; | ||
this._handlers = {}; | ||
this._nativeHandlers = {}; | ||
this._config = {}; | ||
this._pointerIds = new Set(); | ||
this._touchIds = new Set(); | ||
this.state = { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}; | ||
resolveGestures(this, handlers); | ||
}; | ||
class Controller { | ||
constructor(handlers) { | ||
_defineProperty(this, "gestures", new Set()); | ||
Controller.prototype.setEventIds = function (event) { | ||
if (isTouch(event)) { | ||
this._touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this._pointerIds.delete(event.pointerId);else this._pointerIds.add(event.pointerId); | ||
_defineProperty(this, "_targetEventStore", new EventStore(this)); | ||
_defineProperty(this, "gestureEventStores", {}); | ||
_defineProperty(this, "gestureTimeoutStores", {}); | ||
_defineProperty(this, "handlers", {}); | ||
_defineProperty(this, "config", {}); | ||
_defineProperty(this, "pointerIds", new Set()); | ||
_defineProperty(this, "touchIds", new Set()); | ||
_defineProperty(this, "state", { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
}); | ||
resolveGestures(this, handlers); | ||
} | ||
}; | ||
Controller.prototype.applyHandlers = function (handlers, nativeHandlers) { | ||
this._handlers = handlers; | ||
this._nativeHandlers = nativeHandlers; | ||
}; | ||
setEventIds(event) { | ||
if (isTouch(event)) { | ||
this.touchIds = new Set(Touches.ids(event)); | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this.pointerIds.delete(event.pointerId);else this.pointerIds.add(event.pointerId); | ||
} | ||
} | ||
Controller.prototype.applyConfig = function (config, gestureKey) { | ||
this._config = parse(config, gestureKey); | ||
}; | ||
applyHandlers(handlers, nativeHandlers) { | ||
this.handlers = handlers; | ||
this.nativeHandlers = nativeHandlers; | ||
} | ||
Controller.prototype.clean = function () { | ||
this._targetEventStore.clean(); | ||
applyConfig(config, gestureKey) { | ||
this.config = parse(config, gestureKey); | ||
} | ||
for (const key of this._gestures) { | ||
this._gestureEventStores[key].clean(); | ||
clean() { | ||
this._targetEventStore.clean(); | ||
this._gestureTimeoutStores[key].clean(); | ||
for (const key of this.gestures) { | ||
this.gestureEventStores[key].clean(); | ||
this.gestureTimeoutStores[key].clean(); | ||
} | ||
} | ||
}; | ||
Controller.prototype.effect = function () { | ||
if (this._config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
}; | ||
effect() { | ||
if (this.config.shared.target) this.bind(); | ||
return () => this._targetEventStore.clean(); | ||
} | ||
Controller.prototype.bind = function (...args) { | ||
const sharedConfig = this._config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
bind(...args) { | ||
const sharedConfig = this.config.shared; | ||
const eventOptions = sharedConfig.eventOptions; | ||
const props = {}; | ||
const bindFunction = sharedConfig.target ? bindToEventStore(this._targetEventStore, sharedConfig.target()) : bindToProps(props, eventOptions); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this._nativeHandlers) { | ||
bindFunction(eventKey, '', event => this._nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
if (sharedConfig.enabled) { | ||
for (const eventKey in this.nativeHandlers) { | ||
bindFunction(eventKey, '', event => this.nativeHandlers[eventKey](_objectSpread2(_objectSpread2({}, this.state.shared), {}, { | ||
event, | ||
args | ||
})), undefined, true); | ||
} | ||
for (const gestureKey of this.gestures) { | ||
if (this.config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args, gestureKey).bind(bindFunction); | ||
} | ||
} | ||
} | ||
for (const gestureKey of this._gestures) { | ||
if (this._config[gestureKey].enabled) { | ||
const Engine = EngineMap.get(gestureKey); | ||
new Engine(this, args).bind(bindFunction); | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
return props; | ||
} | ||
} | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]); | ||
} | ||
} | ||
return props; | ||
} | ||
}; | ||
function setupGesture(ctrl, gestureKey) { | ||
ctrl._gestures.add(gestureKey); | ||
ctrl._gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl._gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
ctrl.gestures.add(gestureKey); | ||
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl); | ||
ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore(); | ||
} | ||
@@ -650,240 +666,239 @@ | ||
const Engine = function Engine(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.key = key; | ||
this.args = args; | ||
class Engine { | ||
constructor(ctrl, args, key) { | ||
this.ctrl = ctrl; | ||
this.args = args; | ||
this.key = key; | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
}; | ||
if (this.init) this.init(); | ||
this.reset(); | ||
} | ||
} | ||
}; | ||
Engine.prototype = { | ||
get state() { | ||
return this.ctrl.state[this.key]; | ||
}, | ||
} | ||
set state(state) { | ||
this.ctrl.state[this.key] = state; | ||
}, | ||
} | ||
get shared() { | ||
return this.ctrl.state.shared; | ||
}, | ||
} | ||
get eventStore() { | ||
return this.ctrl._gestureEventStores[this.key]; | ||
}, | ||
return this.ctrl.gestureEventStores[this.key]; | ||
} | ||
get timeoutStore() { | ||
return this.ctrl._gestureTimeoutStores[this.key]; | ||
}, | ||
return this.ctrl.gestureTimeoutStores[this.key]; | ||
} | ||
get config() { | ||
return this.ctrl._config[this.key]; | ||
}, | ||
return this.ctrl.config[this.key]; | ||
} | ||
get sharedConfig() { | ||
return this.ctrl._config.shared; | ||
}, | ||
return this.ctrl.config.shared; | ||
} | ||
get handler() { | ||
return this.ctrl._handlers[this.key]; | ||
return this.ctrl.handlers[this.key]; | ||
} | ||
}; | ||
reset() { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
} | ||
Engine.prototype.reset = function () { | ||
const { | ||
state, | ||
shared, | ||
config, | ||
ingKey | ||
} = this; | ||
const { | ||
transform, | ||
threshold = [0, 0] | ||
} = config; | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false; | ||
state._step = [false, false]; | ||
state.intentional = false; | ||
state._movement = [0, 0]; | ||
state._distance = [0, 0]; | ||
state._delta = [0, 0]; | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs); | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]]; | ||
state.axis = undefined; | ||
state.memo = undefined; | ||
state.elapsedTime = 0; | ||
state.direction = [0, 0]; | ||
state.distance = [0, 0]; | ||
state.velocity = [0, 0]; | ||
state.movement = [0, 0]; | ||
state.delta = [0, 0]; | ||
state.timeStamp = 0; | ||
}; | ||
start(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
Engine.prototype.start = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
} | ||
if (!state._active) { | ||
this.reset(); | ||
state._active = true; | ||
state.target = event.currentTarget; | ||
state.initial = state.values; | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset; | ||
state.offset = state.lastOffset; | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
} | ||
state.startTime = state.timeStamp = event.timeStamp; | ||
}; | ||
compute(event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
Engine.prototype.compute = function (event) { | ||
const { | ||
state, | ||
config, | ||
shared | ||
} = this; | ||
state.args = this.args; | ||
let dt = 0; | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl.pointerIds.size || this.ctrl.touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (event) { | ||
state.event = event; | ||
shared.touches = this.ctrl._pointerIds.size || this.ctrl._touchIds.size; | ||
shared.locked = !!document.pointerLockElement; | ||
Object.assign(shared, getEventDetails(event)); | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0; | ||
dt = event.timeStamp - state.timeStamp; | ||
state.timeStamp = event.timeStamp; | ||
state.elapsedTime = state.timeStamp - state.startTime; | ||
} | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs); | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
V.addTo(state._distance, _absoluteDelta); | ||
} | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
const [_m0, _m1] = config.transform(state._movement); | ||
const [_t0, _t1] = state._threshold; | ||
let [_s0, _s1] = state._step; | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0; | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1; | ||
state.intentional = _s0 !== false || _s1 !== false; | ||
if (!state.intentional) return; | ||
state._step = [_s0, _s1]; | ||
const movement = [0, 0]; | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0; | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0; | ||
if (this.intent) this.intent(movement); | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (state._active && !state._blocked || state.active) { | ||
state.first = state._active && !state.active; | ||
state.last = !state._active && state.active; | ||
state.active = shared[this.ingKey] = state._active; | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state); | ||
if (this.setup) this.setup(); | ||
} | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
const previousMovement = state.movement; | ||
state.movement = movement; | ||
this.computeOffset(); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement); | ||
const absoluteDelta = state.delta.map(Math.abs); | ||
V.addTo(state.distance, absoluteDelta); | ||
state.direction = state.delta.map(Math.sign); | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
if (!state.first && dt > 0) { | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]; | ||
} | ||
} | ||
} | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
} | ||
const rubberband = state._active ? config.rubberband || [0, 0] : [0, 0]; | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband); | ||
this.computeMovement(); | ||
}; | ||
emit() { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
} | ||
Engine.prototype.emit = function () { | ||
const state = this.state; | ||
const shared = this.shared; | ||
const config = this.config; | ||
if (!state._active) this.clean(); | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return; | ||
const memo = this.handler(_objectSpread2(_objectSpread2({}, shared), state)); | ||
if (memo !== undefined) state.memo = memo; | ||
}; | ||
clean() { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
} | ||
Engine.prototype.clean = function () { | ||
this.eventStore.clean(); | ||
this.timeoutStore.clean(); | ||
}; | ||
} | ||
const CoordinatesEngine = function CoordinatesEngine(ctrl, args, key) { | ||
Engine.call(this, ctrl, args, key); | ||
}; | ||
CoordinatesEngine.prototype = Object.create(Engine.prototype); | ||
class CoordinatesEngine extends Engine { | ||
reset() { | ||
super.reset(); | ||
this.state.axis = undefined; | ||
} | ||
CoordinatesEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
this.state.axis = undefined; | ||
}; | ||
init() { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
} | ||
CoordinatesEngine.prototype.init = function () { | ||
this.state.offset = [0, 0]; | ||
this.state.lastOffset = [0, 0]; | ||
}; | ||
computeOffset() { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
} | ||
CoordinatesEngine.prototype.computeOffset = function () { | ||
const state = this.state; | ||
state.offset = V.add(state.lastOffset, state.movement); | ||
}; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
} | ||
CoordinatesEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = V.sub(offset, lastOffset); | ||
this.state.xy = this.state.values; | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
CoordinatesEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'y';else if (axisMovementDifference > 0) state.axis = 'x'; | ||
} | ||
const axis = state.axis; | ||
const axis = state.axis; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = false; | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false; | ||
if (axis === 'x') v[1] = 0;else if (axis === 'y') v[0] = 0; | ||
} else { | ||
state._blocked = true; | ||
} | ||
} | ||
}; | ||
} | ||
const DEFAULT_RUBBERBAND = 0.15; | ||
@@ -1086,285 +1101,315 @@ const commonConfigResolver = { | ||
ConfigResolverMap.set('drag', dragConfigResolver); | ||
const DragEngine = function DragEngine(ctrl, args) { | ||
this.ingKey = 'dragging'; | ||
CoordinatesEngine.call(this, ctrl, args, 'drag'); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class DragEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
DragEngine.prototype.reset = function () { | ||
CoordinatesEngine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
}; | ||
_defineProperty(this, "ingKey", 'dragging'); | ||
} | ||
DragEngine.prototype.setup = function () { | ||
const state = this.state; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._pointerId = undefined; | ||
state._pointerActive = false; | ||
state._keyboardActive = false; | ||
state._preventScroll = false; | ||
state._delayed = false; | ||
state.swipe = [0, 0]; | ||
state.tap = false; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
} | ||
}; | ||
DragEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
setup() { | ||
const state = this.state; | ||
DragEngine.prototype.setActive = function ({ | ||
pointer, | ||
keyboard | ||
} = {}) { | ||
this.state._active = (pointer !== null && pointer !== void 0 ? pointer : this.state._pointerActive) || (keyboard !== null && keyboard !== void 0 ? keyboard : this.state._keyboardActive); | ||
}; | ||
if (state._bounds instanceof HTMLElement) { | ||
const boundRect = state._bounds.getBoundingClientRect(); | ||
DragEngine.prototype.clean = function () { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
CoordinatesEngine.prototype.clean.call(this); | ||
}; | ||
const targetRect = state.target.getBoundingClientRect(); | ||
const _bounds = { | ||
left: boundRect.left - targetRect.left + state.offset[0], | ||
right: boundRect.right - targetRect.right + state.offset[0], | ||
top: boundRect.top - targetRect.top + state.offset[1], | ||
bottom: boundRect.bottom - targetRect.bottom + state.offset[1] | ||
}; | ||
state._bounds = coordinatesConfigResolver.bounds(_bounds); | ||
} | ||
} | ||
DragEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
setActive() { | ||
this.state._active = this.state._pointerActive || this.state._keyboardActive; | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
clean() { | ||
this.pointerClean(); | ||
this.state._pointerActive = false; | ||
this.state._keyboardActive = false; | ||
super.clean(); | ||
} | ||
}; | ||
DragEngine.prototype.pointerDown = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
pointerDown(event) { | ||
this.ctrl.setEventIds(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
}; | ||
if (this.config.pointerCapture) { | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
DragEngine.prototype.startPointerDrag = function (event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const state = this.state; | ||
const config = this.config; | ||
if (state._pointerActive) return; | ||
this.start(event); | ||
this.setupPointer(event); | ||
state._pointerId = Pointer.id(event); | ||
state._pointerActive = true; | ||
state.values = Pointer.values(event); | ||
state.initial = state.values; | ||
DragEngine.prototype.pointerMove = function (event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (config.preventScroll) { | ||
this.setupScrollPrevention(event); | ||
} else if (config.delay > 0) { | ||
this.setupDelayTrigger(event); | ||
} else { | ||
this.startPointerDrag(event); | ||
} | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
startPointerDrag(event) { | ||
const state = this.state; | ||
state._active = true; | ||
state._preventScroll = true; | ||
state._delayed = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
pointerMove(event) { | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
const values = Pointer.values(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (document.pointerLockElement === event.target) { | ||
state._delta = [event.movementX, event.movementY]; | ||
} else { | ||
state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
if (state._delayed) { | ||
this.timeoutStore.remove('dragDelay'); | ||
this.startPointerDrag(event); | ||
return; | ||
} | ||
if (config.preventScroll && !state._preventScroll) { | ||
if (state.axis) { | ||
if (state.axis === 'x') { | ||
this.timeoutStore.remove('startPointerDrag'); | ||
this.startPointerDrag(event); | ||
return; | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
state._active = false; | ||
this.clean(); | ||
return; | ||
} | ||
} else { | ||
return; | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerUp(event) { | ||
this.ctrl.setEventIds(event); | ||
DragEngine.prototype.pointerUp = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.setActive({ | ||
pointer: false | ||
}); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
try { | ||
if (this.config.pointerCapture && event.target.hasPointerCapture(event.pointerId)) { | ||
; | ||
event.target.releasePointerCapture(event.pointerId); | ||
} | ||
} catch (_unused) { | ||
if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: If you see this message, it's likely that you're using an outdated version of \`@react-three/fiber\`. \n\nPlease upgrade to the latest version.`); | ||
} | ||
} | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
const state = this.state; | ||
const config = this.config; | ||
if (!state._pointerActive) return; | ||
const id = Pointer.id(event); | ||
if (state._pointerId && id !== state._pointerId) return; | ||
this.state._pointerActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
const [dx, dy] = state._distance; | ||
state.tap = dx <= 3 && dy <= 3; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
if (state.tap && config.filterTaps) { | ||
state._force = true; | ||
} else { | ||
const [dirx, diry] = state.direction; | ||
const [vx, vy] = state.velocity; | ||
const [mx, my] = state.movement; | ||
const [svx, svy] = config.swipe.velocity; | ||
const [sx, sy] = config.swipe.distance; | ||
const sdt = config.swipe.duration; | ||
if (state.elapsedTime < sdt) { | ||
if (Math.abs(vx) > svx && Math.abs(mx) > sx) state.swipe[0] = dirx; | ||
if (Math.abs(vy) > svy && Math.abs(my) > sy) state.swipe[1] = diry; | ||
} | ||
} | ||
this.emit(); | ||
} | ||
this.emit(); | ||
}; | ||
pointerClick(event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
} | ||
DragEngine.prototype.pointerClick = function (event) { | ||
if (!this.state.tap) event.stopPropagation(); | ||
}; | ||
setupPointer(event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
DragEngine.prototype.setupPointer = function (event) { | ||
const config = this.config; | ||
let device = config.device; | ||
const target = event.target; | ||
const currentTarget = event.currentTarget; | ||
if (process.env.NODE_ENV === 'development') { | ||
try { | ||
if (device === 'pointer') { | ||
const _currentTarget = this.sharedConfig.r3f ? event.sourceEvent.currentTarget : event.currentTarget; | ||
if (process.env.NODE_ENV === 'development') { | ||
try { | ||
if (device === 'pointer') { | ||
const _currentTarget = this.sharedConfig.r3f ? event.sourceEvent.currentTarget : event.currentTarget; | ||
const style = window.getComputedStyle(_currentTarget); | ||
const style = window.getComputedStyle(_currentTarget); | ||
if (style.touchAction === 'auto') { | ||
console.warn(`[@use-gesture]: The drag target has its \`touch-action\` style property set to \`auto\`. It is recommended to add \`touch-action: 'none'\` so that the drag gesture behaves correctly on touch-enabled devices. For more information read this: https://use-gesture.netlify.app/docs/extras/#touch-action.\n\nThis message will only show in development mode. It won't appear in production. If this is intended, you can ignore it.`, _currentTarget); | ||
} | ||
} | ||
} catch (_unused2) {} | ||
} | ||
if (style.touchAction === 'auto') { | ||
console.warn(`[@use-gesture]: The drag target has its \`touch-action\` style property set to \`auto\`. It is recommended to add \`touch-action: 'none'\` so that the drag gesture behaves correctly on touch-enabled devices. For more information read this: https://use-gesture.netlify.app/docs/extras/#touch-action.\n\nThis message will only show in development mode. It won't appear in production. If this is intended, you can ignore it.`, _currentTarget); | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
if (process.env.NODE_ENV === 'development') { | ||
if (event.uv) { | ||
console.warn(`[@use-gesture]: You're probably using \`use-gesture\` on with \`@react-three/fiber\` without setting the drag config option \`r3f: true\`. The gesture will now probably fail.`); | ||
} | ||
} | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
} | ||
} catch (_unused) {} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
} | ||
} | ||
if (config.pointerLock) { | ||
currentTarget.requestPointerLock(); | ||
} | ||
pointerClean() { | ||
const state = this.state; | ||
if (!state._pointerActive) return; | ||
if (config.pointerCapture) { | ||
target.setPointerCapture(event.pointerId); | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
} | ||
} | ||
if (device === 'touch' || config.pointerCapture) { | ||
if (!this.sharedConfig.r3f) { | ||
if (process.env.NODE_ENV === 'development') { | ||
if (event.uv) { | ||
console.warn(`[@use-gesture]: You're probably using \`use-gesture\` on with \`@react-three/fiber\` without setting the drag config option \`r3f: true\`. The gesture will now probably fail.`); | ||
} | ||
} | ||
if (document.pointerLockElement === target) device = 'mouse'; | ||
this.eventStore.add(target, device, 'change', this.pointerMove.bind(this)); | ||
preventScroll(event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
} | ||
} else { | ||
if (!this.sharedConfig.r3f) { | ||
this.eventStore.add(this.sharedConfig.window, device, 'change', this.pointerMove.bind(this)); | ||
this.eventStore.add(this.sharedConfig.window, device, 'end', this.pointerUp.bind(this)); | ||
} | ||
} | ||
}; | ||
DragEngine.prototype.pointerClean = function () { | ||
const state = this.state; | ||
const target = state.target; | ||
if (!state._pointerActive) return; | ||
const event = state.event; | ||
setupScrollPrevention(event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
} | ||
if (this.config.pointerLock && document.pointerLockElement === state.target) { | ||
document.exitPointerLock(); | ||
setupDelayTrigger(event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
} | ||
try { | ||
if (this.config.pointerCapture && target.hasPointerCapture(event.pointerId)) { | ||
target.releasePointerCapture(event.pointerId); | ||
keyDown(event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} catch (_unused2) { | ||
if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: If you see this message, it's likely that you're using an outdated version of \`@react-three/fiber\`. \n\nPlease upgrade to the latest version.`); | ||
} | ||
} | ||
}; | ||
DragEngine.prototype.preventScroll = function (event) { | ||
if (this.state._preventScroll && event.cancelable) { | ||
event.preventDefault(); | ||
keyUp(event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.state._keyboardActive = false; | ||
this.setActive(); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.setupScrollPrevention = function (event) { | ||
persistEvent(event); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'change', this.preventScroll.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'end', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.eventStore.add(this.sharedConfig.window, 'touch', 'cancel', this.clean.bind(this), { | ||
passive: false | ||
}); | ||
this.timeoutStore.add('startPointerDrag', this.startPointerDrag.bind(this), 250, event); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
bindFunction(device, 'start', this.pointerDown.bind(this)); | ||
bindFunction(device, 'end', this.pointerUp.bind(this)); | ||
bindFunction('key', 'down', this.keyDown.bind(this)); | ||
bindFunction('key', 'up', this.keyUp.bind(this)); | ||
DragEngine.prototype.setupDelayTrigger = function (event) { | ||
this.state._delayed = true; | ||
this.timeoutStore.add('dragDelay', this.startPointerDrag.bind(this), this.config.delay, event); | ||
}; | ||
if (this.sharedConfig.r3f) { | ||
bindFunction(device, 'change', this.pointerMove.bind(this)); | ||
} | ||
if (this.config.filterTaps) { | ||
bindFunction('click', '', this.pointerClick.bind(this), { | ||
capture: true | ||
}); | ||
} | ||
} | ||
} | ||
function persistEvent(event) { | ||
@@ -1374,34 +1419,2 @@ 'persist' in event && typeof event.persist === 'function' && event.persist(); | ||
const DISPLACEMENT = 10; | ||
const KEYS_DELTA_MAP = { | ||
ArrowRight: (factor = 1) => [DISPLACEMENT * factor, 0], | ||
ArrowLeft: (factor = 1) => [-DISPLACEMENT * factor, 0], | ||
ArrowUp: (factor = 1) => [0, -DISPLACEMENT * factor], | ||
ArrowDown: (factor = 1) => [0, DISPLACEMENT * factor] | ||
}; | ||
DragEngine.prototype.keyDown = function (event) { | ||
const deltaFn = KEYS_DELTA_MAP[event.key]; | ||
const state = this.state; | ||
if (deltaFn) { | ||
const factor = event.shiftKey ? 10 : event.altKey ? 0.1 : 1; | ||
state._delta = deltaFn(factor); | ||
this.start(event); | ||
state._keyboardActive = true; | ||
V.addTo(state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
DragEngine.prototype.keyUp = function (event) { | ||
if (!(event.key in KEYS_DELTA_MAP)) return; | ||
this.setActive({ | ||
keyboard: false | ||
}); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
const pinchConfigResolver = _objectSpread2(_objectSpread2({}, commonConfigResolver), {}, { | ||
@@ -1465,254 +1478,254 @@ useTouch(_v, _k, { | ||
const SCALE_ANGLE_RATIO_INTENT_RAD = SCALE_ANGLE_RATIO_INTENT_DEG / 180 * Math.PI; | ||
const PinchEngine = function PinchEngine(ctrl, args) { | ||
this.ingKey = 'pinching'; | ||
Engine.call(this, ctrl, args, 'pinch'); | ||
}; | ||
PinchEngine.prototype = Object.create(Engine.prototype); | ||
const PINCH_WHEEL_RATIO = 60; | ||
class PinchEngine extends Engine { | ||
constructor(...args) { | ||
super(...args); | ||
PinchEngine.prototype.init = function () { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
}; | ||
_defineProperty(this, "ingKey", 'pinching'); | ||
} | ||
PinchEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
}; | ||
init() { | ||
this.state.offset = [1, 0]; | ||
this.state.lastOffset = [1, 0]; | ||
this.state._pointerEvents = new Map(); | ||
} | ||
PinchEngine.prototype.computeOffset = function () { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
}; | ||
reset() { | ||
super.reset(); | ||
const state = this.state; | ||
state._touchIds = []; | ||
state.canceled = false; | ||
state.cancel = this.cancel.bind(this); | ||
state.turns = 0; | ||
} | ||
PinchEngine.prototype.computeMovement = function () { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
}; | ||
PinchEngine.prototype.intent = function (v) { | ||
const state = this.state; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
computeOffset() { | ||
const { | ||
movement, | ||
lastOffset | ||
} = this.state; | ||
this.state.offset = [(1 + movement[0]) * lastOffset[0], movement[1] + lastOffset[1]]; | ||
} | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
computeMovement() { | ||
const { | ||
offset, | ||
lastOffset | ||
} = this.state; | ||
this.state.movement = [offset[0] / lastOffset[0] - 1, offset[1] - lastOffset[1]]; | ||
this.state.da = this.state.values; | ||
} | ||
}; | ||
PinchEngine.prototype.cancel = function () { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
}; | ||
intent(v) { | ||
const state = this.state; | ||
PinchEngine.prototype.bind = function (bindFunction) { | ||
const device = this.config.device; | ||
if (!state.axis) { | ||
const angleScaleRatio = this.config.useRad ? SCALE_ANGLE_RATIO_INTENT_RAD : SCALE_ANGLE_RATIO_INTENT_DEG; | ||
const axisMovementDifference = Math.abs(v[0]) * angleScaleRatio - Math.abs(v[1]); | ||
if (axisMovementDifference < 0) state.axis = 'angle';else if (axisMovementDifference > 0) state.axis = 'scale'; | ||
} | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
if (this.config.lockDirection) { | ||
if (state.axis === 'scale') v[1] = 0;else if (state.axis === 'angle') v[0] = 0; | ||
} | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
PinchEngine.prototype.touchStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl._touchIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
cancel() { | ||
const state = this.state; | ||
if (state.canceled) return; | ||
setTimeout(() => { | ||
state.canceled = true; | ||
state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}, 0); | ||
} | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
}; | ||
touchStart(event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const ctrlTouchIds = this.ctrl.touchIds; | ||
PinchEngine.prototype.pointerStart = function (event) { | ||
this.ctrl.setEventIds(event); | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl._pointerIds; | ||
if (state._active) { | ||
if (state._touchIds.every(id => ctrlTouchIds.has(id))) return; | ||
} | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
if (ctrlTouchIds.size < 2) return; | ||
this.start(event); | ||
state._touchIds = Array.from(ctrlTouchIds).slice(0, 2); | ||
const payload = Touches.distanceAngle(event, state._touchIds); | ||
this.pinchStart(event, payload); | ||
} | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
pointerStart(event) { | ||
this.ctrl.setEventIds(event); | ||
event.target.setPointerCapture(event.pointerId); | ||
} | ||
const state = this.state; | ||
const _pointerEvents = state._pointerEvents; | ||
const ctrlPointerIds = this.ctrl.pointerIds; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
}; | ||
if (state._active) { | ||
if (Array.from(_pointerEvents.keys()).every(id => ctrlPointerIds.has(id))) return; | ||
} | ||
PinchEngine.prototype.pinchStart = function (event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.size < 2) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchMove = function (event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
}; | ||
if (state._pointerEvents.size < 2) return; | ||
this.start(event); | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchStart(event, payload); | ||
} | ||
PinchEngine.prototype.pointerMove = function (event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
pinchStart(event, payload) { | ||
const state = this.state; | ||
state.origin = payload.origin; | ||
state.values = [payload.distance, payload.angle]; | ||
state.initial = state.values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
touchMove(event) { | ||
if (!this.state._active) return; | ||
const payload = Touches.distanceAngle(event, this.state._touchIds); | ||
this.pinchMove(event, payload); | ||
} | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
}; | ||
pointerMove(event) { | ||
const _pointerEvents = this.state._pointerEvents; | ||
PinchEngine.prototype.pinchMove = function (event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (_pointerEvents.has(event.pointerId)) { | ||
_pointerEvents.set(event.pointerId, event); | ||
} | ||
PinchEngine.prototype.touchEnd = function (event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (!this.state._active) return; | ||
const payload = distanceAngle(...Array.from(_pointerEvents.values())); | ||
this.pinchMove(event, payload); | ||
} | ||
if (this.state._touchIds.some(id => !this.ctrl._touchIds.has(id))) { | ||
this.state._active = false; | ||
pinchMove(event, payload) { | ||
const state = this.state; | ||
const prev_a = state.values[1]; | ||
const delta_a = payload.angle - prev_a; | ||
let delta_turns = 0; | ||
if (Math.abs(delta_a) > 270) delta_turns += Math.sign(delta_a); | ||
state.values = [payload.distance, payload.angle - 360 * delta_turns]; | ||
state.origin = payload.origin; | ||
state.turns = delta_turns; | ||
state._movement = [state.values[0] / state.initial[0] - 1, convertAngle(this, state.values[1] - state.initial[1])]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
PinchEngine.prototype.pointerEnd = function (event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
touchEnd(event) { | ||
this.ctrl.setEventIds(event); | ||
if (!this.state._active) return; | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
if (this.state._touchIds.some(id => !this.ctrl.touchIds.has(id))) { | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
pointerEnd(event) { | ||
const state = this.state; | ||
this.ctrl.setEventIds(event); | ||
try { | ||
event.target.releasePointerCapture(event.pointerId); | ||
} catch (_unused) {} | ||
if (state._pointerEvents.has(event.pointerId)) { | ||
state._pointerEvents.delete(event.pointerId); | ||
} | ||
if (!state._active) return; | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
} | ||
if (!state._active) return; | ||
gestureStart(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
if (state._pointerEvents.size < 2) { | ||
state._active = false; | ||
gestureMove(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
}; | ||
const PINCH_WHEEL_RATIO = 60; | ||
gestureEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheel = function (event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
wheel(event) { | ||
if (!event.ctrlKey) return; | ||
if (!this.state._active) this.wheelStart(event);else this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
PinchEngine.prototype.wheelStart = function (event) { | ||
if (event.cancelable) event.preventDefault();else if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: To properly support zoom on trackpads, try using the \`target\` option and \`config.eventOptions.passive\` set to \`false\`. This message will only appear in development mode.`, event.currentTarget); | ||
wheelStart(event) { | ||
if (event.cancelable) event.preventDefault();else if (process.env.NODE_ENV === 'development') { | ||
console.warn(`[@use-gesture]: To properly support zoom on trackpads, try using the \`target\` option and \`config.eventOptions.passive\` set to \`false\`. This message will only appear in development mode.`, event.currentTarget); | ||
} | ||
this.start(event); | ||
this.wheelChange(event); | ||
} | ||
this.start(event); | ||
this.wheelChange(event); | ||
}; | ||
PinchEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = [-Wheel.values(event)[1] / PINCH_WHEEL_RATIO, 0]; | ||
V.addTo(state._movement, state._delta); | ||
this.state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
PinchEngine.prototype.gestureStart = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
if (state._active) return; | ||
this.start(event); | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
bind(bindFunction) { | ||
const device = this.config.device; | ||
PinchEngine.prototype.gestureMove = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
if (!this.state._active) return; | ||
const state = this.state; | ||
state.values = [event.scale, event.rotation]; | ||
state.origin = [event.clientX, event.clientY]; | ||
const _previousMovement = state._movement; | ||
state._movement = [event.scale - 1, convertAngle(this, event.rotation)]; | ||
state._delta = V.sub(state._movement, _previousMovement); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
if (!!device) { | ||
bindFunction(device, 'start', this[device + 'Start'].bind(this)); | ||
bindFunction(device, 'change', this[device + 'Move'].bind(this)); | ||
bindFunction(device, 'end', this[device + 'End'].bind(this)); | ||
} else bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
PinchEngine.prototype.gestureEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
} | ||
function convertAngle(engine, value) { | ||
if (engine.config.useRad) return value / 180 * Math.PI; | ||
return value; | ||
} | ||
@@ -1722,150 +1735,162 @@ const wheelConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('wheel', wheelConfigResolver); | ||
const WheelEngine = function WheelEngine(ctrl, args) { | ||
this.ingKey = 'wheeling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'wheel'); | ||
}; | ||
WheelEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class WheelEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
WheelEngine.prototype.wheel = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'wheeling'); | ||
} | ||
WheelEngine.prototype.wheelChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
wheel(event) { | ||
if (!this.state._active) this.start(event); | ||
this.wheelChange(event); | ||
this.timeoutStore.add('wheelEnd', this.wheelEnd.bind(this)); | ||
} | ||
WheelEngine.prototype.wheelEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
wheelChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
state._delta = Wheel.values(event); | ||
V.addTo(this.state._movement, state._delta); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
WheelEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
}; | ||
wheelEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('wheel', '', this.wheel.bind(this)); | ||
} | ||
} | ||
const scrollConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('scroll', scrollConfigResolver); | ||
const ScrollEngine = function ScrollEngine(ctrl, args) { | ||
this.ingKey = 'scrolling'; | ||
CoordinatesEngine.call(this, ctrl, args, 'scroll'); | ||
}; | ||
ScrollEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class ScrollEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
ScrollEngine.prototype.scroll = function (event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'scrolling'); | ||
} | ||
ScrollEngine.prototype.scrollChange = function (event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
scroll(event) { | ||
if (!this.state._active) this.start(event); | ||
this.scrollChange(event); | ||
this.timeoutStore.add('scrollEnd', this.scrollEnd.bind(this)); | ||
} | ||
ScrollEngine.prototype.scrollEnd = function () { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
}; | ||
scrollChange(event) { | ||
if (event.cancelable) event.preventDefault(); | ||
const state = this.state; | ||
const values = Scroll.values(event); | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
ScrollEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
}; | ||
scrollEnd() { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('scroll', '', this.scroll.bind(this)); | ||
} | ||
} | ||
const moveConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('move', moveConfigResolver); | ||
const MoveEngine = function MoveEngine(ctrl, args) { | ||
this.ingKey = 'moving'; | ||
CoordinatesEngine.call(this, ctrl, args, 'move'); | ||
}; | ||
MoveEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class MoveEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
MoveEngine.prototype.move = function (event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
}; | ||
_defineProperty(this, "ingKey", 'moving'); | ||
} | ||
MoveEngine.prototype.moveStart = function (event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
}; | ||
move(event) { | ||
if (!this.state._active) this.moveStart(event);else this.moveChange(event); | ||
this.timeoutStore.add('moveEnd', this.moveEnd.bind(this)); | ||
} | ||
MoveEngine.prototype.moveChange = function (event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveStart(event) { | ||
this.start(event); | ||
const state = this.state; | ||
state.values = Pointer.values(event); | ||
this.compute(event); | ||
state.initial = state.values; | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.moveEnd = function (event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
moveChange(event) { | ||
if (!this.state._active) return; | ||
const values = Pointer.values(event); | ||
const state = this.state; | ||
state._delta = V.sub(values, state.values); | ||
V.addTo(state._movement, state._delta); | ||
state.values = values; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
MoveEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
}; | ||
moveEnd(event) { | ||
if (!this.state._active) return; | ||
this.state._active = false; | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'change', this.move.bind(this)); | ||
bindFunction('mouse', 'leave', this.moveEnd.bind(this)); | ||
} | ||
} | ||
const hoverConfigResolver = coordinatesConfigResolver; | ||
ConfigResolverMap.set('hover', hoverConfigResolver); | ||
const HoverEngine = function HoverEngine(ctrl, args) { | ||
this.ingKey = 'hovering'; | ||
CoordinatesEngine.call(this, ctrl, args, 'hover'); | ||
}; | ||
HoverEngine.prototype = Object.create(CoordinatesEngine.prototype); | ||
class HoverEngine extends CoordinatesEngine { | ||
constructor(...args) { | ||
super(...args); | ||
HoverEngine.prototype.enter = function (event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
}; | ||
_defineProperty(this, "ingKey", 'hovering'); | ||
} | ||
HoverEngine.prototype.leave = function (event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
}; | ||
enter(event) { | ||
this.start(event); | ||
this.state.values = Pointer.values(event); | ||
this.compute(event); | ||
this.emit(); | ||
} | ||
HoverEngine.prototype.bind = function (bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
}; | ||
leave(event) { | ||
const state = this.state; | ||
if (!state._active) return; | ||
state._active = false; | ||
const values = Pointer.values(event); | ||
state._movement = state._delta = V.sub(values, state.values); | ||
state.values = values; | ||
this.compute(event); | ||
state.delta = state.movement; | ||
this.emit(); | ||
} | ||
bind(bindFunction) { | ||
bindFunction('mouse', 'enter', this.enter.bind(this)); | ||
bindFunction('mouse', 'leave', this.leave.bind(this)); | ||
} | ||
} | ||
export { Controller, DragEngine, HoverEngine, MoveEngine, PinchEngine, ScrollEngine, WheelEngine, parseMergedHandlers, registerEngine }; |
{ | ||
"name": "@use-gesture/core", | ||
"version": "10.0.0-beta.1", | ||
"version": "10.0.0-beta.2", | ||
"description": "Core engine for receiving gestures", | ||
@@ -8,2 +8,3 @@ "license": "MIT", | ||
"module": "dist/use-gesture-core.esm.js", | ||
"sideEffects": false, | ||
"repository": { | ||
@@ -10,0 +11,0 @@ "type": "git", |
@@ -9,52 +9,45 @@ import { EngineMap } from './imports' | ||
interface ControllerConstructor { | ||
new (handlers: InternalHandlers): Controller | ||
} | ||
export interface Controller { | ||
export class Controller { | ||
/** | ||
* The list of gestures handled by the Controller. | ||
*/ | ||
_gestures: Set<GestureKey> | ||
public gestures = new Set<GestureKey>() | ||
/** | ||
* The event store that keeps track of the config.target listeners. | ||
*/ | ||
_targetEventStore: EventStore | ||
private _targetEventStore = new EventStore(this) | ||
/** | ||
* Object that keeps track of all gesture event listeners. | ||
*/ | ||
_gestureEventStores: { [key in GestureKey]?: EventStore } | ||
public gestureEventStores: { [key in GestureKey]?: EventStore } = {} | ||
public gestureTimeoutStores: { [key in GestureKey]?: TimeoutStore } = {} | ||
public handlers: InternalHandlers = {} | ||
private nativeHandlers?: NativeHandlers | ||
public config = {} as InternalConfig | ||
public pointerIds = new Set<number>() | ||
public touchIds = new Set<number>() | ||
public state = { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
} as State | ||
constructor(handlers: InternalHandlers) { | ||
resolveGestures(this, handlers) | ||
} | ||
/** | ||
* Object that keeps track of all gesture timeouts. | ||
*/ | ||
_gestureTimeoutStores: { [key in GestureKey]?: TimeoutStore } | ||
/** | ||
* Gesture handlers. | ||
*/ | ||
_handlers: InternalHandlers | ||
/** | ||
* Native event handlers. | ||
*/ | ||
_nativeHandlers?: NativeHandlers | ||
/** | ||
* Computed configuration. | ||
*/ | ||
_config: InternalConfig | ||
/** | ||
* Pointer ids active on the target. | ||
*/ | ||
_pointerIds: Set<number> | ||
/** | ||
* Touch identifiers active on the target. | ||
*/ | ||
_touchIds: Set<number> | ||
/** | ||
* The Controller state reflecting the state of all gestures. | ||
*/ | ||
state: State | ||
/** | ||
* Sets pointer or touch ids based on the event. | ||
* @param event | ||
*/ | ||
setEventIds(this: Controller, event: TouchEvent | PointerEvent): void | ||
setEventIds(event: TouchEvent | PointerEvent) { | ||
if (isTouch(event)) { | ||
this.touchIds = new Set(Touches.ids(event as TouchEvent)) | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this.pointerIds.delete(event.pointerId) | ||
else this.pointerIds.add(event.pointerId) | ||
} | ||
} | ||
/** | ||
@@ -65,3 +58,6 @@ * Attaches handlers to the controller. | ||
*/ | ||
applyHandlers(this: Controller, handlers: InternalHandlers, nativeHandlers?: NativeHandlers): void | ||
applyHandlers(handlers: InternalHandlers, nativeHandlers?: NativeHandlers) { | ||
this.handlers = handlers | ||
this.nativeHandlers = nativeHandlers | ||
} | ||
/** | ||
@@ -72,3 +68,5 @@ * Compute and attaches a config to the controller. | ||
*/ | ||
applyConfig(this: Controller, config: UserGestureConfig, gestureKey?: GestureKey): void | ||
applyConfig(config: UserGestureConfig, gestureKey?: GestureKey) { | ||
this.config = parse(config, gestureKey) | ||
} | ||
/** | ||
@@ -78,3 +76,9 @@ * Cleans all side effects (listeners, timeouts). When the gesture is | ||
*/ | ||
clean(this: Controller): void | ||
clean() { | ||
this._targetEventStore.clean() | ||
for (const key of this.gestures) { | ||
this.gestureEventStores[key]!.clean() | ||
this.gestureTimeoutStores[key]!.clean() | ||
} | ||
} | ||
/** | ||
@@ -84,3 +88,6 @@ * Executes side effects (attaching listeneds to a `config.target`). Ran on | ||
*/ | ||
effect(this: Controller): void | ||
effect() { | ||
if (this.config.shared.target) this.bind() | ||
return () => this._targetEventStore.clean() | ||
} | ||
/** | ||
@@ -91,104 +98,49 @@ * The bind function that can be returned by the gesture handler (a hook in | ||
*/ | ||
bind(this: Controller, ...args: any[]): NativeHandlers | void | ||
} | ||
bind(...args: any[]) { | ||
const sharedConfig = this.config.shared | ||
const eventOptions = sharedConfig.eventOptions | ||
const props: any = {} | ||
export const Controller: ControllerConstructor = function (this: Controller, handlers: InternalHandlers) { | ||
this._gestures = new Set() | ||
this._targetEventStore = new EventStore(this) | ||
this._gestureEventStores = {} | ||
this._gestureTimeoutStores = {} | ||
this._handlers = {} | ||
this._nativeHandlers = {} | ||
this._config = {} as InternalConfig | ||
this._pointerIds = new Set() | ||
this._touchIds = new Set() | ||
this.state = { | ||
shared: { | ||
shiftKey: false, | ||
metaKey: false, | ||
ctrlKey: false, | ||
altKey: false | ||
} | ||
} as State | ||
const bindFunction = sharedConfig.target | ||
? bindToEventStore(this._targetEventStore, sharedConfig.target()) | ||
: bindToProps(props, eventOptions) | ||
resolveGestures(this, handlers) | ||
} as any | ||
if (sharedConfig.enabled) { | ||
// Adding native handlers | ||
for (const eventKey in this.nativeHandlers) { | ||
bindFunction( | ||
eventKey, | ||
'', | ||
// @ts-ignore | ||
(event) => this.nativeHandlers[eventKey]({ ...this.state.shared, event, args }), | ||
undefined, | ||
true | ||
) | ||
} | ||
Controller.prototype.setEventIds = function (event) { | ||
if (isTouch(event)) { | ||
this._touchIds = new Set(Touches.ids(event as TouchEvent)) | ||
} else if ('pointerId' in event) { | ||
if (event.type === 'pointerup') this._pointerIds.delete(event.pointerId) | ||
else this._pointerIds.add(event.pointerId) | ||
} | ||
} as Controller['setEventIds'] | ||
Controller.prototype.applyHandlers = function (handlers, nativeHandlers) { | ||
this._handlers = handlers | ||
this._nativeHandlers = nativeHandlers | ||
} as Controller['applyHandlers'] | ||
Controller.prototype.applyConfig = function (config, gestureKey) { | ||
this._config = parse(config, gestureKey) | ||
} as Controller['applyConfig'] | ||
Controller.prototype.clean = function () { | ||
this._targetEventStore.clean() | ||
for (const key of this._gestures) { | ||
this._gestureEventStores[key]!.clean() | ||
this._gestureTimeoutStores[key]!.clean() | ||
} | ||
} as Controller['clean'] | ||
Controller.prototype.effect = function () { | ||
if (this._config.shared.target) this.bind() | ||
return () => this._targetEventStore.clean() | ||
} as Controller['effect'] | ||
Controller.prototype.bind = function (...args) { | ||
const sharedConfig = this._config.shared | ||
const eventOptions = sharedConfig.eventOptions | ||
const props: any = {} | ||
const bindFunction = sharedConfig.target | ||
? bindToEventStore(this._targetEventStore, sharedConfig.target()) | ||
: bindToProps(props, eventOptions) | ||
if (sharedConfig.enabled) { | ||
// Adding native handlers | ||
for (const eventKey in this._nativeHandlers) { | ||
bindFunction( | ||
eventKey, | ||
'', | ||
// @ts-ignore | ||
(event) => this._nativeHandlers[eventKey]({ ...this.state.shared, event, args }), | ||
undefined, | ||
true | ||
) | ||
// Adding gesture handlers | ||
for (const gestureKey of this.gestures) { | ||
if (this.config[gestureKey]!.enabled) { | ||
const Engine = EngineMap.get(gestureKey)! | ||
// @ts-ignore | ||
new Engine(this, args, gestureKey).bind(bindFunction) | ||
} | ||
} | ||
} | ||
// Adding gesture handlers | ||
for (const gestureKey of this._gestures) { | ||
if (this._config[gestureKey]!.enabled) { | ||
const Engine = EngineMap.get(gestureKey)! | ||
// @ts-ignore | ||
new Engine(this, args).bind(bindFunction) | ||
// If target isn't set, we return an object that contains gesture handlers | ||
// mapped to props handler event keys. | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]) | ||
} | ||
return props | ||
} | ||
} | ||
} | ||
// If target isn't set, we return an object that contains gesture handlers | ||
// mapped to props handler event keys. | ||
if (!sharedConfig.target) { | ||
for (const handlerProp in props) { | ||
props[handlerProp] = chain(...props[handlerProp]) | ||
} | ||
return props | ||
} | ||
} as Controller['bind'] | ||
function setupGesture(ctrl: Controller, gestureKey: GestureKey) { | ||
ctrl._gestures.add(gestureKey) | ||
ctrl._gestureEventStores[gestureKey] = new EventStore(ctrl) | ||
ctrl._gestureTimeoutStores[gestureKey] = new TimeoutStore() | ||
ctrl.gestures.add(gestureKey) | ||
ctrl.gestureEventStores[gestureKey] = new EventStore(ctrl) | ||
ctrl.gestureTimeoutStores[gestureKey] = new TimeoutStore() | ||
} | ||
@@ -195,0 +147,0 @@ |
import { Engine } from './Engine' | ||
import { V } from '../utils/maths' | ||
import type { Controller } from '../Controller' | ||
import { CoordinatesKey, GestureKey } from '../types' | ||
import { CoordinatesKey, Vector2 } from '../types' | ||
export interface CoordinatesEngineConstructor { | ||
new <Key extends CoordinatesKey>(ctrl: Controller, args: any[], key: Key): CoordinatesEngine<Key> | ||
} | ||
export abstract class CoordinatesEngine<Key extends CoordinatesKey> extends Engine<Key> { | ||
reset() { | ||
super.reset() | ||
this.state.axis = undefined | ||
} | ||
export interface CoordinatesEngine<Key extends GestureKey = CoordinatesKey> extends Engine<Key> {} | ||
init() { | ||
this.state.offset = [0, 0] | ||
this.state.lastOffset = [0, 0] | ||
} | ||
export const CoordinatesEngine: CoordinatesEngineConstructor = function <Key extends CoordinatesKey>( | ||
this: CoordinatesEngine<Key>, | ||
ctrl: Controller, | ||
args: any[], | ||
key: Key | ||
) { | ||
// @ts-ignore | ||
Engine.call(this, ctrl, args, key) | ||
} as any | ||
computeOffset() { | ||
const state = this.state | ||
state.offset = V.add(state.lastOffset, state.movement) | ||
} | ||
CoordinatesEngine.prototype = Object.create(Engine.prototype) | ||
computeMovement() { | ||
const { offset, lastOffset } = this.state | ||
this.state.movement = V.sub(offset, lastOffset) | ||
// let's take profit from this function to set `values` alias to `xy` | ||
this.state.xy = this.state.values | ||
} | ||
CoordinatesEngine.prototype.reset = function () { | ||
Engine.prototype.reset.call(this) | ||
this.state.axis = undefined | ||
} as CoordinatesEngine['reset'] | ||
intent(v: Vector2) { | ||
const state = this.state | ||
CoordinatesEngine.prototype.init = function () { | ||
this.state.offset = [0, 0] | ||
this.state.lastOffset = [0, 0] | ||
} as CoordinatesEngine['init'] | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]) | ||
if (axisMovementDifference < 0) state.axis = 'y' | ||
else if (axisMovementDifference > 0) state.axis = 'x' | ||
} | ||
CoordinatesEngine.prototype.computeOffset = function () { | ||
const state = this.state | ||
state.offset = V.add(state.lastOffset, state.movement) | ||
} as CoordinatesEngine['computeOffset'] | ||
const axis = state.axis | ||
CoordinatesEngine.prototype.computeMovement = function () { | ||
const { offset, lastOffset } = this.state | ||
this.state.movement = V.sub(offset, lastOffset) | ||
// let's take profit from this function to set `values` alias to `xy` | ||
this.state.xy = this.state.values | ||
} as CoordinatesEngine['computeMovement'] | ||
CoordinatesEngine.prototype.intent = function (v) { | ||
const state = this.state | ||
if (!state.axis) { | ||
const axisMovementDifference = Math.abs(v[0]) - Math.abs(v[1]) | ||
if (axisMovementDifference < 0) state.axis = 'y' | ||
else if (axisMovementDifference > 0) state.axis = 'x' | ||
} | ||
const axis = state.axis | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false | ||
if (axis === 'x') v[1] = 0 | ||
else if (axis === 'y') v[0] = 0 | ||
} else { | ||
state._blocked = false | ||
if (this.config.lockDirection) { | ||
if (axis) { | ||
state._blocked = false | ||
if (axis === 'x') v[1] = 0 | ||
else if (axis === 'y') v[0] = 0 | ||
} else { | ||
state._blocked = false | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false | ||
if (axis === 'x') v[1] = 0 | ||
else if (axis === 'y') v[0] = 0 | ||
} else { | ||
state._blocked = true | ||
} | ||
} | ||
} else if (this.config.axis) { | ||
if (!!axis && axis === this.config.axis) { | ||
state._blocked = false | ||
if (axis === 'x') v[1] = 0 | ||
else if (axis === 'y') v[0] = 0 | ||
} else { | ||
state._blocked = true | ||
} | ||
} | ||
} as CoordinatesEngine['intent'] | ||
} |
import { Controller } from '../Controller' | ||
import { EventStore } from '../EventStore' | ||
import { TimeoutStore } from '../TimeoutStore' | ||
import { getEventDetails } from '../utils/events' | ||
import { call } from '../utils/fn' | ||
import { V, computeRubberband } from '../utils/maths' | ||
import { GestureKey, Handler, IngKey, InternalConfig, State, Vector2 } from '../types' | ||
import { GestureKey, IngKey, State, Vector2 } from '../types' | ||
export interface EngineConstructor { | ||
new <Key extends GestureKey>(ctrl: Controller, args: any[], key: Key): Engine<Key> | ||
} | ||
export interface Engine<Key extends GestureKey = GestureKey> { | ||
export interface Engine<Key extends GestureKey> { | ||
/** | ||
* The Controller handling state. | ||
*/ | ||
ctrl: Controller | ||
/** | ||
* The gesture key ('drag' | 'pinch' | 'wheel' | 'scroll' | 'move' | 'hover') | ||
*/ | ||
key: Key | ||
/** | ||
* The key representing the active state of the gesture in the shared state. | ||
* ('dragging' | 'pinching' | 'wheeling' | 'scrolling' | 'moving' | 'hovering') | ||
*/ | ||
ingKey: IngKey | ||
/** | ||
* The arguments passed to the `bind` function. | ||
*/ | ||
args: any[] | ||
/** | ||
* Shortcut to the gesture state read from the Controller. | ||
*/ | ||
state: NonNullable<State[Key]> | ||
/** | ||
* Shortcut to the shared state read from the Controller | ||
*/ | ||
shared: State['shared'] | ||
/** | ||
* Shortcut to the gesture config read from the Controller. | ||
*/ | ||
config: NonNullable<InternalConfig[Key]> | ||
/** | ||
* Shortcut to the shared config read from the Controller. | ||
*/ | ||
sharedConfig: InternalConfig['shared'] | ||
/** | ||
* Shortcut to the gesture event store read from the Controller. | ||
*/ | ||
eventStore: EventStore | ||
/** | ||
* Shortcut to the gesture timeout store read from the Controller. | ||
*/ | ||
timeoutStore: TimeoutStore | ||
/** | ||
* Shortcut to the gesture handler read from the Controller. | ||
*/ | ||
handler: Handler<Key> | ||
/** | ||
* Function that some gestures can use to add initilization | ||
* properties to the state when it is created. | ||
*/ | ||
init?(this: Engine<Key>): void | ||
init?(): void | ||
/** | ||
@@ -68,3 +17,3 @@ * Setup function that some gestures can use to set additional properties of | ||
*/ | ||
setup?(this: Engine<Key>): void | ||
setup?(): void | ||
/** | ||
@@ -76,22 +25,43 @@ * Function used by some gestures to determine the intentionality of a | ||
*/ | ||
intent?(this: Engine<Key>, movement: Vector2): void | ||
intent?(movement: Vector2): void | ||
} | ||
export abstract class Engine<Key extends GestureKey> { | ||
/** | ||
* Function that resets the state. | ||
* The Controller handling state. | ||
*/ | ||
reset(this: Engine<Key>): void | ||
ctrl: Controller | ||
/** | ||
* Function ran at the start of the gesture. | ||
* @param event | ||
* The gesture key ('drag' | 'pinch' | 'wheel' | 'scroll' | 'move' | 'hover') | ||
*/ | ||
start(this: Engine<Key>, event: NonNullable<State[Key]>['event']): void | ||
readonly key: Key | ||
/** | ||
* Computes all sorts of state attributes, including kinematics. | ||
* @param event | ||
* The key representing the active state of the gesture in the shared state. | ||
* ('dragging' | 'pinching' | 'wheeling' | 'scrolling' | 'moving' | 'hovering') | ||
*/ | ||
compute(this: Engine<Key>, event?: NonNullable<State[Key]>['event']): void | ||
abstract readonly ingKey: IngKey | ||
/** | ||
* The arguments passed to the `bind` function. | ||
*/ | ||
args: any[] | ||
constructor(ctrl: Controller, args: any[], key: Key) { | ||
this.ctrl = ctrl | ||
this.args = args | ||
this.key = key | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
} as any | ||
if (this.init) this.init() | ||
this.reset() | ||
} | ||
} | ||
/** | ||
* Function implemented by gestures that compute the offset from the state | ||
* movement. | ||
*/ | ||
computeOffset(this: Engine<Key>): void | ||
abstract computeOffset(): void | ||
/** | ||
@@ -101,12 +71,4 @@ * Function implemented by the gestures that compute the movement from the | ||
*/ | ||
computeMovement(this: Engine<Key>): void | ||
abstract computeMovement(): void | ||
/** | ||
* Fires the gesture handler. | ||
*/ | ||
emit(this: Engine<Key>): void | ||
/** | ||
* Cleans the gesture timeouts and event listeners. | ||
*/ | ||
clean(this: Engine<Key>): void | ||
/** | ||
* Executes the bind function so that listeners are properly set by the | ||
@@ -116,4 +78,3 @@ * Controller. | ||
*/ | ||
bind( | ||
this: Engine<Key>, | ||
abstract bind( | ||
bindFunction: ( | ||
@@ -126,202 +87,209 @@ device: string, | ||
): void | ||
} | ||
export const Engine: EngineConstructor = function <Key extends GestureKey>( | ||
this: Engine<Key>, | ||
ctrl: Controller, | ||
args: any[], | ||
key: Key | ||
) { | ||
this.ctrl = ctrl | ||
this.key = key | ||
this.args = args | ||
if (!this.state) { | ||
this.state = { | ||
values: [0, 0], | ||
initial: [0, 0] | ||
} as any | ||
if (this.init) this.init() | ||
this.reset() | ||
/** | ||
* Shortcut to the gesture state read from the Controller. | ||
*/ | ||
get state() { | ||
return this.ctrl.state[this.key]! | ||
} | ||
} as any | ||
Engine.prototype = { | ||
get state() { | ||
return this.ctrl.state[this.key] | ||
}, | ||
set state(state) { | ||
this.ctrl.state[this.key] = state | ||
}, | ||
} | ||
/** | ||
* Shortcut to the shared state read from the Controller | ||
*/ | ||
get shared() { | ||
return this.ctrl.state.shared | ||
}, | ||
} | ||
/** | ||
* Shortcut to the gesture event store read from the Controller. | ||
*/ | ||
get eventStore() { | ||
return this.ctrl._gestureEventStores[this.key] | ||
}, | ||
return this.ctrl.gestureEventStores[this.key]! | ||
} | ||
/** | ||
* Shortcut to the gesture timeout store read from the Controller. | ||
*/ | ||
get timeoutStore() { | ||
return this.ctrl._gestureTimeoutStores[this.key] | ||
}, | ||
return this.ctrl.gestureTimeoutStores[this.key]! | ||
} | ||
/** | ||
* Shortcut to the gesture config read from the Controller. | ||
*/ | ||
get config() { | ||
return this.ctrl._config[this.key] | ||
}, | ||
return this.ctrl.config[this.key]! | ||
} | ||
/** | ||
* Shortcut to the shared config read from the Controller. | ||
*/ | ||
get sharedConfig() { | ||
return this.ctrl._config.shared | ||
}, | ||
return this.ctrl.config.shared | ||
} | ||
/** | ||
* Shortcut to the gesture handler read from the Controller. | ||
*/ | ||
get handler() { | ||
return this.ctrl._handlers[this.key] | ||
return this.ctrl.handlers[this.key]! | ||
} | ||
} | ||
Engine.prototype.reset = function () { | ||
const { state, shared, config, ingKey } = this | ||
const { transform, threshold = [0, 0] } = config | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false | ||
state._step = [false, false] | ||
state.intentional = false | ||
state._movement = [0, 0] | ||
state._distance = [0, 0] | ||
state._delta = [0, 0] | ||
// the _threshold is the difference between a [0,0] origin offset converted to | ||
// its new space coordinates | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs) as Vector2 | ||
// prettier-ignore | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]] | ||
state.axis = undefined | ||
state.memo = undefined | ||
state.elapsedTime = 0 | ||
state.direction = [0, 0] | ||
state.distance = [0, 0] | ||
state.velocity = [0, 0] | ||
state.movement = [0, 0] | ||
state.delta = [0, 0] | ||
state.timeStamp = 0 | ||
} as Engine['reset'] | ||
Engine.prototype.start = function (event) { | ||
const state = this.state | ||
const config = this.config | ||
if (!state._active) { | ||
this.reset() | ||
state._active = true | ||
state.target = event.currentTarget! | ||
state.initial = state.values | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset | ||
state.offset = state.lastOffset | ||
reset() { | ||
const { state, shared, config, ingKey } = this | ||
const { transform, threshold = [0, 0] } = config | ||
shared[ingKey] = state._active = state.active = state._blocked = state._force = false | ||
state._step = [false, false] | ||
state.intentional = false | ||
state._movement = [0, 0] | ||
state._distance = [0, 0] | ||
state._delta = [0, 0] | ||
// the _threshold is the difference between a [0,0] origin offset converted to | ||
// its new space coordinates | ||
state._threshold = V.sub(transform(threshold), transform([0, 0])).map(Math.abs) as Vector2 | ||
// prettier-ignore | ||
state._bounds = [[-Infinity, Infinity], [-Infinity, Infinity]] | ||
state.axis = undefined | ||
state.memo = undefined | ||
state.elapsedTime = 0 | ||
state.direction = [0, 0] | ||
state.distance = [0, 0] | ||
state.velocity = [0, 0] | ||
state.movement = [0, 0] | ||
state.delta = [0, 0] | ||
state.timeStamp = 0 | ||
} | ||
state.startTime = state.timeStamp = event.timeStamp | ||
} as Engine['start'] | ||
/** | ||
* Function ran at the start of the gesture. | ||
* @param event | ||
*/ | ||
start(event: NonNullable<State[Key]>['event']) { | ||
const state = this.state | ||
const config = this.config | ||
if (!state._active) { | ||
this.reset() | ||
state._active = true | ||
state.target = event.currentTarget! | ||
state.initial = state.values | ||
state.lastOffset = config.from ? call(config.from, state) : state.offset | ||
state.offset = state.lastOffset | ||
} | ||
state.startTime = state.timeStamp = event.timeStamp | ||
} | ||
/** | ||
* Computes all sorts of state attributes, including kinematics. | ||
* @param event | ||
*/ | ||
compute(event?: NonNullable<State[Key]>['event']) { | ||
const { state, config, shared } = this | ||
state.args = this.args | ||
Engine.prototype.compute = function (event) { | ||
const { state, config, shared } = this | ||
state.args = this.args | ||
let dt = 0 | ||
let dt = 0 | ||
if (event) { | ||
// sets the shared state with event properties | ||
state.event = event | ||
shared.touches = this.ctrl.pointerIds.size || this.ctrl.touchIds.size | ||
shared.locked = !!document.pointerLockElement | ||
Object.assign(shared, getEventDetails(event)) | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0 | ||
if (event) { | ||
// sets the shared state with event properties | ||
state.event = event | ||
shared.touches = this.ctrl._pointerIds.size || this.ctrl._touchIds.size | ||
shared.locked = !!document.pointerLockElement | ||
Object.assign(shared, getEventDetails(event)) | ||
shared.down = shared.pressed = shared.buttons > 0 || shared.touches > 0 | ||
// sets time stamps | ||
dt = event.timeStamp - state.timeStamp | ||
state.timeStamp = event.timeStamp | ||
state.elapsedTime = state.timeStamp - state.startTime | ||
} | ||
// sets time stamps | ||
dt = event.timeStamp - state.timeStamp | ||
state.timeStamp = event.timeStamp | ||
state.elapsedTime = state.timeStamp - state.startTime | ||
} | ||
// only compute _distance if the state is active otherwise we might compute it | ||
// twice when the gesture ends because state._delta wouldn't have changed on | ||
// the last frame. | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs) as Vector2 | ||
V.addTo(state._distance, _absoluteDelta) | ||
} | ||
// only compute _distance if the state is active otherwise we might compute it | ||
// twice when the gesture ends because state._delta wouldn't have changed on | ||
// the last frame. | ||
if (state._active) { | ||
const _absoluteDelta = state._delta.map(Math.abs) as Vector2 | ||
V.addTo(state._distance, _absoluteDelta) | ||
} | ||
const [_m0, _m1] = config.transform(state._movement) | ||
const [_t0, _t1] = state._threshold | ||
// Step will hold the threshold at which point the gesture was triggered. The | ||
// threshold is signed depending on which direction triggered it. | ||
let [_s0, _s1] = state._step | ||
const [_m0, _m1] = config.transform(state._movement) | ||
const [_t0, _t1] = state._threshold | ||
// Step will hold the threshold at which point the gesture was triggered. The | ||
// threshold is signed depending on which direction triggered it. | ||
let [_s0, _s1] = state._step | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0 | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1 | ||
if (_s0 === false) _s0 = Math.abs(_m0) >= _t0 && Math.sign(_m0) * _t0 | ||
if (_s1 === false) _s1 = Math.abs(_m1) >= _t1 && Math.sign(_m1) * _t1 | ||
state.intentional = _s0 !== false || _s1 !== false | ||
state.intentional = _s0 !== false || _s1 !== false | ||
if (!state.intentional) return | ||
if (!state.intentional) return | ||
state._step = [_s0, _s1] | ||
state._step = [_s0, _s1] | ||
const movement: Vector2 = [0, 0] | ||
const movement: Vector2 = [0, 0] | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0 | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0 | ||
movement[0] = _s0 !== false ? _m0 - _s0 : 0 | ||
movement[1] = _s1 !== false ? _m1 - _s1 : 0 | ||
// let's run intentionality check. | ||
if (this.intent) this.intent(movement) | ||
// let's run intentionality check. | ||
if (this.intent) this.intent(movement) | ||
if ((state._active && !state._blocked) || state.active) { | ||
state.first = state._active && !state.active | ||
state.last = !state._active && state.active | ||
state.active = shared[this.ingKey] = state._active | ||
if ((state._active && !state._blocked) || state.active) { | ||
state.first = state._active && !state.active | ||
state.last = !state._active && state.active | ||
state.active = shared[this.ingKey] = state._active | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state) | ||
if (this.setup) this.setup() | ||
} | ||
if (event) { | ||
if (state.first) { | ||
if ('bounds' in config) state._bounds = call(config.bounds, state) | ||
if (this.setup) this.setup() | ||
} | ||
const previousMovement = state.movement | ||
state.movement = movement | ||
const previousMovement = state.movement | ||
state.movement = movement | ||
this.computeOffset() | ||
this.computeOffset() | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement) | ||
const absoluteDelta = state.delta.map(Math.abs) as Vector2 | ||
if (!state.last) { | ||
state.delta = V.sub(movement, previousMovement) | ||
const absoluteDelta = state.delta.map(Math.abs) as Vector2 | ||
V.addTo(state.distance, absoluteDelta) | ||
state.direction = state.delta.map(Math.sign) as Vector2 | ||
V.addTo(state.distance, absoluteDelta) | ||
state.direction = state.delta.map(Math.sign) as Vector2 | ||
if (!state.first && dt > 0) { | ||
// calculates kinematics unless the gesture starts or ends | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt] | ||
if (!state.first && dt > 0) { | ||
// calculates kinematics unless the gesture starts or ends | ||
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt] | ||
} | ||
} | ||
} | ||
} | ||
// @ts-ignore | ||
const rubberband: Vector2 = state._active ? config.rubberband || [0, 0] : [0, 0] | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband) | ||
this.computeMovement() | ||
} | ||
/** | ||
* Fires the gesture handler. | ||
*/ | ||
emit() { | ||
const state = this.state | ||
const shared = this.shared | ||
const config = this.config | ||
// @ts-ignore | ||
const rubberband: Vector2 = state._active ? config.rubberband || [0, 0] : [0, 0] | ||
state.offset = computeRubberband(state._bounds, state.offset, rubberband) | ||
this.computeMovement() | ||
} as Engine['compute'] | ||
if (!state._active) this.clean() | ||
Engine.prototype.emit = function () { | ||
const state = this.state | ||
const shared = this.shared | ||
const config = this.config | ||
// we don't trigger the handler if the gesture is blockedor non intentional, | ||
// unless the `_force` flag was set or the `triggerAllEvents` option was set | ||
// to true in the config. | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return | ||
if (!state._active) this.clean() | ||
// @ts-ignore | ||
const memo = this.handler({ ...shared, ...state }) | ||
// we don't trigger the handler if the gesture is blockedor non intentional, | ||
// unless the `_force` flag was set or the `triggerAllEvents` option was set | ||
// to true in the config. | ||
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return | ||
const memo = this.handler({ | ||
...shared, | ||
...state | ||
}) | ||
// Sets memo to the returned value of the handler (unless it's undefined) | ||
if (memo !== undefined) state.memo = memo | ||
} as Engine['emit'] | ||
Engine.prototype.clean = function () { | ||
this.eventStore.clean() | ||
this.timeoutStore.clean() | ||
} as Engine['clean'] | ||
// Sets memo to the returned value of the handler (unless it's undefined) | ||
if (memo !== undefined) state.memo = memo | ||
} | ||
/** | ||
* Cleans the gesture timeouts and event listeners. | ||
*/ | ||
clean() { | ||
this.eventStore.clean() | ||
this.timeoutStore.clean() | ||
} | ||
} |
@@ -0,13 +1,12 @@ | ||
import type { Controller } from './Controller' | ||
import { toDomEventType } from './utils/events' | ||
interface EventStoreConstructor { | ||
new (ctrl: any): EventStore | ||
} | ||
export class EventStore { | ||
private _listeners: (() => void)[] = [] | ||
private _ctrl: Controller | ||
constructor(ctrl: Controller) { | ||
this._ctrl = ctrl | ||
} | ||
export interface EventStore { | ||
_ctrl: any | ||
_listeners: (() => void)[] | ||
clean(this: EventStore): void | ||
add( | ||
this: EventStore, | ||
element: EventTarget, | ||
@@ -18,20 +17,13 @@ device: string, | ||
options?: AddEventListenerOptions | ||
): void | ||
) { | ||
const type = toDomEventType(device, action) | ||
const eventOptions = options || this._ctrl.config.shared.eventOptions | ||
element.addEventListener(type, handler, eventOptions) | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)) | ||
} | ||
clean() { | ||
this._listeners.forEach((remove) => remove()) | ||
this._listeners = [] | ||
} | ||
} | ||
export const EventStore: EventStoreConstructor = function (this: EventStore, ctrl: any) { | ||
this._ctrl = ctrl | ||
this._listeners = [] | ||
} as any | ||
EventStore.prototype.add = function (element, device, action, handler, options) { | ||
const type = toDomEventType(device, action) | ||
const eventOptions = options || this._ctrl._config.shared.eventOptions | ||
element.addEventListener(type, handler, eventOptions) | ||
this._listeners.push(() => element.removeEventListener(type, handler, eventOptions)) | ||
} as EventStore['add'] | ||
EventStore.prototype.clean = function () { | ||
this._listeners.forEach((remove) => remove()) | ||
this._listeners = [] | ||
} as EventStore['clean'] |
import { ResolverMap } from './config/resolver' | ||
import { DragEngineConstructor } from './engines/DragEngine/DragEngineCore' | ||
import { HoverEngineConstructor } from './engines/HoverEngine/HoverEngineCore' | ||
import { MoveEngineConstructor } from './engines/MoveEngine/MoveEngineCore' | ||
import { PinchEngineConstructor } from './engines/PinchEngine/PinchEngineCore' | ||
import { ScrollEngineConstructor } from './engines/ScrollEngine/ScrollEngineCore' | ||
import { WheelEngineConstructor } from './engines/WheelEngine/WheelEngineCore' | ||
import type { Controller } from './Controller' | ||
import type { Engine } from './engines/Engine' | ||
import { FullGestureState, GestureHandlers, GestureKey, InternalHandlers, UserGestureConfig } from './types' | ||
type GestureEngineConstuctor = | ||
| DragEngineConstructor | ||
| ScrollEngineConstructor | ||
| WheelEngineConstructor | ||
| PinchEngineConstructor | ||
| HoverEngineConstructor | ||
| MoveEngineConstructor | ||
export type EngineClass<Key extends GestureKey> = { | ||
new (controller: Controller, args: any[], key: Key): Engine<Key> | ||
} | ||
export const EngineMap = new Map<GestureKey, GestureEngineConstuctor>() | ||
export const EngineMap = new Map<GestureKey, EngineClass<any>>() | ||
export const ConfigResolverMap = new Map<GestureKey, ResolverMap>() | ||
export function registerEngine(action: GestureKey, Engine: GestureEngineConstuctor) { | ||
export function registerEngine<Key extends GestureKey>(action: Key, Engine: EngineClass<Key>) { | ||
EngineMap.set(action, Engine) | ||
@@ -23,0 +15,0 @@ } |
@@ -1,35 +0,23 @@ | ||
interface TimeoutStoreConstructor { | ||
new (): TimeoutStore | ||
} | ||
export class TimeoutStore { | ||
private _timeouts = new Map<string, number>() | ||
export interface TimeoutStore { | ||
_timeouts: Map<string, number> | ||
add<FunctionType extends (...args: any) => any>( | ||
this: TimeoutStore, | ||
key: string, | ||
callback: FunctionType, | ||
ms?: number, | ||
ms = 140, | ||
...args: Parameters<FunctionType> | ||
): void | ||
remove(this: TimeoutStore, key: string): void | ||
clean(this: TimeoutStore): void | ||
} | ||
) { | ||
this.remove(key) | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)) | ||
} | ||
export const TimeoutStore: TimeoutStoreConstructor = function (this: TimeoutStore) { | ||
this._timeouts = new Map() | ||
} as any | ||
remove(key: string) { | ||
const timeout = this._timeouts.get(key) | ||
if (timeout) window.clearTimeout(timeout) | ||
} | ||
TimeoutStore.prototype.add = function (key, callback, ms = 140, ...args) { | ||
this.remove(key) | ||
this._timeouts.set(key, window.setTimeout(callback, ms, ...args)) | ||
} as TimeoutStore['add'] | ||
TimeoutStore.prototype.remove = function (key) { | ||
const timeout = this._timeouts.get(key) | ||
if (timeout) window.clearTimeout(timeout) | ||
} as TimeoutStore['remove'] | ||
TimeoutStore.prototype.clean = function () { | ||
this._timeouts.forEach((timeout) => void window.clearTimeout(timeout)) | ||
this._timeouts.clear() | ||
} as TimeoutStore['clean'] | ||
clean() { | ||
this._timeouts.forEach((timeout) => void window.clearTimeout(timeout)) | ||
this._timeouts.clear() | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
265434
72
8003