☠☠ backed out by 1bbb4c7ec2d7 ☠ ☠ | |
author | Benjamin Stover <bstover@mozilla.com> |
Sat, 09 Apr 2011 12:38:23 -0700 | |
changeset 67743 | e5f8db95d0c5583fdcd57bae1d49c2036d5183be |
parent 67742 | 195c8ad12184ad608834101a8985018cab58632b |
child 67744 | 97a7a9f807250dfd079551c0c5c5c97a4bde29a4 |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mbrubeck |
bugs | 639179 |
milestone | 2.2a1pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/chrome/content/input.js +++ b/mobile/chrome/content/input.js @@ -55,16 +55,23 @@ const kOverTapWait = 150; const kLongTapWait = 500; // maximum drag distance in inches while axis locking can still be reverted const kAxisLockRevertThreshold = 0.8; // Same as NS_EVENT_STATE_ACTIVE from nsIEventStateManager.h const kStateActive = 0x00000001; +// After a drag begins, kinetic panning is stopped if the drag doesn't become +// a pan in 300 milliseconds. +const kStopKineticPanOnDragTimeout = 300; + +// Max velocity of a pan. This is in pixels/millisecond. +const kMaxVelocity = 6; + /** * MouseModule * * Handles all touch-related input such as dragging and tapping. * * The Fennec chrome DOM tree has elements that are augmented dynamically with * custom JS properties that tell the MouseModule they have custom support for * either dragging or clicking. These JS properties are JS objects that expose @@ -200,29 +207,31 @@ MouseModule.prototype = { // stop kinetic panning if targetScrollbox has changed if (this._kinetic.isActive() && this._dragger != dragger) this._kinetic.end(); this._targetScrollbox = targetScrollInterface ? targetScrollInterface.element : targetScrollbox; this._targetScrollInterface = targetScrollInterface; // Do tap - let event = document.createEvent("Events"); - event.initEvent("TapDown", true, true); - event.clientX = aEvent.clientX; - event.clientY = aEvent.clientY; - let success = aEvent.target.dispatchEvent(event); - if (success) { - this._recordEvent(aEvent); - this._target = aEvent.target; - this._mouseOverTimeout.once(kOverTapWait); - this._longClickTimeout.once(kLongTapWait); - } else { - // cancel all pending content clicks - this._cleanClickBuffer(); + if (!this._kinetic.isActive()) { + let event = document.createEvent("Events"); + event.initEvent("TapDown", true, true); + event.clientX = aEvent.clientX; + event.clientY = aEvent.clientY; + let success = aEvent.target.dispatchEvent(event); + if (success) { + this._recordEvent(aEvent); + this._target = aEvent.target; + this._mouseOverTimeout.once(kOverTapWait); + this._longClickTimeout.once(kLongTapWait); + } else { + // cancel all pending content clicks + this._cleanClickBuffer(); + } } // Do pan if (dragger) { let draggable = dragger.isDraggable(targetScrollbox, targetScrollInterface); dragData.locked = !draggable.x || !draggable.y; if (draggable.x || draggable.y) { this._dragger = dragger; @@ -353,60 +362,67 @@ MouseModule.prototype = { /** * Inform our dragger of a dragStart. */ _doDragStart: function _doDragStart(aEvent, aDraggable) { let dragData = this._dragData; dragData.setDragStart(aEvent.screenX, aEvent.screenY, aDraggable); this._kinetic.addData(0, 0); + this._dragStartTime = Date.now(); if (!this._kinetic.isActive()) this._dragger.dragStart(aEvent.clientX, aEvent.clientY, aEvent.target, this._targetScrollInterface); }, /** Finish a drag. */ _doDragStop: function _doDragStop() { let dragData = this._dragData; if (!dragData.dragging) return; dragData.endDrag(); // Note: it is possible for kinetic scrolling to be active from a // mousedown/mouseup event previous to this one. In this case, we // want the kinetic panner to tell our drag interface to stop. - if (!dragData.isPan() && !this._kinetic.isActive()) { - // There was no pan and no kinetic scrolling, so just stop dragger. + if (dragData.isPan()) { + if (Date.now() - this._dragStartTime > kStopKineticPanOnDragTimeout) + this._kinetic._velocity.set(0, 0); + // Start kinetic pan. + this._kinetic.start(); + } else { + this._kinetic.end(); this._dragger.dragStop(0, 0, this._targetScrollInterface); this._dragger = null; - } else if (dragData.isPan()) { - // Start kinetic pan. - this._kinetic.start(); } }, /** * Used by _onMouseMove() above and by KineticController's timer to do the * actual dragMove signalling to the dragger. We'd put this in _onMouseMove() * but then KineticController would be adding to its own data as it signals * the dragger of dragMove()s. */ _dragBy: function _dragBy(dX, dY, aIsKinetic) { let dragged = true; + let dragData = this._dragData; if (!this._waitingForPaint || aIsKinetic) { let dragData = this._dragData; dragged = this._dragger.dragMove(dX, dY, this._targetScrollInterface, aIsKinetic); if (dragged && !this._waitingForPaint) { this._waitingForPaint = true; mozRequestAnimationFrame(this); } this.dX = 0; this.dY = 0; } + if (!dragData.isPan()) + this._kinetic.pause(); + return dragged; }, /** Callback for kinetic scroller. */ _kineticStop: function _kineticStop() { // Kinetic panning could finish while user is panning, so don't finish // the pan just yet. let dragData = this._dragData; @@ -826,16 +842,17 @@ function KineticController(aPanBy, aEndC this._swipeLength = Services.prefs.getIntPref("browser.ui.kinetic.swipeLength"); this._reset(); } KineticController.prototype = { _reset: function _reset() { this._active = false; + this._paused = false; this.momentumBuffer = []; this._velocity.set(0, 0); }, isActive: function isActive() { return this._active; }, @@ -867,17 +884,19 @@ KineticController.prototype = { // Temporary "bins" so that we don't create new objects during pan. let aBin = new Point(0, 0); let v0Bin = new Point(0, 0); let self = this; let callback = { onBeforePaint: function kineticHandleEvent(timeStamp) { - if (!self.isActive()) // someone called end() on us between timer intervals + // Someone called end() on us between timer intervals + // or we are paused. + if (!self.isActive() || self._paused) return; // To make animation end fast enough but to keep smoothness, average the ideal // time frame (smooth animation) with the actual time lapse (end fast enough). // Animation will never take longer than 2 times the ideal length of time. let realt = timeStamp - self._initialTime; self._time += self._updateInterval; let t = (self._time + realt) / 2; @@ -918,16 +937,17 @@ KineticController.prototype = { self.end(); else mozRequestAnimationFrame(this); } } }; this._active = true; + this._paused = false; mozRequestAnimationFrame(callback); }, start: function start() { function sign(x) { return x ? ((x > 0) ? 1 : -1) : 0; } @@ -944,34 +964,51 @@ KineticController.prototype = { for (let i = 0; i < mblen; i++) { me = mb[i]; if (lastTime - me.t < swipeLength) { distanceX += me.dx; distanceY += me.dy; } } - // Only allow kinetic scrolling to speed up if kinetic scrolling is active. - this._velocity.x = (distanceX < 0 ? Math.min : Math.max)((distanceX / swipeLength) * this._speedSensitivity, this._velocity.x); - this._velocity.y = (distanceY < 0 ? Math.min : Math.max)((distanceY / swipeLength) * this._speedSensitivity, this._velocity.y); + let currentVelocityX = 0; + let currentVelocityY = 0; + + if (this.isActive()) { + let currentTime = Date.now() - this._initialTime; + currentVelocityX = Util.clamp(this._velocity.x + this._acceleration.x * currentTime, -kMaxVelocity, kMaxVelocity); + currentVelocityY = Util.clamp(this._velocity.y + this._acceleration.y * currentTime, -kMaxVelocity, kMaxVelocity); + } + + if (currentVelocityX * this._velocity.x <= 0) + currentVelocityX = 0; + if (currentVelocityY * this._velocity.y <= 0) + currentVelocityY = 0; + + this._velocity.x = clampFromZero((distanceX / swipeLength) + currentVelocityX, Math.abs(currentVelocityX), kMaxVelocity); + this._velocity.y = clampFromZero((distanceY / swipeLength) + currentVelocityY, Math.abs(currentVelocityY), kMaxVelocity); // Set acceleration vector to opposite signs of velocity this._acceleration.set(this._velocity.clone().map(sign).scale(-this._decelerationRate)); this._position.set(0, 0); this._initialTime = mozAnimationStartTime; this._time = 0; this.momentumBuffer = []; - if (!this.isActive()) + if (!this.isActive() || this._paused) this._startTimer(); return true; }, + pause: function pause() { + this._paused = true; + }, + end: function end() { if (this.isActive()) { if (this._beforeEnd) this._beforeEnd(); this._reset(); } },