Improve kinetic panning and add constants to customise behaviour.

Increase the number of considered motion events and add some constants to
control the kinetic panning behaviour. This should make it a lot less glitchy.
This commit is contained in:
Chris Lord 2011-10-17 15:02:27 -04:00
parent 162b480291
commit c3ab0aab11

View File

@ -7,6 +7,24 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm")
// The ratio of velocity that is retained every ms.
const kPanDeceleration = 0.999;
// The number of ms to consider events over for a swipe gesture.
const kSwipeLength = 1000;
// Minimum speed to move during kinetic panning. 0.015 pixels/ms is roughly
// equivalent to a pixel every 4 frames at 60fps.
const kMinKineticSpeed = 0.015;
// Maximum kinetic panning speed. 3 pixels/ms is equivalent to 50 pixels per
// frame at 60fps.
const kMaxKineticSpeed = 3;
// The maximum magnitude of disparity allowed between axes acceleration. If
// it's larger than this, lock the slow-moving axis.
const kAxisLockRatio = 5;
function dump(a) {
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).logStringMessage(a);
}
@ -319,9 +337,6 @@ Tab.prototype = {
var BrowserEventHandler = {
init: function init() {
this.motionBuffer = [];
this._updateLastPosition(0, 0, 0, 0);
window.addEventListener("click", this, true);
window.addEventListener("mousedown", this, true);
window.addEventListener("mouseup", this, true);
@ -406,14 +421,12 @@ var BrowserEventHandler = {
this.firstMovement = true;
this.firstXMovement = 0;
this.firstYMovement = 0;
this.motionBuffer = [];
this._updateLastPosition(aEvent.clientX, aEvent.clientY, 0, 0);
this.panElement = this._findScrollableElement(aEvent.originalTarget,
true);
// Set panning to true *after* calling updateLastPosition, so
// that it can clear the motion buffer the first time we call
// it.
if (this.panElement)
this.panning = true;
@ -486,19 +499,18 @@ var BrowserEventHandler = {
this.blockClick = true;
aEvent.stopPropagation();
aEvent.preventDefault();
}
if (isDrag &&
(this.motionBuffer[4].time - this.motionBuffer[0].time) < 1000) {
// We've exceeded the scroll threshold, start kinetic pan
// Find the average velocity over the last few motion events
this.panLastTime = window.mozAnimationStartTime;
this.panX = 0;
this.panY = 0;
let nEvents = 0;
for (let i = 1; i < 5; i++) {
for (let i = 0; i < this.motionBuffer.length - 1; i++) {
if (this.motionBuffer[i].time < this.panLastTime - kSwipeLength)
break;
let timeDelta = this.motionBuffer[i].time -
this.motionBuffer[i - 1].time;
this.motionBuffer[i + 1].time;
if (timeDelta <= 0)
continue;
@ -506,11 +518,19 @@ var BrowserEventHandler = {
this.panY += this.motionBuffer[i].dy / timeDelta;
nEvents ++;
}
if (nEvents == 0)
break;
this.panX /= nEvents;
this.panY /= nEvents;
if (Math.abs(this.panX) > kMaxKineticSpeed)
this.panX = (this.panX > 0) ? kMaxKineticSpeed : -kMaxKineticSpeed;
if (Math.abs(this.panY) > kMaxKineticSpeed)
this.panY = (this.panY > 0) ? kMaxKineticSpeed : -kMaxKineticSpeed;
// If we have zero acceleration, bail out.
if (this.panX == 0 && this.panY == 0)
// If we have (near) zero acceleration, bail out.
if (Math.abs(this.panX) < kMinKineticSpeed &&
Math.abs(this.panY) < kMinKineticSpeed)
break;
// Check if this element can scroll - if not, let's bail out.
@ -522,10 +542,8 @@ var BrowserEventHandler = {
this._scrollElementBy(this.panElement, -this.panX, -this.panY);
// TODO: Use the kinetic scrolling prefs
this.panDeceleration = 0.999;
this.panAccumulatedDeltaX = 0;
this.panAccumulatedDeltaY = 0;
this.panLastTime = window.mozAnimationStartTime;
let self = this;
let panElement = this.panElement;
@ -538,8 +556,8 @@ var BrowserEventHandler = {
self.panLastTime = timeStamp;
// Adjust deceleration
self.panX *= Math.pow(self.panDeceleration, timeDelta);
self.panY *= Math.pow(self.panDeceleration, timeDelta);
self.panX *= Math.pow(kPanDeceleration, timeDelta);
self.panY *= Math.pow(kPanDeceleration, timeDelta);
// Calculate panning motion
let dx = self.panX * timeDelta;
@ -566,20 +584,16 @@ var BrowserEventHandler = {
self._scrollElementBy(panElement, -dx, -dy);
// If we're moving at a rate slower than 0.015
// pixels/ms, stop requesting frames.
// This is roughly equivalent to a pixel every
// 4 frames at 60fps.
if (Math.abs(self.panX) >= 0.015 || Math.abs(self.panY) >= 0.015)
if (Math.abs(self.panX) >= kMinKineticSpeed ||
Math.abs(self.panY) >= kMinKineticSpeed)
window.mozRequestAnimationFrame(this);
}
};
// If one axis is moving more than five times faster than
// the other, lock to that axis.
if (Math.abs(this.panX) < Math.abs(this.panY) / 5)
// If one axis is moving a lot slower than the other, lock it.
if (Math.abs(this.panX) < Math.abs(this.panY) / kAxisLockRatio)
this.panX = 0;
else if (Math.abs(this.panY) < Math.abs(this.panX) / 5)
else if (Math.abs(this.panY) < Math.abs(this.panX) / kAxisLockRatio)
this.panY = 0;
// Start the panning animation
@ -611,16 +625,7 @@ var BrowserEventHandler = {
this.lastY = y;
this.lastTime = Date.now();
for (let i = 0; i < 4; i++) {
if (this.panning) {
this.motionBuffer[i] = this.motionBuffer[i + 1];
} else {
this.motionBuffer[i] = { dx: 0, dy: 0, time: this.lastTime };
}
}
this.motionBuffer[4] =
{ dx: dx, dy: dy, time: this.lastTime };
this.motionBuffer.unshift({ dx: dx, dy: dy, time: this.lastTime });
},
_findScrollableElement: function(elem, checkElem) {