diff options
Diffstat (limited to 'Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js')
-rw-r--r-- | Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js new file mode 100644 index 000000000..12bf0713a --- /dev/null +++ b/Source/WebCore/Modules/modern-media-controls/gesture-recognizers/pinch.js @@ -0,0 +1,205 @@ + +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; + } + + // Public + + 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; + } + + // Protected + + touchesBegan(event) + { + if (event.currentTarget !== this.target) + return; + + // Additional setup for when the the platform doesn't natively + // provide us with gesture events. + if (!GestureRecognizer.SupportsGestures) { + // A pinch gesture can only be performed with 2 fingers, anything more + // and we failed our gesture. + if (this.numberOfTouches > 2) { + this.enterFailedState(); + return; + } + + // We can only start tracking touches with 2 fingers. + if (this.numberOfTouches !== 2) + return; + + this._startDistance = this._distance(); + + // We manually add a start value so that we always have 2 entries in the + // _gestures array so that we don't have to check for the existence of 2 + // entries when computing velocity. + this._recordGesture(1); + + this._scaledMinimumAmount = false; + this._updateStateWithEvent(event); + } else if (this.numberOfTouches !== 2) { + // When we support gesture events, we only care about the case where we're + // using two fingers. + return; + } + + super.touchesBegan(event); + } + + touchesMoved(event) + { + // This method only needs to be overriden in the case where the platform + // doesn't natively provide us with gesture events. + if (GestureRecognizer.SupportsGestures) + return; + + if (this.numberOfTouches !== 2) + return; + + this._updateStateWithEvent(event); + } + + touchesEnded(event) + { + // This method only needs to be overriden in the case where the platform + // doesn't natively provide us with gesture events. + if (GestureRecognizer.SupportsGestures) + return; + + // If we don't have the required number of touches or have not event + // obtained 2 fingers, then there's nothing for us to do. + if (this.numberOfTouches >= 2 || !this._startDistance) + return; + + if (this._scaledMinimumAmount) + this.enterEndedState(); + else + this.enterFailedState(); + } + + gestureBegan(event) + { + super.gestureBegan(event); + + // We manually add a start value so that we always have 2 entries in the + // _gestures array so that we don't have to check for the existence of 2 + // entries when computing velocity. + 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; + } + + // Private + + _recordGesture(scale) + { + const currentTime = Date.now(); + const count = this._gestures.push({ + scale: scale, + timeStamp: currentTime + }); + + // We want to keep at least two gestures at all times. + 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)); + } + +} |