wheel-gestures
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -1,2 +0,1 @@ | ||
import { WheelAnalyzer } from '../../wheel-analyzer/wheel-analyzer'; | ||
import { PhaseData, SubscribeFn, WheelEventData, WheelTypes } from '../../wheel-analyzer/wheel-analyzer-types'; | ||
@@ -15,10 +14,11 @@ export declare type Range = [number, number]; | ||
}): { | ||
wheelAnalyzer: WheelAnalyzer; | ||
allPhaseData: { | ||
type: import("../../wheel-analyzer/wheel-analyzer-types").WheelPhase; | ||
willEndSoon: boolean; | ||
isMomentum: boolean; | ||
axisMovement: number[]; | ||
axisVelocity: number[]; | ||
}[]; | ||
wheelAnalyzer: Readonly<{ | ||
observe: (target: EventTarget) => import("../../wheel-analyzer/wheel-analyzer-types").Unobserve; | ||
unobserve: (target: EventTarget) => void; | ||
disconnect: () => void; | ||
subscribe: (callback: SubscribeFn) => import("../../wheel-analyzer/wheel-analyzer-types").Unsubscribe; | ||
unsubscribe: (callback: SubscribeFn) => void; | ||
feedWheel: (wheelEvents: WheelEventData | WheelEventData[]) => void; | ||
}>; | ||
allPhaseData: PhaseData[]; | ||
}; | ||
@@ -25,0 +25,0 @@ export declare function recordPhases(wheelEvents: WheelEventData[]): { |
export declare function lastOf<T>(array: T[]): T; | ||
export declare function firstOf<T>(array: T[]): T; | ||
export declare function average(numbers: number[]): number; |
@@ -1,2 +0,21 @@ | ||
import { WheelAnalyzer } from './wheel-analyzer'; | ||
export declare type Vector3D = number[]; | ||
export interface WheelAnalyzerState { | ||
isStarted: boolean; | ||
isStartPublished: boolean; | ||
isMomentum: boolean; | ||
lastAbsDelta: number; | ||
axisMovement: Vector3D; | ||
axisVelocity: Vector3D; | ||
accelerationFactors: number[][]; | ||
scrollPoints: ScrollPoint[]; | ||
scrollPointsToMerge: ScrollPoint[]; | ||
willEndTimeout: number; | ||
} | ||
export interface PhaseData { | ||
type: WheelPhase; | ||
isMomentum: boolean; | ||
willEndSoon: boolean; | ||
axisMovement: Vector3D; | ||
axisVelocity: Vector3D; | ||
} | ||
export interface ScrollPoint { | ||
@@ -23,3 +42,2 @@ currentAbsDelta: number; | ||
} | ||
export declare type PhaseData = ReturnType<typeof WheelAnalyzer.prototype.getCurrentState>; | ||
export declare type SubscribeFn = (type: WheelPhase, data: PhaseData) => void; | ||
@@ -26,0 +44,0 @@ export declare type Unsubscribe = () => void; |
@@ -1,20 +0,6 @@ | ||
import { PreventWheelActionType, SubscribeFn, Unobserve, Unsubscribe, WheelEventData, WheelPhase } from './wheel-analyzer-types'; | ||
import { PreventWheelActionType, SubscribeFn, Unobserve, Unsubscribe, WheelEventData } from './wheel-analyzer-types'; | ||
export interface Options { | ||
preventWheelAction: PreventWheelActionType; | ||
} | ||
export declare class WheelAnalyzer { | ||
private isStarted; | ||
private isStartPublished; | ||
private isMomentum; | ||
private lastAbsDelta; | ||
private axisMovement; | ||
private axisVelocity; | ||
private accelerationFactors; | ||
private scrollPoints; | ||
private scrollPointsToMerge; | ||
private subscriptions; | ||
private targets; | ||
private options; | ||
private willEndTimeout; | ||
constructor(options?: Partial<Options>); | ||
export declare function WheelAnalyzer(optionsParam?: Partial<Options>): Readonly<{ | ||
observe: (target: EventTarget) => Unobserve; | ||
@@ -25,23 +11,3 @@ unobserve: (target: EventTarget) => void; | ||
unsubscribe: (callback: SubscribeFn) => void; | ||
private publish; | ||
feedWheel: (wheelEvents: WheelEventData | WheelEventData[]) => void; | ||
private shouldPreventDefault; | ||
private clampDelta; | ||
private processWheelEventData; | ||
private updateStartVelocity; | ||
private updateVelocity; | ||
private updateWillEndTimeout; | ||
private static accelerationFactorInMomentumRange; | ||
private detectMomentum; | ||
getCurrentState(type: WheelPhase): { | ||
type: WheelPhase; | ||
willEndSoon: boolean; | ||
isMomentum: boolean; | ||
axisMovement: number[]; | ||
axisVelocity: number[]; | ||
}; | ||
private start; | ||
private willEnd; | ||
private end; | ||
private get willEndSoon(); | ||
} | ||
}>; |
@@ -5,18 +5,2 @@ 'use strict'; | ||
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); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _extends() { | ||
@@ -70,2 +54,8 @@ _extends = Object.assign || function (target) { | ||
function average(numbers) { | ||
return numbers.reduce(function (a, b) { | ||
return a + b; | ||
}) / numbers.length; | ||
} | ||
var LINE_HEIGHT = 16 * 1.125; | ||
@@ -87,2 +77,22 @@ var PAGE_HEIGHT = typeof window !== 'undefined' && window.innerHeight || 800; | ||
/** | ||
* the timeout is automatically adjusted during a gesture | ||
* the initial timeout period is pretty long, so even old mouses, which emit wheel events less often, can produce a continuous gesture | ||
*/ | ||
var WILL_END_TIMEOUT_DEFAULT = 400; | ||
function createWheelAnalyzerState() { | ||
return { | ||
isStarted: false, | ||
isStartPublished: false, | ||
isMomentum: false, | ||
lastAbsDelta: Infinity, | ||
axisMovement: [0, 0], | ||
axisVelocity: [0, 0], | ||
accelerationFactors: [], | ||
scrollPoints: [], | ||
scrollPointsToMerge: [], | ||
willEndTimeout: WILL_END_TIMEOUT_DEFAULT | ||
}; | ||
} | ||
(function (WheelPhase) { | ||
@@ -104,10 +114,3 @@ WheelPhase["ANY_WHEEL_START"] = "ANY_WHEEL_START"; | ||
var ACC_FACTOR_MAX = 0.96; | ||
var DELTA_MAX_ABS = 150; // TODO: test next release | ||
/** | ||
* the timeout is automatically adjusted during a gesture | ||
* the initial timeout period is pretty long, so even old mouses, which emit wheel events less often, can produce a continuous gesture | ||
*/ | ||
var WILL_END_TIMEOUT_DEFAULT = 400; | ||
var DELTA_MAX_ABS = 150; | ||
var axes = ['x', 'y']; | ||
@@ -124,125 +127,84 @@ var deltaProp = { | ||
}; | ||
var WheelAnalyzer = /*#__PURE__*/function () { | ||
function WheelAnalyzer(options) { | ||
var _this = this; | ||
function WheelAnalyzer(optionsParam) { | ||
if (optionsParam === void 0) { | ||
optionsParam = {}; | ||
} | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var state = createWheelAnalyzerState(); | ||
var subscriptions = []; | ||
var targets = []; // merge passed options with defaults (filter undefined option values) | ||
this.isStarted = false; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisMovement = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.accelerationFactors = []; | ||
this.scrollPoints = []; | ||
this.scrollPointsToMerge = []; | ||
this.subscriptions = []; | ||
this.targets = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
var options = Object.entries(optionsParam).filter(function (_ref) { | ||
var value = _ref[1]; | ||
return value !== undefined; | ||
}).reduce(function (o, _ref2) { | ||
var _Object$assign; | ||
this.observe = function (target) { | ||
// TODO: need to test if passive supported? might throw error otherwise in older browsers? | ||
target.addEventListener('wheel', _this.feedWheel, { | ||
passive: false | ||
}); | ||
var key = _ref2[0], | ||
value = _ref2[1]; | ||
return Object.assign(o, (_Object$assign = {}, _Object$assign[key] = value, _Object$assign)); | ||
}, _extends({}, defaults)); | ||
_this.targets.push(target); | ||
return _this.unobserve.bind(_this, target); | ||
var observe = function observe(target) { | ||
target.addEventListener('wheel', feedWheel, { | ||
passive: false | ||
}); | ||
targets.push(target); | ||
return function () { | ||
return unobserve(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. | ||
var unobserve = function unobserve(target) { | ||
target.removeEventListener('wheel', feedWheel); | ||
targets = 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); | ||
}; | ||
var disconnect = function disconnect() { | ||
targets.forEach(unobserve); | ||
}; | ||
this.subscribe = function (callback) { | ||
_this.subscriptions.push(callback); // return bound unsubscribe | ||
var subscribe = function subscribe(callback) { | ||
subscriptions.push(callback); // return bound unsubscribe | ||
return _this.unsubscribe.bind(_this, callback); | ||
return function () { | ||
return unsubscribe(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; | ||
}); | ||
var unsubscribe = function unsubscribe(callback) { | ||
if (!callback) throw new Error('please pass the callback which was used to subscribe'); | ||
subscriptions = subscriptions.filter(function (s) { | ||
return s !== callback; | ||
}); | ||
}; | ||
var publish = function publish(type) { | ||
var data = { | ||
type: type, | ||
willEndSoon: willEndSoon(), | ||
isMomentum: state.isMomentum, | ||
axisMovement: state.axisMovement, | ||
axisVelocity: state.axisVelocity | ||
}; | ||
subscriptions.forEach(function (fn) { | ||
return fn(type, data); | ||
}); | ||
}; | ||
this.publish = function (phase, data) { | ||
if (data === void 0) { | ||
data = _this.getCurrentState(phase); | ||
} | ||
_this.subscriptions.forEach(function (fn) { | ||
return fn(phase, data); | ||
var feedWheel = function feedWheel(wheelEvents) { | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { | ||
return processWheelEventData(wheelEvent); | ||
}); | ||
}; | ||
} else { | ||
processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
this.feedWheel = function (wheelEvents) { | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { | ||
return _this.processWheelEventData(wheelEvent); | ||
}); | ||
} else { | ||
_this.processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
this.willEnd = function () { | ||
var willEndId; | ||
return function () { | ||
clearTimeout(willEndId); | ||
willEndId = setTimeout(_this.end, _this.willEndTimeout); | ||
}; | ||
}(); | ||
this.end = function () { | ||
if (!_this.isStarted) return; | ||
if (_this.isMomentum) { | ||
if (!_this.willEndSoon) { | ||
_this.publish(exports.WheelPhase.MOMENTUM_WHEEL_CANCEL); | ||
} else { | ||
_this.publish(exports.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(exports.WheelPhase.WHEEL_END); | ||
} | ||
_this.publish(exports.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 shouldPreventDefault = function shouldPreventDefault(e) { | ||
var preventWheelAction = options.preventWheelAction; | ||
var deltaX = e.deltaX, | ||
@@ -265,32 +227,30 @@ deltaY = e.deltaY; | ||
_proto.clampDelta = function clampDelta(delta) { | ||
var 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 processWheelEventData = function processWheelEventData(wheelEvent) { | ||
var normalizedWheel = normalizeWheel(wheelEvent); | ||
if (wheelEvent.preventDefault && this.shouldPreventDefault(wheelEvent)) { | ||
if (wheelEvent.preventDefault && shouldPreventDefault(wheelEvent)) { | ||
wheelEvent.preventDefault(); | ||
} | ||
if (!this.isStarted) { | ||
this.start(); | ||
if (!state.isStarted) { | ||
start(); | ||
} | ||
var currentDelta = this.clampDelta(Math.abs(normalizedWheel.deltaY) > Math.abs(normalizedWheel.deltaX) ? normalizedWheel.deltaY : normalizedWheel.deltaX); | ||
var currentDelta = 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(); | ||
if (state.isMomentum && currentAbsDelta > state.lastAbsDelta) { | ||
end(); | ||
start(); | ||
} | ||
this.axisMovement = this.axisMovement.map(function (prevDelta, i) { | ||
return prevDelta + _this2.clampDelta(normalizedWheel[deltaProp[axes[i]]]); | ||
state.axisMovement = state.axisMovement.map(function (prevDelta, i) { | ||
return prevDelta + clampDelta(normalizedWheel[deltaProp[axes[i]]]); | ||
}); | ||
this.lastAbsDelta = currentAbsDelta; | ||
this.scrollPointsToMerge.push({ | ||
state.lastAbsDelta = currentAbsDelta; | ||
state.scrollPointsToMerge.push({ | ||
currentAbsDelta: currentAbsDelta, | ||
@@ -301,8 +261,10 @@ axisDeltaUnclampt: [normalizedWheel.deltaX, normalizedWheel.deltaY], | ||
if (this.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
if (state.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
var _state = state, | ||
scrollPointsToMerge = _state.scrollPointsToMerge; | ||
var mergedScrollPoint = { | ||
currentAbsDelta: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.currentAbsDelta; | ||
}, 0) / WHEELEVENTS_TO_MERGE, | ||
axisDeltaUnclampt: this.scrollPointsToMerge.reduce(function (_ref3, _ref4) { | ||
currentAbsDelta: average(scrollPointsToMerge.map(function (b) { | ||
return b.currentAbsDelta; | ||
})), | ||
axisDeltaUnclampt: scrollPointsToMerge.reduce(function (_ref3, _ref4) { | ||
var sumX = _ref3[0], | ||
@@ -315,49 +277,47 @@ sumY = _ref3[1]; | ||
}, [0, 0]), | ||
timestamp: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.timestamp; | ||
}, 0) / WHEELEVENTS_TO_MERGE | ||
timestamp: average(scrollPointsToMerge.map(function (b) { | ||
return b.timestamp; | ||
})) | ||
}; | ||
this.scrollPoints.push(mergedScrollPoint); // only update velocity after a merged scrollpoint was generated | ||
state.scrollPoints.push(mergedScrollPoint); // only update velocity after a merged scrollpoint was generated | ||
this.updateVelocity(); | ||
updateVelocity(); | ||
if (!this.isMomentum) { | ||
this.detectMomentum(); | ||
if (!state.isMomentum) { | ||
detectMomentum(); | ||
} // reset merge array | ||
this.scrollPointsToMerge = []; | ||
state.scrollPointsToMerge = []; | ||
} | ||
if (!this.scrollPoints.length) { | ||
this.updateStartVelocity(); | ||
if (!state.scrollPoints.length) { | ||
updateStartVelocity(); | ||
} // publish start after velocity etc. have been updated | ||
if (!this.isStartPublished) { | ||
this.publish(exports.WheelPhase.ANY_WHEEL_START); | ||
this.publish(exports.WheelPhase.WHEEL_START); | ||
this.isStartPublished = true; | ||
if (!state.isStartPublished) { | ||
publish(exports.WheelPhase.ANY_WHEEL_START); | ||
publish(exports.WheelPhase.WHEEL_START); | ||
state.isStartPublished = true; | ||
} | ||
this.publish(exports.WheelPhase.ANY_WHEEL); | ||
this.publish(this.isMomentum ? exports.WheelPhase.MOMENTUM_WHEEL : exports.WheelPhase.WHEEL); // calc debounced end function, to recognize end of wheel event stream | ||
publish(exports.WheelPhase.ANY_WHEEL); | ||
publish(state.isMomentum ? exports.WheelPhase.MOMENTUM_WHEEL : exports.WheelPhase.WHEEL); // calc debounced end function, to recognize end of wheel event stream | ||
this.willEnd(); | ||
willEnd(); | ||
}; | ||
_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 updateStartVelocity = function updateStartVelocity() { | ||
var latestScrollPoint = state.scrollPointsToMerge[state.scrollPointsToMerge.length - 1]; | ||
state.axisVelocity = latestScrollPoint.axisDeltaUnclampt.map(function (d) { | ||
return d / state.willEndTimeout; | ||
}); | ||
}; | ||
_proto.updateVelocity = function updateVelocity() { | ||
var _this3 = this; | ||
var updateVelocity = function updateVelocity() { | ||
// 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]; | ||
var _state$scrollPoints$s = state.scrollPoints.slice(-2), | ||
pA = _state$scrollPoints$s[0], | ||
pB = _state$scrollPoints$s[1]; | ||
@@ -382,21 +342,21 @@ if (!pA || !pB) { | ||
var accelerationFactor = velocity.map(function (v, i) { | ||
return v / (_this3.axisVelocity[i] || 1); | ||
return v / (state.axisVelocity[i] || 1); | ||
}); | ||
this.axisVelocity = velocity; | ||
this.accelerationFactors.push(accelerationFactor); | ||
this.updateWillEndTimeout(deltaTime); | ||
state.axisVelocity = velocity; | ||
state.accelerationFactors.push(accelerationFactor); | ||
updateWillEndTimeout(deltaTime); | ||
}; | ||
_proto.updateWillEndTimeout = function updateWillEndTimeout(deltaTime) { | ||
var 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) { | ||
if (!state.isMomentum) { | ||
newTimeout = Math.max(100, newTimeout * 2); | ||
} | ||
this.willEndTimeout = Math.min(1000, Math.round(newTimeout)); | ||
state.willEndTimeout = Math.min(1000, Math.round(newTimeout)); | ||
}; | ||
WheelAnalyzer.accelerationFactorInMomentumRange = function accelerationFactorInMomentumRange(accFactor) { | ||
var accelerationFactorInMomentumRange = function accelerationFactorInMomentumRange(accFactor) { | ||
// when main axis is the the other one and there is no movement/change on the current one | ||
@@ -407,8 +367,8 @@ if (accFactor === 0) return true; | ||
_proto.detectMomentum = function detectMomentum() { | ||
if (this.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return this.isMomentum; | ||
var detectMomentum = function detectMomentum() { | ||
if (state.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return state.isMomentum; | ||
} | ||
var recentAccelerationFactors = this.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); // check recent acceleration / deceleration factors | ||
var recentAccelerationFactors = state.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); // check recent acceleration / deceleration factors | ||
@@ -423,3 +383,3 @@ var detectedMomentum = recentAccelerationFactors.reduce(function (mightBeMomentum, accFac) { | ||
var bothAreInRangeOrZero = accFac.filter(WheelAnalyzer.accelerationFactorInMomentumRange).length === accFac.length; // one the requirements must be fulfilled | ||
var bothAreInRangeOrZero = accFac.filter(accelerationFactorInMomentumRange).length === accFac.length; // one the requirements must be fulfilled | ||
@@ -429,53 +389,66 @@ return sameAccFac || bothAreInRangeOrZero; | ||
this.accelerationFactors = recentAccelerationFactors; | ||
state.accelerationFactors = recentAccelerationFactors; | ||
if (detectedMomentum && !this.isMomentum) { | ||
this.publish(exports.WheelPhase.WHEEL_END); | ||
this.isMomentum = true; | ||
this.publish(exports.WheelPhase.MOMENTUM_WHEEL_START); | ||
if (detectedMomentum && !state.isMomentum) { | ||
publish(exports.WheelPhase.WHEEL_END); | ||
state.isMomentum = true; | ||
publish(exports.WheelPhase.MOMENTUM_WHEEL_START); | ||
} | ||
return this.isMomentum; | ||
return state.isMomentum; | ||
}; | ||
_proto.getCurrentState = function getCurrentState(type) { | ||
return { | ||
type: type, | ||
willEndSoon: this.willEndSoon, | ||
isMomentum: this.isMomentum, | ||
axisMovement: this.axisMovement, | ||
axisVelocity: this.axisVelocity | ||
var start = function start() { | ||
state = createWheelAnalyzerState(); | ||
state.isStarted = true; | ||
}; | ||
var willEnd = function () { | ||
var willEndId; | ||
return function () { | ||
clearTimeout(willEndId); | ||
willEndId = setTimeout(end, state.willEndTimeout); | ||
}; | ||
}(); | ||
var end = function end() { | ||
if (!state.isStarted) return; | ||
if (state.isMomentum) { | ||
if (!willEndSoon()) { | ||
publish(exports.WheelPhase.MOMENTUM_WHEEL_CANCEL); | ||
} else { | ||
publish(exports.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 | ||
publish(exports.WheelPhase.WHEEL_END); | ||
} | ||
publish(exports.WheelPhase.ANY_WHEEL_END); | ||
state.isMomentum = false; | ||
state.isStarted = false; | ||
}; | ||
_proto.start = function start() { | ||
this.isStarted = true; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisMovement = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.scrollPointsToMerge = []; | ||
this.scrollPoints = []; | ||
this.accelerationFactors = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
var willEndSoon = function willEndSoon() { | ||
var absDeltas = state.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; | ||
}; | ||
_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 Object.freeze({ | ||
observe: observe, | ||
unobserve: unobserve, | ||
disconnect: disconnect, | ||
subscribe: subscribe, | ||
unsubscribe: unsubscribe, | ||
feedWheel: feedWheel | ||
}); | ||
} | ||
return WheelAnalyzer; | ||
}(); | ||
(function (WheelReason) { | ||
@@ -508,3 +481,3 @@ WheelReason["USER"] = "USER"; | ||
}; | ||
var wheelAnalyzer = new WheelAnalyzer({ | ||
var wheelAnalyzer = WheelAnalyzer({ | ||
preventWheelAction: axis | ||
@@ -511,0 +484,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
"use strict";function e(){return(e=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var s=arguments[t];for(var i in s)Object.prototype.hasOwnProperty.call(s,i)&&(e[i]=s[i])}return e}).apply(this,arguments)}Object.defineProperty(exports,"__esModule",{value:!0});var t,s=[1,18,"undefined"!=typeof window&&window.innerHeight||800];(t=exports.WheelPhase||(exports.WheelPhase={})).ANY_WHEEL_START="ANY_WHEEL_START",t.ANY_WHEEL="ANY_WHEEL",t.ANY_WHEEL_END="ANY_WHEEL_END",t.WHEEL_START="WHEEL_START",t.WHEEL="WHEEL",t.WHEEL_END="WHEEL_END",t.MOMENTUM_WHEEL_START="MOMENTUM_WHEEL_START",t.MOMENTUM_WHEEL="MOMENTUM_WHEEL",t.MOMENTUM_WHEEL_CANCEL="MOMENTUM_WHEEL_CANCEL",t.MOMENTUM_WHEEL_END="MOMENTUM_WHEEL_END";var i,n,r=["x","y"],o={x:"deltaX",y:"deltaY"},a={preventWheelAction:"all"},l=function(){function t(t){var s,i=this;void 0===t&&(t={}),this.isStarted=!1,this.isStartPublished=!1,this.isMomentum=!1,this.lastAbsDelta=Infinity,this.axisMovement=[0,0],this.axisVelocity=[0,0],this.accelerationFactors=[],this.scrollPoints=[],this.scrollPointsToMerge=[],this.subscriptions=[],this.targets=[],this.willEndTimeout=400,this.observe=function(e){return e.addEventListener("wheel",i.feedWheel,{passive:!1}),i.targets.push(e),i.unobserve.bind(i,e)},this.unobserve=function(e){e.removeEventListener("wheel",i.feedWheel),i.targets=i.targets.filter((function(t){return t!==e}))},this.disconnect=function(){i.targets.forEach(i.unobserve)},this.subscribe=function(e){return i.subscriptions.push(e),i.unsubscribe.bind(i,e)},this.unsubscribe=function(e){if(!e)throw new Error("please pass the callback which was used to subscribe");i.subscriptions=i.subscriptions.filter((function(t){return t!==e}))},this.publish=function(e,t){void 0===t&&(t=i.getCurrentState(e)),i.subscriptions.forEach((function(s){return s(e,t)}))},this.feedWheel=function(e){Array.isArray(e)?e.forEach((function(e){return i.processWheelEventData(e)})):i.processWheelEventData(e)},this.willEnd=function(){clearTimeout(s),s=setTimeout(i.end,i.willEndTimeout)},this.end=function(){i.isStarted&&(i.publish(i.isMomentum?i.willEndSoon?exports.WheelPhase.MOMENTUM_WHEEL_END:exports.WheelPhase.MOMENTUM_WHEEL_CANCEL:exports.WheelPhase.WHEEL_END),i.publish(exports.WheelPhase.ANY_WHEEL_END),i.isMomentum=!1,i.isStarted=!1)},this.options=Object.entries(t).filter((function(e){return void 0!==e[1]})).reduce((function(e,t){var s;return Object.assign(e,((s={})[t[0]]=t[1],s))}),e({},a))}var i,n=t.prototype;return n.shouldPreventDefault=function(e){var t=e.deltaX,s=e.deltaY;switch(this.options.preventWheelAction){case"all":return!0;case"x":return Math.abs(t)>=Math.abs(s);case"y":return Math.abs(s)>=Math.abs(t)}},n.clampDelta=function(e){return Math.min(150,Math.max(-150,e))},n.processWheelEventData=function(e){var t,i=this,n={deltaX:(t=e).deltaX*s[t.deltaMode],deltaY:t.deltaY*s[t.deltaMode],deltaZ:(t.deltaZ||0)*s[t.deltaMode],deltaMode:0,timestamp:t.timeStamp};e.preventDefault&&this.shouldPreventDefault(e)&&e.preventDefault(),this.isStarted||this.start();var a=this.clampDelta(Math.abs(n.deltaY)>Math.abs(n.deltaX)?n.deltaY:n.deltaX),l=Math.abs(a);if(this.isMomentum&&l>this.lastAbsDelta&&(this.end(),this.start()),this.axisMovement=this.axisMovement.map((function(e,t){return e+i.clampDelta(n[o[r[t]]])})),this.lastAbsDelta=l,this.scrollPointsToMerge.push({currentAbsDelta:l,axisDeltaUnclampt:[n.deltaX,n.deltaY],timestamp:e.timeStamp}),2===this.scrollPointsToMerge.length){var h={currentAbsDelta:this.scrollPointsToMerge.reduce((function(e,t){return e+t.currentAbsDelta}),0)/2,axisDeltaUnclampt:this.scrollPointsToMerge.reduce((function(e,t){var s=t.axisDeltaUnclampt;return[e[0]+s[0],e[1]+s[1]]}),[0,0]),timestamp:this.scrollPointsToMerge.reduce((function(e,t){return e+t.timestamp}),0)/2};this.scrollPoints.push(h),this.updateVelocity(),this.isMomentum||this.detectMomentum(),this.scrollPointsToMerge=[]}this.scrollPoints.length||this.updateStartVelocity(),this.isStartPublished||(this.publish(exports.WheelPhase.ANY_WHEEL_START),this.publish(exports.WheelPhase.WHEEL_START),this.isStartPublished=!0),this.publish(exports.WheelPhase.ANY_WHEEL),this.publish(this.isMomentum?exports.WheelPhase.MOMENTUM_WHEEL:exports.WheelPhase.WHEEL),this.willEnd()},n.updateStartVelocity=function(){this.axisVelocity=this.scrollPointsToMerge[this.scrollPointsToMerge.length-1].axisDeltaUnclampt.map((function(e){return e/400}))},n.updateVelocity=function(){var e=this,t=this.scrollPoints.slice(-2),s=t[0],i=t[1];if(s&&i){var n=i.timestamp-s.timestamp;if(!(n<=0)){var r=i.axisDeltaUnclampt.map((function(e){return e/n})),o=r.map((function(t,s){return t/(e.axisVelocity[s]||1)}));this.axisVelocity=r,this.accelerationFactors.push(o),this.updateWillEndTimeout(n)}}},n.updateWillEndTimeout=function(e){var t=10*Math.ceil(e/10)*1.2;this.isMomentum||(t=Math.max(100,2*t)),this.willEndTimeout=Math.min(1e3,Math.round(t))},t.accelerationFactorInMomentumRange=function(e){return 0===e||e<=.96&&e>=.6},n.detectMomentum=function(){if(this.accelerationFactors.length<5)return this.isMomentum;var e=this.accelerationFactors.slice(-5),s=e.reduce((function(e,s){if(!e)return!1;var i=!!s.reduce((function(e,t){return e&&e<1&&e===t?1:0})),n=s.filter(t.accelerationFactorInMomentumRange).length===s.length;return i||n}),!0);return this.accelerationFactors=e,s&&!this.isMomentum&&(this.publish(exports.WheelPhase.WHEEL_END),this.isMomentum=!0,this.publish(exports.WheelPhase.MOMENTUM_WHEEL_START)),this.isMomentum},n.getCurrentState=function(e){return{type:e,willEndSoon:this.willEndSoon,isMomentum:this.isMomentum,axisMovement:this.axisMovement,axisVelocity:this.axisVelocity}},n.start=function(){this.isStarted=!0,this.isStartPublished=!1,this.isMomentum=!1,this.lastAbsDelta=Infinity,this.axisMovement=[0,0],this.axisVelocity=[0,0],this.scrollPointsToMerge=[],this.scrollPoints=[],this.accelerationFactors=[],this.willEndTimeout=400},(i=[{key:"willEndSoon",get:function(){var e=this.scrollPoints.slice(-3).map((function(e){return e.currentAbsDelta}));return e.reduce((function(e,t){return e+t}),0)/e.length<=1.4}}])&&function(e,t){for(var s=0;s<t.length;s++){var i=t[s];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}(t.prototype,i),t}();(i=exports.WheelReason||(exports.WheelReason={})).USER="USER",i.ANY="ANY";var h=((n={})[exports.WheelReason.USER]={start:exports.WheelPhase.WHEEL_START,wheel:exports.WheelPhase.WHEEL,end:exports.WheelPhase.WHEEL_END},n[exports.WheelReason.ANY]={start:exports.WheelPhase.ANY_WHEEL_START,wheel:exports.WheelPhase.ANY_WHEEL,end:exports.WheelPhase.ANY_WHEEL_END},n);exports.WheelAnalyzer=l,exports.WheelGestures=function(t){var s=void 0===t?{}:t,i=s.axis,n=s.wheelReason,r=void 0===n?exports.WheelReason.USER:n,o={down:!1,axisMovement:[0,0],axisVelocity:[0,0]},a=new l({preventWheelAction:void 0===i?"all":i}),u=a.observe,c=a.unobserve,E=a.disconnect,d=function(){var e={};function t(t,s){e[t]=(e[t]||[]).filter((function(e){return e!==s}))}return Object.freeze({on:function(s,i){return e[s]=(e[s]||[]).concat(i),function(){return t(s,i)}},off:t,dispatch:function(t,s){t in e&&e[t].forEach((function(e){return e(s)}))}})}(),p=d.on,f=d.off,m=d.dispatch;return a.subscribe((function(t,s){switch(o={down:!0,axisMovement:s.axisMovement.map((function(e){return-1*e})),axisVelocity:[-1*s.axisVelocity[0],-1*s.axisVelocity[1]]},t){case h[r].start:o=e({},o,{down:!0}),m("wheelstart",o);break;case h[r].wheel:o=e({},o,{down:!0}),m("wheelmove",o);break;case h[r].end:o=e({},o,{down:!1}),m("wheelend",o);break;default:return}})),Object.freeze({observe:u,unobserve:c,disconnect:E,on:p,off:f})}; | ||
"use strict";function e(){return(e=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e}).apply(this,arguments)}function t(e){return e.reduce((function(e,t){return e+t}))/e.length}Object.defineProperty(exports,"__esModule",{value:!0});var n,r=[1,18,"undefined"!=typeof window&&window.innerHeight||800];(n=exports.WheelPhase||(exports.WheelPhase={})).ANY_WHEEL_START="ANY_WHEEL_START",n.ANY_WHEEL="ANY_WHEEL",n.ANY_WHEEL_END="ANY_WHEEL_END",n.WHEEL_START="WHEEL_START",n.WHEEL="WHEEL",n.WHEEL_END="WHEEL_END",n.MOMENTUM_WHEEL_START="MOMENTUM_WHEEL_START",n.MOMENTUM_WHEEL="MOMENTUM_WHEEL",n.MOMENTUM_WHEEL_CANCEL="MOMENTUM_WHEEL_CANCEL",n.MOMENTUM_WHEEL_END="MOMENTUM_WHEEL_END";var o,s,a=["x","y"],i={x:"deltaX",y:"deltaY"},l={preventWheelAction:"all"};function c(n){void 0===n&&(n={});var o,s={isStarted:!1,isStartPublished:!1,isMomentum:!1,lastAbsDelta:Infinity,axisMovement:[0,0],axisVelocity:[0,0],accelerationFactors:[],scrollPoints:[],scrollPointsToMerge:[],willEndTimeout:400},c=[],u=[],E=Object.entries(n).filter((function(e){return void 0!==e[1]})).reduce((function(e,t){var n;return Object.assign(e,((n={})[t[0]]=t[1],n))}),e({},l)),h=function(e){e.removeEventListener("wheel",m),u=u.filter((function(t){return t!==e}))},f=function(e){if(!e)throw new Error("please pass the callback which was used to subscribe");c=c.filter((function(t){return t!==e}))},M=function(e){var t={type:e,willEndSoon:N(),isMomentum:s.isMomentum,axisMovement:s.axisMovement,axisVelocity:s.axisVelocity};c.forEach((function(n){return n(e,t)}))},m=function(e){Array.isArray(e)?e.forEach((function(e){return p(e)})):p(e)},d=function(e){return Math.min(150,Math.max(-150,e))},p=function(e){var n,o={deltaX:(n=e).deltaX*r[n.deltaMode],deltaY:n.deltaY*r[n.deltaMode],deltaZ:(n.deltaZ||0)*r[n.deltaMode],deltaMode:0,timestamp:n.timeStamp};e.preventDefault&&function(e){var t=e.deltaX,n=e.deltaY;switch(E.preventWheelAction){case"all":return!0;case"x":return Math.abs(t)>=Math.abs(n);case"y":return Math.abs(n)>=Math.abs(t)}}(e)&&e.preventDefault(),s.isStarted||b();var l=d(Math.abs(o.deltaY)>Math.abs(o.deltaX)?o.deltaY:o.deltaX),c=Math.abs(l);if(s.isMomentum&&c>s.lastAbsDelta&&(L(),b()),s.axisMovement=s.axisMovement.map((function(e,t){return e+d(o[i[a[t]]])})),s.lastAbsDelta=c,s.scrollPointsToMerge.push({currentAbsDelta:c,axisDeltaUnclampt:[o.deltaX,o.deltaY],timestamp:e.timeStamp}),2===s.scrollPointsToMerge.length){var u=s.scrollPointsToMerge,h={currentAbsDelta:t(u.map((function(e){return e.currentAbsDelta}))),axisDeltaUnclampt:u.reduce((function(e,t){var n=t.axisDeltaUnclampt;return[e[0]+n[0],e[1]+n[1]]}),[0,0]),timestamp:t(u.map((function(e){return e.timestamp})))};s.scrollPoints.push(h),x(),s.isMomentum||T(),s.scrollPointsToMerge=[]}s.scrollPoints.length||W(),s.isStartPublished||(M(exports.WheelPhase.ANY_WHEEL_START),M(exports.WheelPhase.WHEEL_START),s.isStartPublished=!0),M(exports.WheelPhase.ANY_WHEEL),M(s.isMomentum?exports.WheelPhase.MOMENTUM_WHEEL:exports.WheelPhase.WHEEL),A()},W=function(){s.axisVelocity=s.scrollPointsToMerge[s.scrollPointsToMerge.length-1].axisDeltaUnclampt.map((function(e){return e/s.willEndTimeout}))},x=function(){var e=s.scrollPoints.slice(-2),t=e[0],n=e[1];if(t&&n){var r=n.timestamp-t.timestamp;if(!(r<=0)){var o=n.axisDeltaUnclampt.map((function(e){return e/r})),a=o.map((function(e,t){return e/(s.axisVelocity[t]||1)}));s.axisVelocity=o,s.accelerationFactors.push(a),v(r)}}},v=function(e){var t=10*Math.ceil(e/10)*1.2;s.isMomentum||(t=Math.max(100,2*t)),s.willEndTimeout=Math.min(1e3,Math.round(t))},_=function(e){return 0===e||e<=.96&&e>=.6},T=function(){if(s.accelerationFactors.length<5)return s.isMomentum;var e=s.accelerationFactors.slice(-5),t=e.reduce((function(e,t){if(!e)return!1;var n=!!t.reduce((function(e,t){return e&&e<1&&e===t?1:0})),r=t.filter(_).length===t.length;return n||r}),!0);return s.accelerationFactors=e,t&&!s.isMomentum&&(M(exports.WheelPhase.WHEEL_END),s.isMomentum=!0,M(exports.WheelPhase.MOMENTUM_WHEEL_START)),s.isMomentum},b=function(){(s={isStarted:!1,isStartPublished:!1,isMomentum:!1,lastAbsDelta:Infinity,axisMovement:[0,0],axisVelocity:[0,0],accelerationFactors:[],scrollPoints:[],scrollPointsToMerge:[],willEndTimeout:400}).isStarted=!0},A=function(){clearTimeout(o),o=setTimeout(L,s.willEndTimeout)},L=function(){s.isStarted&&(s.isMomentum?N()?M(exports.WheelPhase.MOMENTUM_WHEEL_END):M(exports.WheelPhase.MOMENTUM_WHEEL_CANCEL):M(exports.WheelPhase.WHEEL_END),M(exports.WheelPhase.ANY_WHEEL_END),s.isMomentum=!1,s.isStarted=!1)},N=function(){var e=s.scrollPoints.slice(-3).map((function(e){return e.currentAbsDelta}));return e.reduce((function(e,t){return e+t}),0)/e.length<=1.4};return Object.freeze({observe:function(e){return e.addEventListener("wheel",m,{passive:!1}),u.push(e),function(){return h(e)}},unobserve:h,disconnect:function(){u.forEach(h)},subscribe:function(e){return c.push(e),function(){return f(e)}},unsubscribe:f,feedWheel:m})}(o=exports.WheelReason||(exports.WheelReason={})).USER="USER",o.ANY="ANY";var u=((s={})[exports.WheelReason.USER]={start:exports.WheelPhase.WHEEL_START,wheel:exports.WheelPhase.WHEEL,end:exports.WheelPhase.WHEEL_END},s[exports.WheelReason.ANY]={start:exports.WheelPhase.ANY_WHEEL_START,wheel:exports.WheelPhase.ANY_WHEEL,end:exports.WheelPhase.ANY_WHEEL_END},s);exports.WheelAnalyzer=c,exports.WheelGestures=function(t){var n=void 0===t?{}:t,r=n.axis,o=n.wheelReason,s=void 0===o?exports.WheelReason.USER:o,a={down:!1,axisMovement:[0,0],axisVelocity:[0,0]},i=c({preventWheelAction:void 0===r?"all":r}),l=i.observe,E=i.unobserve,h=i.disconnect,f=function(){var e={};function t(t,n){e[t]=(e[t]||[]).filter((function(e){return e!==n}))}return Object.freeze({on:function(n,r){return e[n]=(e[n]||[]).concat(r),function(){return t(n,r)}},off:t,dispatch:function(t,n){t in e&&e[t].forEach((function(e){return e(n)}))}})}(),M=f.on,m=f.off,d=f.dispatch;return i.subscribe((function(t,n){switch(a={down:!0,axisMovement:n.axisMovement.map((function(e){return-1*e})),axisVelocity:[-1*n.axisVelocity[0],-1*n.axisVelocity[1]]},t){case u[s].start:a=e({},a,{down:!0}),d("wheelstart",a);break;case u[s].wheel:a=e({},a,{down:!0}),d("wheelmove",a);break;case u[s].end:a=e({},a,{down:!1}),d("wheelend",a);break;default:return}})),Object.freeze({observe:l,unobserve:E,disconnect:h,on:M,off:m})}; | ||
//# sourceMappingURL=wheel-gestures.cjs.production.min.js.map |
@@ -1,17 +0,1 @@ | ||
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); | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _extends() { | ||
@@ -65,2 +49,8 @@ _extends = Object.assign || function (target) { | ||
function average(numbers) { | ||
return numbers.reduce(function (a, b) { | ||
return a + b; | ||
}) / numbers.length; | ||
} | ||
var LINE_HEIGHT = 16 * 1.125; | ||
@@ -82,2 +72,22 @@ var PAGE_HEIGHT = typeof window !== 'undefined' && window.innerHeight || 800; | ||
/** | ||
* the timeout is automatically adjusted during a gesture | ||
* the initial timeout period is pretty long, so even old mouses, which emit wheel events less often, can produce a continuous gesture | ||
*/ | ||
var WILL_END_TIMEOUT_DEFAULT = 400; | ||
function createWheelAnalyzerState() { | ||
return { | ||
isStarted: false, | ||
isStartPublished: false, | ||
isMomentum: false, | ||
lastAbsDelta: Infinity, | ||
axisMovement: [0, 0], | ||
axisVelocity: [0, 0], | ||
accelerationFactors: [], | ||
scrollPoints: [], | ||
scrollPointsToMerge: [], | ||
willEndTimeout: WILL_END_TIMEOUT_DEFAULT | ||
}; | ||
} | ||
var WheelPhase; | ||
@@ -101,10 +111,3 @@ | ||
var ACC_FACTOR_MAX = 0.96; | ||
var DELTA_MAX_ABS = 150; // TODO: test next release | ||
/** | ||
* the timeout is automatically adjusted during a gesture | ||
* the initial timeout period is pretty long, so even old mouses, which emit wheel events less often, can produce a continuous gesture | ||
*/ | ||
var WILL_END_TIMEOUT_DEFAULT = 400; | ||
var DELTA_MAX_ABS = 150; | ||
var axes = ['x', 'y']; | ||
@@ -122,125 +125,84 @@ var deltaProp = { | ||
}; | ||
var WheelAnalyzer = /*#__PURE__*/function () { | ||
function WheelAnalyzer(options) { | ||
var _this = this; | ||
function WheelAnalyzer(optionsParam) { | ||
if (optionsParam === void 0) { | ||
optionsParam = {}; | ||
} | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var state = createWheelAnalyzerState(); | ||
var subscriptions = []; | ||
var targets = []; // merge passed options with defaults (filter undefined option values) | ||
this.isStarted = false; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisMovement = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.accelerationFactors = []; | ||
this.scrollPoints = []; | ||
this.scrollPointsToMerge = []; | ||
this.subscriptions = []; | ||
this.targets = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
var options = Object.entries(optionsParam).filter(function (_ref) { | ||
var value = _ref[1]; | ||
return value !== undefined; | ||
}).reduce(function (o, _ref2) { | ||
var _Object$assign; | ||
this.observe = function (target) { | ||
// TODO: need to test if passive supported? might throw error otherwise in older browsers? | ||
target.addEventListener('wheel', _this.feedWheel, { | ||
passive: false | ||
}); | ||
var key = _ref2[0], | ||
value = _ref2[1]; | ||
return Object.assign(o, (_Object$assign = {}, _Object$assign[key] = value, _Object$assign)); | ||
}, _extends({}, defaults)); | ||
_this.targets.push(target); | ||
return _this.unobserve.bind(_this, target); | ||
var observe = function observe(target) { | ||
target.addEventListener('wheel', feedWheel, { | ||
passive: false | ||
}); | ||
targets.push(target); | ||
return function () { | ||
return unobserve(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. | ||
var unobserve = function unobserve(target) { | ||
target.removeEventListener('wheel', feedWheel); | ||
targets = 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); | ||
}; | ||
var disconnect = function disconnect() { | ||
targets.forEach(unobserve); | ||
}; | ||
this.subscribe = function (callback) { | ||
_this.subscriptions.push(callback); // return bound unsubscribe | ||
var subscribe = function subscribe(callback) { | ||
subscriptions.push(callback); // return bound unsubscribe | ||
return _this.unsubscribe.bind(_this, callback); | ||
return function () { | ||
return unsubscribe(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; | ||
}); | ||
var unsubscribe = function unsubscribe(callback) { | ||
if (!callback) throw new Error('please pass the callback which was used to subscribe'); | ||
subscriptions = subscriptions.filter(function (s) { | ||
return s !== callback; | ||
}); | ||
}; | ||
var publish = function publish(type) { | ||
var data = { | ||
type: type, | ||
willEndSoon: willEndSoon(), | ||
isMomentum: state.isMomentum, | ||
axisMovement: state.axisMovement, | ||
axisVelocity: state.axisVelocity | ||
}; | ||
subscriptions.forEach(function (fn) { | ||
return fn(type, data); | ||
}); | ||
}; | ||
this.publish = function (phase, data) { | ||
if (data === void 0) { | ||
data = _this.getCurrentState(phase); | ||
} | ||
_this.subscriptions.forEach(function (fn) { | ||
return fn(phase, data); | ||
var feedWheel = function feedWheel(wheelEvents) { | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { | ||
return processWheelEventData(wheelEvent); | ||
}); | ||
}; | ||
} else { | ||
processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
this.feedWheel = function (wheelEvents) { | ||
if (Array.isArray(wheelEvents)) { | ||
wheelEvents.forEach(function (wheelEvent) { | ||
return _this.processWheelEventData(wheelEvent); | ||
}); | ||
} else { | ||
_this.processWheelEventData(wheelEvents); | ||
} | ||
}; | ||
this.willEnd = function () { | ||
var willEndId; | ||
return function () { | ||
clearTimeout(willEndId); | ||
willEndId = setTimeout(_this.end, _this.willEndTimeout); | ||
}; | ||
}(); | ||
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); | ||
} | ||
} 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 shouldPreventDefault = function shouldPreventDefault(e) { | ||
var preventWheelAction = options.preventWheelAction; | ||
var deltaX = e.deltaX, | ||
@@ -263,32 +225,30 @@ deltaY = e.deltaY; | ||
_proto.clampDelta = function clampDelta(delta) { | ||
var 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 processWheelEventData = function processWheelEventData(wheelEvent) { | ||
var normalizedWheel = normalizeWheel(wheelEvent); | ||
if (wheelEvent.preventDefault && this.shouldPreventDefault(wheelEvent)) { | ||
if (wheelEvent.preventDefault && shouldPreventDefault(wheelEvent)) { | ||
wheelEvent.preventDefault(); | ||
} | ||
if (!this.isStarted) { | ||
this.start(); | ||
if (!state.isStarted) { | ||
start(); | ||
} | ||
var currentDelta = this.clampDelta(Math.abs(normalizedWheel.deltaY) > Math.abs(normalizedWheel.deltaX) ? normalizedWheel.deltaY : normalizedWheel.deltaX); | ||
var currentDelta = 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(); | ||
if (state.isMomentum && currentAbsDelta > state.lastAbsDelta) { | ||
end(); | ||
start(); | ||
} | ||
this.axisMovement = this.axisMovement.map(function (prevDelta, i) { | ||
return prevDelta + _this2.clampDelta(normalizedWheel[deltaProp[axes[i]]]); | ||
state.axisMovement = state.axisMovement.map(function (prevDelta, i) { | ||
return prevDelta + clampDelta(normalizedWheel[deltaProp[axes[i]]]); | ||
}); | ||
this.lastAbsDelta = currentAbsDelta; | ||
this.scrollPointsToMerge.push({ | ||
state.lastAbsDelta = currentAbsDelta; | ||
state.scrollPointsToMerge.push({ | ||
currentAbsDelta: currentAbsDelta, | ||
@@ -299,8 +259,10 @@ axisDeltaUnclampt: [normalizedWheel.deltaX, normalizedWheel.deltaY], | ||
if (this.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
if (state.scrollPointsToMerge.length === WHEELEVENTS_TO_MERGE) { | ||
var _state = state, | ||
scrollPointsToMerge = _state.scrollPointsToMerge; | ||
var mergedScrollPoint = { | ||
currentAbsDelta: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.currentAbsDelta; | ||
}, 0) / WHEELEVENTS_TO_MERGE, | ||
axisDeltaUnclampt: this.scrollPointsToMerge.reduce(function (_ref3, _ref4) { | ||
currentAbsDelta: average(scrollPointsToMerge.map(function (b) { | ||
return b.currentAbsDelta; | ||
})), | ||
axisDeltaUnclampt: scrollPointsToMerge.reduce(function (_ref3, _ref4) { | ||
var sumX = _ref3[0], | ||
@@ -313,49 +275,47 @@ sumY = _ref3[1]; | ||
}, [0, 0]), | ||
timestamp: this.scrollPointsToMerge.reduce(function (sum, b) { | ||
return sum + b.timestamp; | ||
}, 0) / WHEELEVENTS_TO_MERGE | ||
timestamp: average(scrollPointsToMerge.map(function (b) { | ||
return b.timestamp; | ||
})) | ||
}; | ||
this.scrollPoints.push(mergedScrollPoint); // only update velocity after a merged scrollpoint was generated | ||
state.scrollPoints.push(mergedScrollPoint); // only update velocity after a merged scrollpoint was generated | ||
this.updateVelocity(); | ||
updateVelocity(); | ||
if (!this.isMomentum) { | ||
this.detectMomentum(); | ||
if (!state.isMomentum) { | ||
detectMomentum(); | ||
} // reset merge array | ||
this.scrollPointsToMerge = []; | ||
state.scrollPointsToMerge = []; | ||
} | ||
if (!this.scrollPoints.length) { | ||
this.updateStartVelocity(); | ||
if (!state.scrollPoints.length) { | ||
updateStartVelocity(); | ||
} // publish start after velocity etc. have been updated | ||
if (!this.isStartPublished) { | ||
this.publish(WheelPhase.ANY_WHEEL_START); | ||
this.publish(WheelPhase.WHEEL_START); | ||
this.isStartPublished = true; | ||
if (!state.isStartPublished) { | ||
publish(WheelPhase.ANY_WHEEL_START); | ||
publish(WheelPhase.WHEEL_START); | ||
state.isStartPublished = true; | ||
} | ||
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 | ||
publish(WheelPhase.ANY_WHEEL); | ||
publish(state.isMomentum ? WheelPhase.MOMENTUM_WHEEL : WheelPhase.WHEEL); // calc debounced end function, to recognize end of wheel event stream | ||
this.willEnd(); | ||
willEnd(); | ||
}; | ||
_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 updateStartVelocity = function updateStartVelocity() { | ||
var latestScrollPoint = state.scrollPointsToMerge[state.scrollPointsToMerge.length - 1]; | ||
state.axisVelocity = latestScrollPoint.axisDeltaUnclampt.map(function (d) { | ||
return d / state.willEndTimeout; | ||
}); | ||
}; | ||
_proto.updateVelocity = function updateVelocity() { | ||
var _this3 = this; | ||
var updateVelocity = function updateVelocity() { | ||
// 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]; | ||
var _state$scrollPoints$s = state.scrollPoints.slice(-2), | ||
pA = _state$scrollPoints$s[0], | ||
pB = _state$scrollPoints$s[1]; | ||
@@ -380,21 +340,21 @@ if (!pA || !pB) { | ||
var accelerationFactor = velocity.map(function (v, i) { | ||
return v / (_this3.axisVelocity[i] || 1); | ||
return v / (state.axisVelocity[i] || 1); | ||
}); | ||
this.axisVelocity = velocity; | ||
this.accelerationFactors.push(accelerationFactor); | ||
this.updateWillEndTimeout(deltaTime); | ||
state.axisVelocity = velocity; | ||
state.accelerationFactors.push(accelerationFactor); | ||
updateWillEndTimeout(deltaTime); | ||
}; | ||
_proto.updateWillEndTimeout = function updateWillEndTimeout(deltaTime) { | ||
var 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) { | ||
if (!state.isMomentum) { | ||
newTimeout = Math.max(100, newTimeout * 2); | ||
} | ||
this.willEndTimeout = Math.min(1000, Math.round(newTimeout)); | ||
state.willEndTimeout = Math.min(1000, Math.round(newTimeout)); | ||
}; | ||
WheelAnalyzer.accelerationFactorInMomentumRange = function accelerationFactorInMomentumRange(accFactor) { | ||
var accelerationFactorInMomentumRange = function accelerationFactorInMomentumRange(accFactor) { | ||
// when main axis is the the other one and there is no movement/change on the current one | ||
@@ -405,8 +365,8 @@ if (accFactor === 0) return true; | ||
_proto.detectMomentum = function detectMomentum() { | ||
if (this.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return this.isMomentum; | ||
var detectMomentum = function detectMomentum() { | ||
if (state.accelerationFactors.length < WHEELEVENTS_TO_ANALAZE) { | ||
return state.isMomentum; | ||
} | ||
var recentAccelerationFactors = this.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); // check recent acceleration / deceleration factors | ||
var recentAccelerationFactors = state.accelerationFactors.slice(WHEELEVENTS_TO_ANALAZE * -1); // check recent acceleration / deceleration factors | ||
@@ -421,3 +381,3 @@ var detectedMomentum = recentAccelerationFactors.reduce(function (mightBeMomentum, accFac) { | ||
var bothAreInRangeOrZero = accFac.filter(WheelAnalyzer.accelerationFactorInMomentumRange).length === accFac.length; // one the requirements must be fulfilled | ||
var bothAreInRangeOrZero = accFac.filter(accelerationFactorInMomentumRange).length === accFac.length; // one the requirements must be fulfilled | ||
@@ -427,53 +387,66 @@ return sameAccFac || bothAreInRangeOrZero; | ||
this.accelerationFactors = recentAccelerationFactors; | ||
state.accelerationFactors = recentAccelerationFactors; | ||
if (detectedMomentum && !this.isMomentum) { | ||
this.publish(WheelPhase.WHEEL_END); | ||
this.isMomentum = true; | ||
this.publish(WheelPhase.MOMENTUM_WHEEL_START); | ||
if (detectedMomentum && !state.isMomentum) { | ||
publish(WheelPhase.WHEEL_END); | ||
state.isMomentum = true; | ||
publish(WheelPhase.MOMENTUM_WHEEL_START); | ||
} | ||
return this.isMomentum; | ||
return state.isMomentum; | ||
}; | ||
_proto.getCurrentState = function getCurrentState(type) { | ||
return { | ||
type: type, | ||
willEndSoon: this.willEndSoon, | ||
isMomentum: this.isMomentum, | ||
axisMovement: this.axisMovement, | ||
axisVelocity: this.axisVelocity | ||
var start = function start() { | ||
state = createWheelAnalyzerState(); | ||
state.isStarted = true; | ||
}; | ||
var willEnd = function () { | ||
var willEndId; | ||
return function () { | ||
clearTimeout(willEndId); | ||
willEndId = setTimeout(end, state.willEndTimeout); | ||
}; | ||
}(); | ||
var end = function end() { | ||
if (!state.isStarted) return; | ||
if (state.isMomentum) { | ||
if (!willEndSoon()) { | ||
publish(WheelPhase.MOMENTUM_WHEEL_CANCEL); | ||
} else { | ||
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 | ||
publish(WheelPhase.WHEEL_END); | ||
} | ||
publish(WheelPhase.ANY_WHEEL_END); | ||
state.isMomentum = false; | ||
state.isStarted = false; | ||
}; | ||
_proto.start = function start() { | ||
this.isStarted = true; | ||
this.isStartPublished = false; | ||
this.isMomentum = false; | ||
this.lastAbsDelta = Infinity; | ||
this.axisMovement = [0, 0]; | ||
this.axisVelocity = [0, 0]; | ||
this.scrollPointsToMerge = []; | ||
this.scrollPoints = []; | ||
this.accelerationFactors = []; | ||
this.willEndTimeout = WILL_END_TIMEOUT_DEFAULT; | ||
var willEndSoon = function willEndSoon() { | ||
var absDeltas = state.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; | ||
}; | ||
_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 Object.freeze({ | ||
observe: observe, | ||
unobserve: unobserve, | ||
disconnect: disconnect, | ||
subscribe: subscribe, | ||
unsubscribe: unsubscribe, | ||
feedWheel: feedWheel | ||
}); | ||
} | ||
return WheelAnalyzer; | ||
}(); | ||
var WheelReason; | ||
@@ -508,3 +481,3 @@ | ||
}; | ||
var wheelAnalyzer = new WheelAnalyzer({ | ||
var wheelAnalyzer = WheelAnalyzer({ | ||
preventWheelAction: axis | ||
@@ -511,0 +484,0 @@ }); |
{ | ||
"name": "wheel-gestures", | ||
"version": "1.0.2", | ||
"version": "1.0.3", | ||
"description": "wheel gestures and momentum detection", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -15,4 +15,4 @@ # ![wheel gestures](./WheelGestures.svg) | ||
- Mac OS (Chrome, Firefox, Safari, Brave, Edge) | ||
- Mac OS (Chrome, Firefox, Safari, Brave, Edge, IE10+) | ||
- Windows (testing needed & help appreciated) | ||
- Linux (testing needed & help appreciated) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
20
136235
1050