wheel-gestures
Advanced tools
Comparing version 1.0.1 to 1.0.2-beta.3
@@ -1,10 +0,10 @@ | ||
interface Props<T> { | ||
events: T[]; | ||
export interface Props { | ||
} | ||
declare type EventListener = () => void; | ||
export default function EventBus<T extends string>({ events }: Props<T>): Readonly<{ | ||
on: (type: T, listener: EventListener) => () => void; | ||
off: (type: T, listener: EventListener) => void; | ||
dispatch: (type: T) => void; | ||
export declare type EventMapEmpty = Record<string, unknown>; | ||
export declare type EventListener<D = unknown> = (data: D) => void; | ||
export declare type Off = () => void; | ||
export default function EventBus<EventMap = EventMapEmpty>({}?: Props): Readonly<{ | ||
on: <EK extends keyof EventMap>(type: EK, listener: EventListener<EventMap[EK]>) => Off; | ||
off: <EK_1 extends keyof EventMap>(type: EK_1, listener: EventListener<EventMap[EK_1]>) => void; | ||
dispatch: <EK_2 extends keyof EventMap>(type: EK_2, data: EventMap[EK_2]) => void; | ||
}>; | ||
export {}; |
@@ -1,21 +0,2 @@ | ||
declare type WheelEventDataRequiredFields = 'deltaMode' | 'deltaX' | 'deltaY'; | ||
export interface WheelEventData extends Pick<WheelEvent, WheelEventDataRequiredFields>, Partial<Omit<WheelEvent, WheelEventDataRequiredFields>> { | ||
} | ||
export declare type WheelTypes = 'WHEEL' | 'ANY_WHEEL' | 'MOMENTUM_WHEEL'; | ||
export declare enum WheelPhase { | ||
'ANY_WHEEL_START' = "ANY_WHEEL_START", | ||
'ANY_WHEEL' = "ANY_WHEEL", | ||
'ANY_WHEEL_END' = "ANY_WHEEL_END", | ||
'WHEEL_START' = "WHEEL_START", | ||
'WHEEL' = "WHEEL", | ||
'WHEEL_END' = "WHEEL_END", | ||
'MOMENTUM_WHEEL_START' = "MOMENTUM_WHEEL_START", | ||
'MOMENTUM_WHEEL' = "MOMENTUM_WHEEL", | ||
'MOMENTUM_WHEEL_CANCEL' = "MOMENTUM_WHEEL_CANCEL", | ||
'MOMENTUM_WHEEL_END' = "MOMENTUM_WHEEL_END" | ||
} | ||
export declare type PhaseData = ReturnType<typeof WheelAnalyzer.prototype.getCurrentState>; | ||
export declare type SubscribeFn = (type: WheelPhase, data: PhaseData) => void; | ||
export declare type Unsubscribe = () => void; | ||
declare type PreventWheelActionType = 'all' | 'x' | 'y'; | ||
import { PreventWheelActionType, SubscribeFn, Unobserve, Unsubscribe, WheelEventData, WheelPhase } from './wheel-analyzer.types'; | ||
export interface Options { | ||
@@ -27,2 +8,3 @@ preventWheelAction: PreventWheelActionType; | ||
private isStarted; | ||
private isStartPublished; | ||
private isMomentum; | ||
@@ -33,7 +15,2 @@ private lastAbsDelta; | ||
private accelerationFactors; | ||
private deltaVelocity; | ||
private deltaTotal; | ||
/** | ||
* @description merged scrollPoints | ||
*/ | ||
private scrollPoints; | ||
@@ -44,5 +21,6 @@ private scrollPointsToMerge; | ||
private options; | ||
private readonly willEnd; | ||
private willEndTimeout; | ||
private willEnd; | ||
constructor(options?: Partial<Options>); | ||
observe: (target: EventTarget) => () => void; | ||
observe: (target: EventTarget) => Unobserve; | ||
unobserve: (target: EventTarget) => void; | ||
@@ -55,5 +33,8 @@ disconnect: () => void; | ||
private shouldPreventDefault; | ||
private clampDelta; | ||
private processWheelEventData; | ||
private debugMessage; | ||
private updateVelocityNew; | ||
private updateStartVelocity; | ||
private updateVelocity; | ||
private updateWillEndTimeout; | ||
private static accelerationFactorInMomentumRange; | ||
@@ -67,4 +48,2 @@ private detectMomentum; | ||
isMomentum: boolean; | ||
deltaVelocity: number; | ||
deltaTotal: number; | ||
axisDeltas: number[]; | ||
@@ -75,4 +54,3 @@ axisVelocity: number[]; | ||
private end; | ||
private readonly willEndSoon; | ||
private get willEndSoon(); | ||
} | ||
export {}; |
@@ -1,10 +0,12 @@ | ||
export * from './wheel-analyzer'; | ||
interface Props { | ||
axis: 'x' | 'y' | 'all'; | ||
import { WheelGesturesEventMap, WheelReason } from './wheel-gestures.types'; | ||
export interface Props { | ||
axis?: 'x' | 'y' | 'all'; | ||
wheelReason?: WheelReason; | ||
} | ||
export declare function WheelGestures({ axis }?: Props): Readonly<{ | ||
observe: (target: EventTarget) => () => void; | ||
export declare function WheelGestures({ axis, wheelReason }?: Props): Readonly<{ | ||
observe: (target: EventTarget) => import("./wheel-analyzer.types").Unobserve; | ||
unobserve: (target: EventTarget) => void; | ||
disconnect: () => void; | ||
on: (type: "wheelpan", listener: () => void) => () => void; | ||
on: <EK extends "wheelstart" | "wheelmove" | "wheelend">(type: EK, listener: import("./events/EventBus").EventListener<WheelGesturesEventMap[EK]>) => import("./events/EventBus").Off; | ||
off: <EK_1 extends "wheelstart" | "wheelmove" | "wheelend">(type: EK_1, listener: import("./events/EventBus").EventListener<WheelGesturesEventMap[EK_1]>) => void; | ||
}>; |
@@ -1,41 +0,70 @@ | ||
import { debounce } from 'throttle-debounce'; | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
} | ||
} | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
} | ||
return t; | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
function _objectDestructuringEmpty(obj) { | ||
if (obj == null) throw new TypeError("Cannot destructure undefined"); | ||
} | ||
var WheelPhase; | ||
(function (WheelPhase) { | ||
WheelPhase["ANY_WHEEL_START"] = "ANY_WHEEL_START"; | ||
WheelPhase["ANY_WHEEL"] = "ANY_WHEEL"; | ||
WheelPhase["ANY_WHEEL_END"] = "ANY_WHEEL_END"; | ||
WheelPhase["WHEEL_START"] = "WHEEL_START"; | ||
WheelPhase["WHEEL"] = "WHEEL"; | ||
WheelPhase["WHEEL_END"] = "WHEEL_END"; | ||
WheelPhase["MOMENTUM_WHEEL_START"] = "MOMENTUM_WHEEL_START"; | ||
WheelPhase["MOMENTUM_WHEEL"] = "MOMENTUM_WHEEL"; | ||
WheelPhase["MOMENTUM_WHEEL_CANCEL"] = "MOMENTUM_WHEEL_CANCEL"; | ||
WheelPhase["MOMENTUM_WHEEL_END"] = "MOMENTUM_WHEEL_END"; | ||
})(WheelPhase || (WheelPhase = {})); | ||
var LINE_HEIGHT = 16 * 1.125; | ||
var PAGE_HEIGHT = typeof window !== 'undefined' && window.innerHeight || 800; | ||
var DELTA_MODE_UNIT = [1, LINE_HEIGHT, PAGE_HEIGHT]; | ||
function normalizeWheel(e) { | ||
var deltaX = e.deltaX * DELTA_MODE_UNIT[e.deltaMode]; | ||
var deltaY = e.deltaY * DELTA_MODE_UNIT[e.deltaMode]; | ||
var deltaZ = (e.deltaZ || 0) * DELTA_MODE_UNIT[e.deltaMode]; | ||
return { | ||
deltaX: deltaX, | ||
deltaY: deltaY, | ||
deltaZ: deltaZ, | ||
deltaMode: 0, | ||
timestamp: e.timeStamp | ||
}; | ||
} | ||
var WHEELEVENTS_TO_MERGE = 2; | ||
@@ -47,327 +76,510 @@ var WHEELEVENTS_TO_ANALAZE = 5; | ||
var ACC_FACTOR_MAX = 0.96; | ||
var WheelPhase; | ||
(function (WheelPhase) { | ||
WheelPhase["ANY_WHEEL_START"] = "ANY_WHEEL_START"; | ||
WheelPhase["ANY_WHEEL"] = "ANY_WHEEL"; | ||
WheelPhase["ANY_WHEEL_END"] = "ANY_WHEEL_END"; | ||
WheelPhase["WHEEL_START"] = "WHEEL_START"; | ||
WheelPhase["WHEEL"] = "WHEEL"; | ||
WheelPhase["WHEEL_END"] = "WHEEL_END"; | ||
WheelPhase["MOMENTUM_WHEEL_START"] = "MOMENTUM_WHEEL_START"; | ||
WheelPhase["MOMENTUM_WHEEL"] = "MOMENTUM_WHEEL"; | ||
WheelPhase["MOMENTUM_WHEEL_CANCEL"] = "MOMENTUM_WHEEL_CANCEL"; | ||
WheelPhase["MOMENTUM_WHEEL_END"] = "MOMENTUM_WHEEL_END"; | ||
})(WheelPhase || (WheelPhase = {})); | ||
var console = window.console; | ||
var DELTA_MAX_ABS = 150; // the initial timeout period is pretty long, so even old mouses, which emit wheel events less often, can produce a continuous gesture | ||
// the timeout is automatically adjusted during a gesture | ||
var WILL_END_TIMEOUT_DEFAULT = 400; | ||
var axes = ['x', 'y']; | ||
var deltaProp = { | ||
x: 'deltaX', | ||
y: 'deltaY', | ||
x: 'deltaX', | ||
y: 'deltaY' | ||
}; | ||
var defaults = { | ||
preventWheelAction: 'all', | ||
isDebug: process.env.NODE_ENV === 'development', | ||
preventWheelAction: 'all', | ||
isDebug: process.env.NODE_ENV === 'development' | ||
}; | ||
var WheelAnalyzer = /** @class */ (function () { | ||
function WheelAnalyzer(options) { | ||
var _this = this; | ||
if (options === void 0) { options = {}; } | ||
this.isStarted = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisDeltas = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.accelerationFactors = []; | ||
this.deltaVelocity = 0; // px per second | ||
this.deltaTotal = 0; // moved during this scroll interaction | ||
/** | ||
* @description merged scrollPoints | ||
*/ | ||
this.scrollPoints = []; | ||
this.scrollPointsToMerge = []; | ||
this.subscriptions = []; | ||
this.targets = []; | ||
this.willEnd = this.end; | ||
this.observe = function (target) { | ||
target.addEventListener('wheel', _this.feedWheel, { passive: false }); | ||
_this.targets.push(target); | ||
return _this.unobserve.bind(_this, target); | ||
}; | ||
this.unobserve = function (target) { | ||
target.removeEventListener('wheel', _this.feedWheel); | ||
_this.targets = _this.targets.filter(function (t) { return t !== target; }); | ||
}; | ||
// stops watching all of its target elements for visibility changes. | ||
this.disconnect = function () { | ||
_this.targets.forEach(_this.unobserve); | ||
}; | ||
this.subscribe = function (callback) { | ||
_this.subscriptions.push(callback); | ||
// return bound unsubscribe | ||
return _this.unsubscribe.bind(_this, callback); | ||
}; | ||
this.unsubscribe = function (callback) { | ||
if (!callback) | ||
throw new Error('please pass the callback which was used to subscribe'); | ||
_this.subscriptions = _this.subscriptions.filter(function (s) { return s !== callback; }); | ||
}; | ||
this.feedWheel = function (wheelEvents) { | ||
if (!wheelEvents) | ||
return; | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { return _this.processWheelEventData(wheelEvent); }); | ||
} | ||
else { | ||
_this.processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
this.publish = function (phase, data) { | ||
if (data === void 0) { data = _this.getCurrentState(phase); } | ||
_this.subscriptions.forEach(function (fn) { return fn(phase, data); }); | ||
}; | ||
this.willEnd = debounce(99, function () { return _this.end(); }); | ||
// merge passed options with defaults (filter undefined option values) | ||
this.options = Object.entries(options) | ||
.filter(function (_a) { | ||
var value = _a[1]; | ||
return value !== undefined; | ||
}) | ||
.reduce(function (o, _a) { | ||
var _b; | ||
var key = _a[0], value = _a[1]; | ||
return Object.assign(o, (_b = {}, _b[key] = value, _b)); | ||
}, __assign({}, defaults)); | ||
var WheelAnalyzer = /*#__PURE__*/function () { | ||
function WheelAnalyzer(options) { | ||
var _this = this; | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
WheelAnalyzer.prototype.shouldPreventDefault = function (e) { | ||
var preventWheelAction = this.options.preventWheelAction; | ||
var deltaX = e.deltaX, deltaY = e.deltaY; | ||
switch (preventWheelAction) { | ||
case 'all': | ||
return true; | ||
case 'x': | ||
return Math.abs(deltaX) >= Math.abs(deltaY); | ||
case 'y': | ||
return Math.abs(deltaY) >= Math.abs(deltaX); | ||
} | ||
this.debugMessage('unsupported preventWheelAction value: ' + preventWheelAction, 'warn'); | ||
this.isStarted = false; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisDeltas = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.accelerationFactors = []; | ||
this.scrollPoints = []; | ||
this.scrollPointsToMerge = []; | ||
this.subscriptions = []; | ||
this.targets = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
this.willEnd = function () { | ||
var willEndId = setTimeout(function () {}, _this.willEndTimeout); | ||
return function () { | ||
clearTimeout(willEndId); | ||
willEndId = setTimeout(_this.end, _this.willEndTimeout); | ||
}; | ||
}(); | ||
this.observe = function (target) { | ||
target.addEventListener('wheel', _this.feedWheel, { | ||
passive: false | ||
}); | ||
_this.targets.push(target); | ||
return _this.unobserve.bind(_this, target); | ||
}; | ||
WheelAnalyzer.prototype.processWheelEventData = function (e) { | ||
if (e.deltaMode !== 0) { | ||
if (this.options.isDebug) { | ||
this.debugMessage('deltaMode is not 0'); | ||
} | ||
return; | ||
} | ||
if (e.preventDefault && this.shouldPreventDefault(e)) { | ||
e.preventDefault(); | ||
} | ||
if (!this.isStarted) { | ||
this.start(); | ||
} | ||
var currentDelta = Math.abs(e.deltaY) > Math.abs(e.deltaX) ? e.deltaY : e.deltaX; | ||
var currentAbsDelta = Math.abs(currentDelta); | ||
if (this.isMomentum && currentAbsDelta > this.lastAbsDelta) { | ||
this.end(); | ||
this.start(); | ||
} | ||
this.axisDeltas = this.axisDeltas.map(function (delta, i) { return delta + e[deltaProp[axes[i]]]; }); | ||
this.deltaTotal = this.deltaTotal + currentDelta; | ||
this.lastAbsDelta = currentAbsDelta; | ||
this.scrollPointsToMerge.push({ | ||
currentDelta: currentDelta, | ||
currentAbsDelta: currentAbsDelta, | ||
axisDelta: [e.deltaX, e.deltaY], | ||
timestamp: e.timeStamp || Date.now(), | ||
}); | ||
if (this.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
var mergedScrollPoint = { | ||
currentDelta: this.scrollPointsToMerge.reduce(function (sum, b) { return sum + b.currentDelta; }, 0) / WHEELEVENTS_TO_MERGE, | ||
currentAbsDelta: this.scrollPointsToMerge.reduce(function (sum, b) { return sum + b.currentAbsDelta; }, 0) / WHEELEVENTS_TO_MERGE, | ||
axisDelta: this.scrollPointsToMerge | ||
.reduce(function (_a, _b) { | ||
var sumX = _a[0], sumY = _a[1]; | ||
var _c = _b.axisDelta, x = _c[0], y = _c[1]; | ||
return [sumX + x, sumY + y]; | ||
}, [0, 0]) | ||
.map(function (sum) { return sum / WHEELEVENTS_TO_MERGE; }), | ||
timestamp: this.scrollPointsToMerge.reduce(function (sum, b) { return sum + b.timestamp; }, 0) / WHEELEVENTS_TO_MERGE, | ||
}; | ||
this.scrollPoints.push(mergedScrollPoint); | ||
// only update velo after a merged scrollpoint was generated | ||
this.updateVelocityNew(); | ||
if (!this.isMomentum) { | ||
this.detectMomentum(); | ||
} | ||
// reset merge array | ||
this.scrollPointsToMerge = []; | ||
} | ||
this.publish(WheelPhase.ANY_WHEEL); | ||
this.publish(this.isMomentum ? WheelPhase.MOMENTUM_WHEEL : WheelPhase.WHEEL); | ||
// calc debounced end function, to recognize end of wheel event stream | ||
this.willEnd(); | ||
this.unobserve = function (target) { | ||
target.removeEventListener('wheel', _this.feedWheel); | ||
_this.targets = _this.targets.filter(function (t) { | ||
return t !== target; | ||
}); | ||
}; // stops watching all of its target elements for visibility changes. | ||
this.disconnect = function () { | ||
_this.targets.forEach(_this.unobserve); | ||
}; | ||
WheelAnalyzer.prototype.debugMessage = function (msg, level) { | ||
if (level === void 0) { level = 'log'; } | ||
if (!this.options.isDebug) | ||
return; | ||
console[level](msg); | ||
this.subscribe = function (callback) { | ||
_this.subscriptions.push(callback); // return bound unsubscribe | ||
return _this.unsubscribe.bind(_this, callback); | ||
}; | ||
WheelAnalyzer.prototype.updateVelocityNew = function () { | ||
var _this = this; | ||
// need to have two recent points to calc velocity | ||
var _a = this.scrollPoints.slice(-2), pA = _a[0], pB = _a[1]; | ||
if (!pA || !pB) { | ||
return; | ||
} | ||
// time delta | ||
var deltaTime = pB.timestamp - pA.timestamp; | ||
if (deltaTime <= 0) { | ||
this.debugMessage('invalid deltaTime'); | ||
return; | ||
} | ||
// calc the velocity per axes | ||
var velocity = pB.axisDelta.map(function (d) { return d / deltaTime; }); | ||
// calc the acceleration factor per axis | ||
var accelerationFactor = velocity.map(function (v, i) { | ||
return v / (_this.axisVelocity[i] || 1); | ||
this.unsubscribe = function (callback) { | ||
if (!callback) throw new Error('please pass the callback which was used to subscribe'); | ||
_this.subscriptions = _this.subscriptions.filter(function (s) { | ||
return s !== callback; | ||
}); | ||
}; | ||
this.feedWheel = function (wheelEvents) { | ||
if (!wheelEvents) return; | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { | ||
return _this.processWheelEventData(wheelEvent); | ||
}); | ||
this.axisVelocity = velocity; | ||
this.accelerationFactors.push(accelerationFactor); | ||
} else { | ||
_this.processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
WheelAnalyzer.accelerationFactorInMomentumRange = function (accFactor) { | ||
// when main axis is the the other one and there is no movement/change on the current one | ||
if (accFactor === 0) | ||
return true; | ||
return accFactor <= ACC_FACTOR_MAX && accFactor >= ACC_FACTOR_MIN; | ||
this.publish = function (phase, data) { | ||
if (data === void 0) { | ||
data = _this.getCurrentState(phase); | ||
} | ||
_this.subscriptions.forEach(function (fn) { | ||
return fn(phase, data); | ||
}); | ||
}; | ||
WheelAnalyzer.prototype.detectMomentum = function () { | ||
if (this.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return this.isMomentum; | ||
this.end = function () { | ||
if (!_this.isStarted) return; | ||
if (_this.isMomentum) { | ||
if (!_this.willEndSoon) { | ||
_this.publish(WheelPhase.MOMENTUM_WHEEL_CANCEL); | ||
} else { | ||
_this.publish(WheelPhase.MOMENTUM_WHEEL_END); | ||
} | ||
var recentAccelerationFactors = this.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); | ||
// check recent acceleration / deceleration factors | ||
var detectedMomentum = recentAccelerationFactors.reduce(function (mightBeMomentum, accFac) { | ||
// all recent need to match, if any did not match -> short circuit | ||
if (!mightBeMomentum) | ||
return false; | ||
// when both axis decelerate exactly in the same rate it is very likely caused by momentum | ||
var sameAccFac = !!accFac.reduce(function (f1, f2) { return (f1 && f1 < 1 && f1 === f2 ? 1 : 0); }); | ||
// check if acceleration factor is within momentum range | ||
var bothAreInRangeOrZero = accFac.filter(WheelAnalyzer.accelerationFactorInMomentumRange).length === accFac.length; | ||
// one the requirements must be fulfilled | ||
return sameAccFac || bothAreInRangeOrZero; | ||
}, true); | ||
// only keep the most recent events | ||
this.accelerationFactors = recentAccelerationFactors; | ||
if (detectedMomentum && !this.isMomentum) { | ||
this.publish(WheelPhase.WHEEL_END); | ||
this.isMomentum = true; | ||
this.publish(WheelPhase.MOMENTUM_WHEEL_START); | ||
} | ||
return this.isMomentum; | ||
}; | ||
WheelAnalyzer.prototype.getDebugState = function () { | ||
var props = __rest(this, []); | ||
return props; | ||
}; | ||
WheelAnalyzer.prototype.getCurrentState = function (type) { | ||
var debugData = this.options.isDebug ? this.getDebugState() : null; | ||
return { | ||
type: type, | ||
debugData: debugData, | ||
willEndSoon: this.willEndSoon, | ||
isMomentum: this.isMomentum, | ||
deltaVelocity: this.deltaVelocity, | ||
deltaTotal: this.deltaTotal, | ||
axisDeltas: this.axisDeltas, | ||
axisVelocity: this.axisVelocity, | ||
}; | ||
}; | ||
WheelAnalyzer.prototype.start = function () { | ||
this.isStarted = true; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisDeltas = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.deltaVelocity = 0; | ||
this.deltaTotal = 0; | ||
this.scrollPointsToMerge = []; | ||
this.scrollPoints = []; | ||
this.accelerationFactors = []; | ||
this.publish(WheelPhase.ANY_WHEEL_START); | ||
this.publish(WheelPhase.WHEEL_START); | ||
}; | ||
WheelAnalyzer.prototype.end = function () { | ||
if (this.isMomentum) { | ||
if (!this.willEndSoon) { | ||
this.publish(WheelPhase.MOMENTUM_WHEEL_CANCEL); | ||
} | ||
else { | ||
this.publish(WheelPhase.MOMENTUM_WHEEL_END); | ||
} | ||
} | ||
else { | ||
// in case of momentum, this event was already triggered when the momentum was detected so we do not trigger it here | ||
this.publish(WheelPhase.WHEEL_END); | ||
} | ||
this.publish(WheelPhase.ANY_WHEEL_END); | ||
this.isMomentum = false; | ||
this.isStarted = false; | ||
}; | ||
Object.defineProperty(WheelAnalyzer.prototype, "willEndSoon", { | ||
get: function () { | ||
var absDeltas = this.scrollPoints | ||
.slice(SOON_ENDING_WHEEL_COUNT * -1) | ||
.map(function (_a) { | ||
var currentAbsDelta = _a.currentAbsDelta; | ||
return currentAbsDelta; | ||
}); | ||
var absDeltaAverage = absDeltas.reduce(function (a, b) { return a + b; }, 0) / absDeltas.length; | ||
return absDeltaAverage <= SOON_ENDING_THRESHOLD; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
} else { | ||
// in case of momentum, this event was already triggered when the momentum was detected so we do not trigger it here | ||
_this.publish(WheelPhase.WHEEL_END); | ||
} | ||
_this.publish(WheelPhase.ANY_WHEEL_END); | ||
_this.isMomentum = false; | ||
_this.isStarted = false; | ||
}; // merge passed options with defaults (filter undefined option values) | ||
this.options = Object.entries(options).filter(function (_ref) { | ||
var value = _ref[1]; | ||
return value !== undefined; | ||
}).reduce(function (o, _ref2) { | ||
var _Object$assign; | ||
var key = _ref2[0], | ||
value = _ref2[1]; | ||
return Object.assign(o, (_Object$assign = {}, _Object$assign[key] = value, _Object$assign)); | ||
}, _extends({}, defaults)); | ||
} | ||
var _proto = WheelAnalyzer.prototype; | ||
_proto.shouldPreventDefault = function shouldPreventDefault(e) { | ||
var preventWheelAction = this.options.preventWheelAction; | ||
var deltaX = e.deltaX, | ||
deltaY = e.deltaY; | ||
switch (preventWheelAction) { | ||
case 'all': | ||
return true; | ||
case 'x': | ||
return Math.abs(deltaX) >= Math.abs(deltaY); | ||
case 'y': | ||
return Math.abs(deltaY) >= Math.abs(deltaX); | ||
} | ||
this.debugMessage('unsupported preventWheelAction value: ' + preventWheelAction, 'warn'); | ||
}; | ||
_proto.clampDelta = function clampDelta(delta) { | ||
return Math.min(DELTA_MAX_ABS, Math.max(-DELTA_MAX_ABS, delta)); | ||
}; | ||
_proto.processWheelEventData = function processWheelEventData(wheelEvent) { | ||
var _this2 = this; | ||
var normalizedWheel = normalizeWheel(wheelEvent); | ||
if (wheelEvent.preventDefault && this.shouldPreventDefault(wheelEvent)) { | ||
wheelEvent.preventDefault(); | ||
} | ||
if (!this.isStarted) { | ||
this.start(); | ||
} | ||
var currentDelta = this.clampDelta(Math.abs(normalizedWheel.deltaY) > Math.abs(normalizedWheel.deltaX) ? normalizedWheel.deltaY : normalizedWheel.deltaX); | ||
var currentAbsDelta = Math.abs(currentDelta); | ||
if (this.isMomentum && currentAbsDelta > this.lastAbsDelta) { | ||
this.end(); | ||
this.start(); | ||
} | ||
this.axisDeltas = this.axisDeltas.map(function (prevDelta, i) { | ||
return prevDelta + _this2.clampDelta(normalizedWheel[deltaProp[axes[i]]]); | ||
}); | ||
return WheelAnalyzer; | ||
}()); | ||
this.lastAbsDelta = currentAbsDelta; | ||
this.scrollPointsToMerge.push({ | ||
currentDelta: currentDelta, | ||
currentAbsDelta: currentAbsDelta, | ||
axisDeltaUnclampt: [normalizedWheel.deltaX, normalizedWheel.deltaY], | ||
timestamp: wheelEvent.timeStamp || Date.now() | ||
}); | ||
function EventBus(_a) { | ||
var events = _a.events; | ||
var listeners = Object.fromEntries(events.map(function (eventName) { return [eventName, new Array()]; })); | ||
function on(type, listener) { | ||
listeners[type] = listeners[type].concat(listener); | ||
return off.bind(undefined, type, listener); | ||
if (this.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
var mergedScrollPoint = { | ||
currentDelta: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.currentDelta; | ||
}, 0) / WHEELEVENTS_TO_MERGE, | ||
currentAbsDelta: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.currentAbsDelta; | ||
}, 0) / WHEELEVENTS_TO_MERGE, | ||
axisDeltaUnclampt: this.scrollPointsToMerge.reduce(function (_ref3, _ref4) { | ||
var sumX = _ref3[0], | ||
sumY = _ref3[1]; | ||
var _ref4$axisDeltaUnclam = _ref4.axisDeltaUnclampt, | ||
x = _ref4$axisDeltaUnclam[0], | ||
y = _ref4$axisDeltaUnclam[1]; | ||
return [sumX + x, sumY + y]; | ||
}, [0, 0]), | ||
// TODO: should one devide here or not?! | ||
//.map((sum) => sum / WHEELEVENTS_TO_MERGE), | ||
timestamp: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.timestamp; | ||
}, 0) / WHEELEVENTS_TO_MERGE | ||
}; | ||
this.scrollPoints.push(mergedScrollPoint); // only update velocity after a merged scrollpoint was generated | ||
this.updateVelocity(); | ||
if (!this.isMomentum) { | ||
this.detectMomentum(); | ||
} // reset merge array | ||
this.scrollPointsToMerge = []; | ||
} | ||
function off(type, listener) { | ||
listeners[type] = listeners[type].filter(function (l) { return l === listener; }); | ||
if (!this.scrollPoints.length) { | ||
this.updateStartVelocity(); | ||
} // publish start after all data points have been updated | ||
// TODO: check momentum afterwards | ||
if (!this.isStartPublished) { | ||
this.publish(WheelPhase.ANY_WHEEL_START); | ||
this.publish(WheelPhase.WHEEL_START); | ||
this.isStartPublished = true; | ||
} | ||
function dispatch(type) { | ||
listeners[type].forEach(function (l) { return l(); }); | ||
this.publish(WheelPhase.ANY_WHEEL); | ||
this.publish(this.isMomentum ? WheelPhase.MOMENTUM_WHEEL : WheelPhase.WHEEL); // calc debounced end function, to recognize end of wheel event stream | ||
this.willEnd(); | ||
}; | ||
_proto.debugMessage = function debugMessage(msg, level) { | ||
if (level === void 0) { | ||
level = 'log'; | ||
} | ||
return Object.freeze({ | ||
on: on, | ||
off: off, | ||
dispatch: dispatch, | ||
if (!this.options.isDebug) return; | ||
console[level](msg); | ||
}; | ||
_proto.updateStartVelocity = function updateStartVelocity() { | ||
var latestScrollPoint = this.scrollPointsToMerge[this.scrollPointsToMerge.length - 1]; | ||
this.axisVelocity = latestScrollPoint.axisDeltaUnclampt.map(function (d) { | ||
return d / WILL_END_TIMEOUT_DEFAULT; | ||
}); | ||
} | ||
}; | ||
var defaults$1 = { | ||
axis: 'all', | ||
}; | ||
function WheelGestures(_a) { | ||
var axis = (_a === void 0 ? defaults$1 : _a).axis; | ||
var _b = new WheelAnalyzer({ | ||
preventWheelAction: axis, | ||
}), observe = _b.observe, unobserve = _b.unobserve, disconnect = _b.disconnect; | ||
var on = EventBus({ events: ['wheelpan'] }).on; | ||
on('wheelpan', function () { return undefined; }); | ||
return Object.freeze({ | ||
observe: observe, | ||
unobserve: unobserve, | ||
disconnect: disconnect, | ||
on: on, | ||
_proto.updateVelocity = function updateVelocity() { | ||
var _this3 = this; | ||
// need to have two recent points to calc velocity | ||
var _this$scrollPoints$sl = this.scrollPoints.slice(-2), | ||
pA = _this$scrollPoints$sl[0], | ||
pB = _this$scrollPoints$sl[1]; | ||
if (!pA || !pB) { | ||
return; | ||
} // time delta | ||
var deltaTime = pB.timestamp - pA.timestamp; | ||
if (deltaTime <= 0) { | ||
this.debugMessage('invalid deltaTime'); | ||
return; | ||
} // calc the velocity per axes | ||
var velocity = pB.axisDeltaUnclampt.map(function (d) { | ||
return d / deltaTime; | ||
}); // calc the acceleration factor per axis | ||
var accelerationFactor = velocity.map(function (v, i) { | ||
return v / (_this3.axisVelocity[i] || 1); | ||
}); | ||
this.axisVelocity = velocity; | ||
this.accelerationFactors.push(accelerationFactor); | ||
this.updateWillEndTimeout(deltaTime); | ||
}; | ||
_proto.updateWillEndTimeout = function updateWillEndTimeout(deltaTime) { | ||
// use current time between events rounded up and increased by a bit as timeout | ||
var newTimeout = Math.ceil(deltaTime / 10) * 10 * 1.2; // double the timeout, when momentum was not detected yet | ||
if (!this.isMomentum) { | ||
newTimeout = Math.max(100, newTimeout * 2); | ||
} | ||
this.willEndTimeout = Math.min(1000, Math.round(newTimeout)); | ||
}; | ||
WheelAnalyzer.accelerationFactorInMomentumRange = function accelerationFactorInMomentumRange(accFactor) { | ||
// when main axis is the the other one and there is no movement/change on the current one | ||
if (accFactor === 0) return true; | ||
return accFactor <= ACC_FACTOR_MAX && accFactor >= ACC_FACTOR_MIN; | ||
}; | ||
_proto.detectMomentum = function detectMomentum() { | ||
if (this.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return this.isMomentum; | ||
} | ||
var recentAccelerationFactors = this.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); // check recent acceleration / deceleration factors | ||
var detectedMomentum = recentAccelerationFactors.reduce(function (mightBeMomentum, accFac) { | ||
// all recent need to match, if any did not match -> short circuit | ||
if (!mightBeMomentum) return false; // when both axis decelerate exactly in the same rate it is very likely caused by momentum | ||
var sameAccFac = !!accFac.reduce(function (f1, f2) { | ||
return f1 && f1 < 1 && f1 === f2 ? 1 : 0; | ||
}); // check if acceleration factor is within momentum range | ||
var bothAreInRangeOrZero = accFac.filter(WheelAnalyzer.accelerationFactorInMomentumRange).length === accFac.length; // one the requirements must be fulfilled | ||
return sameAccFac || bothAreInRangeOrZero; | ||
}, true); // only keep the most recent events | ||
this.accelerationFactors = recentAccelerationFactors; | ||
if (detectedMomentum && !this.isMomentum) { | ||
this.publish(WheelPhase.WHEEL_END); | ||
this.isMomentum = true; | ||
this.publish(WheelPhase.MOMENTUM_WHEEL_START); | ||
} | ||
return this.isMomentum; | ||
}; | ||
_proto.getDebugState = function getDebugState() { | ||
var props = _extends({}, this); | ||
return props; | ||
}; | ||
_proto.getCurrentState = function getCurrentState(type) { | ||
var debugData = this.options.isDebug ? this.getDebugState() : null; | ||
return { | ||
type: type, | ||
debugData: debugData, | ||
willEndSoon: this.willEndSoon, | ||
isMomentum: this.isMomentum, | ||
axisDeltas: this.axisDeltas, | ||
axisVelocity: this.axisVelocity | ||
}; | ||
}; | ||
_proto.start = function start() { | ||
this.isStarted = true; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisDeltas = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.scrollPointsToMerge = []; | ||
this.scrollPoints = []; | ||
this.accelerationFactors = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
}; | ||
_createClass(WheelAnalyzer, [{ | ||
key: "willEndSoon", | ||
get: function get() { | ||
var absDeltas = this.scrollPoints.slice(SOON_ENDING_WHEEL_COUNT * -1).map(function (_ref5) { | ||
var currentAbsDelta = _ref5.currentAbsDelta; | ||
return currentAbsDelta; | ||
}); | ||
var absDeltaAverage = absDeltas.reduce(function (a, b) { | ||
return a + b; | ||
}, 0) / absDeltas.length; | ||
return absDeltaAverage <= SOON_ENDING_THRESHOLD; | ||
} | ||
}]); | ||
return WheelAnalyzer; | ||
}(); | ||
function EventBus(_temp) { | ||
var _ref = _temp === void 0 ? {} : _temp; | ||
_objectDestructuringEmpty(_ref); | ||
var listeners = {}; | ||
function on(type, listener) { | ||
listeners[type] = (listeners[type] || []).concat(listener); | ||
return function () { | ||
return off(type, listener); | ||
}; | ||
} | ||
function off(type, listener) { | ||
listeners[type] = (listeners[type] || []).filter(function (l) { | ||
return l !== listener; | ||
}); | ||
} | ||
function dispatch(type, data) { | ||
if (!(type in listeners)) return; | ||
listeners[type].forEach(function (l) { | ||
return l(data); | ||
}); | ||
} | ||
return Object.freeze({ | ||
on: on, | ||
off: off, | ||
dispatch: dispatch | ||
}); | ||
} | ||
export { WheelAnalyzer, WheelGestures, WheelPhase }; | ||
var WheelReason; | ||
(function (WheelReason) { | ||
WheelReason["USER"] = "USER"; | ||
WheelReason["ANY"] = "ANY"; | ||
})(WheelReason || (WheelReason = {})); | ||
var _wheelType; | ||
var wheelType = (_wheelType = {}, _wheelType[WheelReason.USER] = { | ||
start: WheelPhase.WHEEL_START, | ||
wheel: WheelPhase.WHEEL, | ||
end: WheelPhase.WHEEL_END | ||
}, _wheelType[WheelReason.ANY] = { | ||
start: WheelPhase.ANY_WHEEL_START, | ||
wheel: WheelPhase.ANY_WHEEL, | ||
end: WheelPhase.ANY_WHEEL_END | ||
}, _wheelType); | ||
function WheelGestures(_temp) { | ||
var _ref = _temp === void 0 ? {} : _temp, | ||
_ref$axis = _ref.axis, | ||
axis = _ref$axis === void 0 ? 'all' : _ref$axis, | ||
_ref$wheelReason = _ref.wheelReason, | ||
wheelReason = _ref$wheelReason === void 0 ? WheelReason.USER : _ref$wheelReason; | ||
var dragState = { | ||
down: false, | ||
delta: [0, 0], | ||
axisVelocity: [0, 0] | ||
}; | ||
var wheelAnalyzer = new WheelAnalyzer({ | ||
preventWheelAction: axis | ||
}); | ||
var observe = wheelAnalyzer.observe, | ||
unobserve = wheelAnalyzer.unobserve, | ||
disconnect = wheelAnalyzer.disconnect; | ||
var _EventBus = EventBus(), | ||
on = _EventBus.on, | ||
off = _EventBus.off, | ||
dispatch = _EventBus.dispatch; | ||
wheelAnalyzer.subscribe(function (type, data) { | ||
dragState = { | ||
down: true, | ||
// TODO: why * -1, should this not better be in analyzer or when used? | ||
delta: data.axisDeltas.map(function (d) { | ||
return d * -1; | ||
}), | ||
axisVelocity: [data.axisVelocity[0] * -1, data.axisVelocity[1] * -1] | ||
}; | ||
switch (type) { | ||
case wheelType[wheelReason].start: | ||
dragState = _extends({}, dragState, { | ||
down: true | ||
}); | ||
dispatch('wheelstart', dragState); | ||
break; | ||
case wheelType[wheelReason].wheel: | ||
dragState = _extends({}, dragState, { | ||
down: true | ||
}); | ||
dispatch('wheelmove', dragState); | ||
break; | ||
case wheelType[wheelReason].end: | ||
dragState = _extends({}, dragState, { | ||
down: false | ||
}); | ||
dispatch('wheelend', dragState); | ||
break; | ||
default: | ||
return; | ||
} | ||
}); | ||
return Object.freeze({ | ||
observe: observe, | ||
unobserve: unobserve, | ||
disconnect: disconnect, | ||
on: on, | ||
off: off | ||
}); | ||
} | ||
export { WheelAnalyzer, WheelGestures, WheelPhase, WheelReason }; | ||
//# sourceMappingURL=wheel-gestures.esm.js.map |
{ | ||
"name": "wheel-gestures", | ||
"main": "dist/wheel-gestures.cjs.js", | ||
"version": "1.0.2-beta.3", | ||
"description": "wheel gestures and momentum detection", | ||
"main": "dist/index.js", | ||
"module": "dist/wheel-gestures.esm.js", | ||
"types": "dist/wheel-gestures.d.ts", | ||
"version": "1.0.1", | ||
"description": "wheel gestures and momentum detection", | ||
"typings": "dist/index.d.ts", | ||
"files": [ | ||
"dist" | ||
], | ||
"engines": { | ||
"node": ">=10" | ||
}, | ||
"scripts": { | ||
"build": "rollup -c", | ||
"dev": "rollup -c -w", | ||
"start": "yarn dev", | ||
"test": "react-scripts test", | ||
"semantic-release": "semantic-release", | ||
"link-examples": "yarn unlink; yarn link; cd examples; yarn link wheel-gestures;" | ||
"start": "tsdx watch", | ||
"build": "tsdx build", | ||
"test": "tsdx test", | ||
"lint": "tsdx lint", | ||
"prepare": "tsdx build", | ||
"semantic-release": "semantic-release" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "tsdx lint" | ||
} | ||
}, | ||
"repository": { | ||
@@ -34,23 +45,13 @@ "type": "git", | ||
"homepage": "https://github.com/xiel/wheel-gestures#readme", | ||
"dependencies": { | ||
"throttle-debounce": "^2.1.0" | ||
}, | ||
"files": [ | ||
"dist/**/*", | ||
"!**/test/*" | ||
], | ||
"peerDependencies": {}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@testing-library/react": "^9.0.1", | ||
"@types/jest": "^24.0.11", | ||
"@types/throttle-debounce": "^2.1.0", | ||
"eslint-config-prettier": "*", | ||
"eslint-plugin-prettier": "*", | ||
"eslint-plugin-react-app": "*", | ||
"prettier": "^1.17.0", | ||
"react-scripts": "^3.2.0", | ||
"rollup": "^1.19.3", | ||
"rollup-plugin-typescript2": "^0.24.0", | ||
"tslib": "^1.9.3", | ||
"typescript": "3.6.2", | ||
"semantic-release": "^15.13.24" | ||
"@testing-library/react": "^10.0.2", | ||
"@types/jest": "^25.2.1", | ||
"bundlewatch": "^0.2.6", | ||
"husky": "^4.2.5", | ||
"tsdx": "^0.13.2", | ||
"tslib": "^1.11.1", | ||
"typescript": "^3.8.3", | ||
"semantic-release": "^17.0.4" | ||
}, | ||
@@ -65,3 +66,11 @@ "jest": { | ||
] | ||
}, | ||
"bundlewatch": { | ||
"files": [ | ||
{ | ||
"path": "dist/*.js", | ||
"maxSize": "10kB" | ||
} | ||
] | ||
} | ||
} |
@@ -5,2 +5,4 @@ # ![wheel gestures](./WheelGestures.svg) | ||
🚧 `IN DEVELOPMENT` still testing and doing heaps of breaking changes, don't use yet please :) | ||
[![npm (tag)](https://img.shields.io/npm/v/wheel-gestures/latest.svg)](https://www.npmjs.com/package/wheel-gestures) | ||
@@ -10,1 +12,8 @@ ![GitHub top language](https://img.shields.io/github/languages/top/xiel/wheel-gestures.svg) | ||
you bring the wheel events, wheelAnalyzer returns wheel gestures & momentum information | ||
**OS & Browsers**k | ||
- Mac OS (Chrome, Firefox, Safari, Brave, Edge) | ||
- Windows (testing needed & help appreciated) | ||
- Linux (testing needed & help appreciated) |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
143909
0
8
19
0
1149
18
1
1
1
- Removedthrottle-debounce@^2.1.0
- Removedthrottle-debounce@2.3.0(transitive)