const MAXIMUM_TIME_FOR_RECORDING_GESTURES = 100;
const MAXIMUM_DECELERATION_TIME = 500;
class PinchGestureRecognizer extends GestureRecognizer
{
constructor(target, delegate)
{
super(target, delegate);
this.scaleThreshold = 0;
this._scaledMinimumAmount = false;
}
get velocity()
{
const lastGesture = this._gestures[this._gestures.length - 1];
if (!lastGesture)
return this._velocity;
const elapsedTime = Date.now() - (lastGesture.timeStamp + MAXIMUM_TIME_FOR_RECORDING_GESTURES);
if (elapsedTime <= 0)
return this._velocity;
const f = Math.max((MAXIMUM_DECELERATION_TIME - elapsedTime) / MAXIMUM_DECELERATION_TIME, 0);
return this._velocity * f;
}
touchesBegan(event)
{
if (event.currentTarget !== this.target)
return;
if (!GestureRecognizer.SupportsGestures) {
if (this.numberOfTouches > 2) {
this.enterFailedState();
return;
}
if (this.numberOfTouches !== 2)
return;
this._startDistance = this._distance();
this._recordGesture(1);
this._scaledMinimumAmount = false;
this._updateStateWithEvent(event);
} else if (this.numberOfTouches !== 2) {
return;
}
super.touchesBegan(event);
}
touchesMoved(event)
{
if (GestureRecognizer.SupportsGestures)
return;
if (this.numberOfTouches !== 2)
return;
this._updateStateWithEvent(event);
}
touchesEnded(event)
{
if (GestureRecognizer.SupportsGestures)
return;
if (this.numberOfTouches >= 2 || !this._startDistance)
return;
if (this._scaledMinimumAmount)
this.enterEndedState();
else
this.enterFailedState();
}
gestureBegan(event)
{
super.gestureBegan(event);
this._recordGesture(event.scale);
this._scaledMinimumAmount = false;
this._updateStateWithEvent(event);
event.preventDefault();
}
gestureChanged(event)
{
event.preventDefault();
this._updateStateWithEvent(event);
}
gestureEnded(event)
{
if (this._scaledMinimumAmount)
this.enterEndedState();
else
this.enterFailedState();
}
reset()
{
this.scale = 1;
this._velocity = 0;
this._gestures = [];
delete this._startDistance;
}
_recordGesture(scale)
{
const currentTime = Date.now();
const count = this._gestures.push({
scale: scale,
timeStamp: currentTime
});
if (count <= 2)
return;
const scaleDirection = this._gestures[count - 1].scale >= this._gestures[count - 2].scale;
let i = count - 3;
for (; i >= 0; --i) {
let gesture = this._gestures[i];
if (currentTime - gesture.timeStamp > MAXIMUM_TIME_FOR_RECORDING_GESTURES ||
this._gestures[i + 1].scale >= gesture.scale !== scaleDirection)
break;
}
if (i > 0)
this._gestures = this._gestures.slice(i + 1);
}
_updateStateWithEvent(event)
{
const scaleSinceStart = GestureRecognizer.SupportsGestures ? event.scale : this._distance() / this._startDistance;
if (!this._scaledMinimumAmount) {
if (Math.abs(1 - scaleSinceStart) >= this.scaleThreshold) {
this._scaledMinimumAmount = true;
this.scale = 1;
this.enterBeganState();
}
return;
}
this._recordGesture(scaleSinceStart);
const oldestGesture = this._gestures[0];
const ds = scaleSinceStart - oldestGesture.scale;
const dt = Date.now() - oldestGesture.timeStamp;
this._velocity = (dt === 0) ? 0 : ds / dt * 1000;
this.scale *= scaleSinceStart / this._gestures[this._gestures.length - 2].scale;
this.enterChangedState();
}
_distance()
{
console.assert(this.numberOfTouches === 2);
const firstTouch = this._targetTouches[0];
const firstTouchPoint = new DOMPoint(firstTouch.pageX, firstTouch.pageY);
const secondTouch = this._targetTouches[1];
const secondTouchPoint = new DOMPoint(secondTouch.pageX, secondTouch.pageY);
return Math.sqrt(Math.pow(firstTouchPoint.x - secondTouchPoint.x, 2) + Math.pow(firstTouchPoint.y - secondTouchPoint.y, 2));
}
}