/*
* Contains the Rotate class.
*/
'use strict';
const { Gesture, Point2D, Smoothable } = require('../core');
/**
* Data returned when a Pivotable is recognized.
*
* @typedef {Object} SwivelData
* @mixes ReturnTypes.BaseData
*
* @property {number} rotation - In radians, the change in angle since last
* emit.
* @property {westures-core.Point2D} pivot - The pivot point.
*
* @memberof ReturnTypes
*/
/**
* A Pivotable is a single input rotating around a fixed point. The fixed point
* is determined by the input's location at its 'start' phase.
*
* @extends westures.Gesture
* @see {ReturnTypes.SwivelData}
* @memberof westures
*
* @param {Element} element - The element to which to associate the gesture.
* @param {Function} handler - The function handler to execute when a gesture
* is recognized on the associated element.
* @param {object} [options] - Gesture customization options.
* @param {westures-core.STATE_KEYS[]} [options.enableKeys=[]] - List of keys
* which will enable the gesture. The gesture will not be recognized unless one
* of these keys is pressed while the interaction occurs. If not specified or an
* empty list, the gesture is treated as though the enable key is always down.
* @param {westures-core.STATE_KEYS[]} [options.disableKeys=[]] - List of keys
* which will disable the gesture. The gesture will not be recognized if one of
* these keys is pressed while the interaction occurs. If not specified or an
* empty list, the gesture is treated as though the disable key is never down.
* @param {number} [options.minInputs=1] - The minimum number of pointers that
* must be active for the gesture to be recognized. Uses >=.
* @param {number} [options.maxInputs=Number.MAX_VALUE] - The maximum number of
* pointers that may be active for the gesture to be recognized. Uses <=.
* @param {boolean} [options.applySmoothing=true] - Whether to apply inertial
* smoothing for systems with coarse pointers.
* @param {number} [options.deadzoneRadius=15] - The radius in pixels around the
* start point in which to do nothing.
* @param {Element} [options.dynamicPivot=false] - Normally the center point of
* the gesture's element is used as the pivot. If this option is set, the
* initial contact point with the element is used as the pivot instead.
*/
class Pivotable extends Gesture {
constructor(type = 'pivotable', element, handler, options = {}) {
super(type, element, handler, { ...Pivotable.DEFAULTS, ...options });
/**
* The pivot point of the pivotable.
*
* @type {westures-core.Point2D}
*/
this.pivot = null;
/**
* The previous data.
*
* @type {number}
*/
this.previous = 0;
/**
* The outgoing data.
*
* @type {westures-core.Smoothable}
*/
this.outgoing = new Smoothable(options);
}
/**
* Determine the center point of the given element's bounding client
* rectangle.
*
* @static
*
* @param {Element} element - The DOM element to analyze.
* @return {westures-core.Point2D} - The center of the element's bounding
* client rectangle.
*/
static getClientCenter(element) {
const rect = element.getBoundingClientRect();
return new Point2D(
rect.left + (rect.width / 2),
rect.top + (rect.height / 2),
);
}
/**
* Updates the previous data. It will be called during the 'start' and 'end'
* phases, and should also be called during the 'move' phase implemented by
* the subclass.
*
* @abstract
* @param {State} state - the current input state.
*/
updatePrevious() {
throw 'Gestures which extend Pivotable must implement updatePrevious()';
}
/**
* Restart the given progress object using the given input object.
*
* @param {State} state - current input state.
*/
restart(state) {
if (this.options.dynamicPivot) {
this.pivot = state.centroid;
this.previous = 0;
} else {
this.pivot = Pivotable.getClientCenter(this.element);
this.updatePrevious(state);
}
this.outgoing.restart();
}
start(state) {
this.restart(state);
}
end(state) {
if (state.active.length > 0) {
this.restart(state);
} else {
this.outgoing.restart();
}
}
cancel() {
this.outgoing.restart();
}
}
Pivotable.DEFAULTS = Object.freeze({
deadzoneRadius: 15,
dynamicPivot: false,
});
module.exports = Pivotable;