Bug 587814 - Smoother panning using mozAnimationFrame [r=mfinkle]
--- a/mobile/chrome/content/InputHandler.js
+++ b/mobile/chrome/content/InputHandler.js
@@ -957,17 +957,16 @@ DragData.prototype = {
* object was panned, false if there was no movement.
*
* There are two complicated things done here. One is calculating the
* initial velocity of the movement based on user input. Two is
* calculating the distance to move every frame.
*/
function KineticController(aPanBy, aEndCallback) {
this._panBy = aPanBy;
- this._timer = null;
this._beforeEnd = aEndCallback;
// These are used to calculate the position of the scroll panes during kinetic panning. Think of
// these points as vectors that are added together and multiplied by scalars.
this._position = new Point(0, 0);
this._velocity = new Point(0, 0);
this._acceleration = new Point(0, 0);
this._time = 0;
@@ -1030,29 +1029,28 @@ KineticController.prototype = {
let lastx = this._position; // track last position vector because pan takes differences
let v0 = this._velocity; // initial velocity
let a = this._acceleration; // acceleration
// 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 = {
- _self: this,
- notify: function kineticTimerCallback(timer) {
- let self = this._self;
+ handleEvent: function kineticHandleEvent(event) {
if (!self.isActive()) // someone called end() on us between timer intervals
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 = Date.now() - self._initialTime;
+ let realt = event.timeStamp - self._initialTime;
self._time += self._updateInterval;
let t = (self._time + realt) / 2;
// Calculate new position using x(t) formula.
let x = v0Bin.set(v0).scale(t).add(aBin.set(a).scale(0.5 * t * t));
let dx = x.x - lastx.x;
let dy = x.y - lastx.y;
lastx.set(x);
@@ -1074,24 +1072,24 @@ KineticController.prototype = {
v0.y = 0;
a.y = 0;
}
let panned = false;
try { panned = self._panBy(Math.round(-dx), Math.round(-dy)); } catch (e) {}
if (!panned)
self.end();
+ else
+ mozRequestAnimationFrame();
}
};
- this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- //initialize our timer with updateInterval
- this._timer.initWithCallback(callback,
- this._updateInterval,
- this._timer.TYPE_REPEATING_SLACK);
+ this._callback = callback;
+ addEventListener("MozBeforePaint", callback, false);
+ mozRequestAnimationFrame();
},
start: function start() {
function sign(x) {
return x ? ((x > 0) ? 1 : -1) : 0;
}
let mb = this.momentumBuffer;
@@ -1115,17 +1113,17 @@ KineticController.prototype = {
// 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);
// 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 = Date.now();
+ this._initialTime = mozAnimationStartTime;
this._time = 0;
this.momentumBuffer = [];
if (!this.isActive())
this._startTimer();
return true;
},