author | L. David Baron <dbaron@dbaron.org> |
Sat, 09 Apr 2011 18:22:06 -0700 | |
changeset 67750 | 91b90242f8822434681fe37ab20b53a1320618f8 |
parent 67749 | 18db426b1879162867bbb83579a145e90cdde042 |
child 67751 | f91f9993012233b3d8a1a1a013c2219a783bfec9 |
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) |
bugs | 639179 |
milestone | 2.2a1pre |
backs out | fd6ad8e567c9139c15d8323f16ddef26fe9a471a |
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
|
mobile/app/mobile.js | file | annotate | diff | comparison | revisions | |
mobile/chrome/content/input.js | file | annotate | diff | comparison | revisions |
--- a/mobile/app/mobile.js +++ b/mobile/app/mobile.js @@ -390,19 +390,18 @@ pref("javascript.options.methodjit.chrom pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome pref("dom.max_script_run_time", 20); // JS error console pref("devtools.errorconsole.enabled", false); // kinetic tweakables -pref("browser.ui.kinetic.updateInterval", 16); -pref("browser.ui.kinetic.exponentialC", 1400); -pref("browser.ui.kinetic.polynomialC", 100); +pref("browser.ui.kinetic.updateInterval", 30); +pref("browser.ui.kinetic.decelerationRate", 20); pref("browser.ui.kinetic.swipeLength", 160); // zooming pref("browser.ui.zoom.pageFitGranularity", 9); // don't zoom to fit by less than 1/9 (11%) pref("browser.ui.zoom.animationDuration", 200); // ms duration of double-tap zoom animation pref("browser.ui.zoom.reflow", false); // Change text wrapping on double-tap pref("browser.ui.zoom.reflow.fontSize", 720);
--- a/mobile/chrome/content/input.js +++ b/mobile/chrome/content/input.js @@ -829,19 +829,18 @@ function KineticController(aPanBy, aEndC this._velocity = new Point(0, 0); this._acceleration = new Point(0, 0); this._time = 0; this._timeStart = 0; // How often do we change the position of the scroll pane? Too often and panning may jerk near // the end. Too little and panning will be choppy. In milliseconds. this._updateInterval = Services.prefs.getIntPref("browser.ui.kinetic.updateInterval"); - // Constants that affect the "friction" of the scroll pane. - this._exponentialC = Services.prefs.getIntPref("browser.ui.kinetic.exponentialC"); - this._polynomialC = Services.prefs.getIntPref("browser.ui.kinetic.polynomialC") / 1000000; + // "Friction" of the scroll pane. The lower, the less friction and the further distance traveled. + this._decelerationRate = Services.prefs.getIntPref("browser.ui.kinetic.decelerationRate") / 10000; // Number of milliseconds that can contain a swipe. Movements earlier than this are disregarded. this._swipeLength = Services.prefs.getIntPref("browser.ui.kinetic.swipeLength"); this._reset(); } KineticController.prototype = { _reset: function _reset() { @@ -851,82 +850,90 @@ KineticController.prototype = { this._velocity.set(0, 0); }, isActive: function isActive() { return this._active; }, _startTimer: function _startTimer() { - let self = this; + // Use closed form of a parabola to calculate each position for panning. + // x(t) = v0*t + .5*t^2*a + // where: v0 is initial velocity + // a is acceleration + // t is time elapsed + // + // x(t) + // ^ + // | | + // | + // | | + // | ....^^^^.... + // | ...^^ | ^^... + // | ...^ ^... + // |.. | .. + // -----------------------------------> t + // t0 tf=-v0/a + // + // Using this formula, distance moved is independent of the time between each frame, unlike time + // step approaches. Once the time is up, set the position to x(tf) and stop the timer. - let lastp = this._position; // track last position vector because pan takes deltas + let lastx = this._position; // track last position vector because pan takes differences let v0 = this._velocity; // initial velocity let a = this._acceleration; // acceleration - let c = this._exponentialC; - let p = new Point(0, 0); - let dx, dy, t, realt; - function calcP(v0, a, t) { - // Important traits for this function: - // p(t=0) is 0 - // p'(t=0) is v0 - // - // We use exponential to get a smoother stop, but by itself exponential - // is too smooth at the end. Adding a polynomial with the appropriate - // weight helps to balance - return v0 * Math.exp(-t / c) * -c + a * t * t + v0 * c; - } - - this._calcV = function(v0, a, t) { - return v0 * Math.exp(-t / c) + 2 * a * t; - } + // 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) { // 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. - realt = timeStamp - self._initialTime; + let realt = timeStamp - self._initialTime; self._time += self._updateInterval; - t = (self._time + realt) / 2; + let t = (self._time + realt) / 2; - // Calculate new position. - p.x = calcP(v0.x, a.x, t); - p.y = calcP(v0.y, a.y, t); - dx = Math.round(p.x - lastp.x); - dy = Math.round(p.y - lastp.y); + // 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 = Math.round(x.x - lastx.x); + let dy = Math.round(x.y - lastx.y); - // Test to see if movement is finished for each component. - if (dx * a.x > 0) { - dx = 0; - lastp.x = 0; + // Test to see if movement is finished for each component. As seen in graph, we want the + // final position to be at tf. + if (t >= -v0.x / a.x) { + // Plug in t=-v0/a into x(t) to get final position. + dx = Math.round(-v0.x * v0.x / 2 / a.x - lastx.x); + // Reset components. Next frame: a's component will be 0 and t >= NaN will be false. + lastx.x = 0; v0.x = 0; a.x = 0; } // Symmetric to above case. - if (dy * a.y > 0) { - dy = 0; - lastp.y = 0; + if (t >= -v0.y / a.y) { + dy = Math.round(-v0.y * v0.y / 2 / a.y - lastx.y); + lastx.y = 0; v0.y = 0; a.y = 0; } if (v0.x == 0 && v0.y == 0) { self.end(); } else { let panStop = false; if (dx != 0 || dy != 0) { try { panStop = !self._panBy(-dx, -dy, true); } catch (e) {} - lastp.add(dx, dy); + lastx.add(dx, dy); } if (panStop) self.end(); else mozRequestAnimationFrame(this); } } @@ -937,22 +944,16 @@ KineticController.prototype = { mozRequestAnimationFrame(callback); }, start: function start() { function sign(x) { return x ? ((x > 0) ? 1 : -1) : 0; } - function clampFromZero(x, closerToZero, furtherFromZero) { - if (x >= 0) - return Math.max(closerToZero, Math.min(furtherFromZero, x)); - return Math.min(-closerToZero, Math.max(-furtherFromZero, x)); - } - let mb = this.momentumBuffer; let mblen = this.momentumBuffer.length; let lastTime = mb[mblen - 1].t; let distanceX = 0; let distanceY = 0; let swipeLength = this._swipeLength; @@ -965,33 +966,32 @@ KineticController.prototype = { distanceY += me.dy; } } let currentVelocityX = 0; let currentVelocityY = 0; if (this.isActive()) { - // If active, then we expect this._calcV to be defined. let currentTime = Date.now() - this._initialTime; - currentVelocityX = Util.clamp(this._calcV(this._velocity.x, this._acceleration.x, currentTime), -kMaxVelocity, kMaxVelocity); - currentVelocityY = Util.clamp(this._calcV(this._velocity.y, this._acceleration.y, currentTime), -kMaxVelocity, kMaxVelocity); + 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; let swipeTime = Math.min(swipeLength, lastTime - mb[0].t); this._velocity.x = clampFromZero((distanceX / swipeTime) + currentVelocityX, Math.abs(currentVelocityX), 6); this._velocity.y = clampFromZero((distanceY / swipeTime) + currentVelocityY, Math.abs(currentVelocityY), 6); // Set acceleration vector to opposite signs of velocity - this._acceleration.set(this._velocity.clone().map(sign).scale(-this._polynomialC)); + 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() || this._paused) this._startTimer();