Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "AsyncScrollBase.h"
7 : #include "gfxPrefs.h"
8 :
9 : using namespace mozilla;
10 :
11 0 : AsyncScrollBase::AsyncScrollBase(nsPoint aStartPos)
12 : : mIsFirstIteration(true)
13 0 : , mStartPos(aStartPos)
14 : {
15 0 : }
16 :
17 : void
18 0 : AsyncScrollBase::Update(TimeStamp aTime,
19 : nsPoint aDestination,
20 : const nsSize& aCurrentVelocity)
21 : {
22 0 : TimeDuration duration = ComputeDuration(aTime);
23 0 : nsSize currentVelocity = aCurrentVelocity;
24 :
25 0 : if (!mIsFirstIteration) {
26 : // If an additional event has not changed the destination, then do not let
27 : // another minimum duration reset slow things down. If it would then
28 : // instead continue with the existing timing function.
29 0 : if (aDestination == mDestination &&
30 0 : aTime + duration > mStartTime + mDuration)
31 : {
32 0 : return;
33 : }
34 :
35 0 : currentVelocity = VelocityAt(aTime);
36 0 : mStartPos = PositionAt(aTime);
37 : }
38 :
39 0 : mStartTime = aTime;
40 0 : mDuration = duration;
41 0 : mDestination = aDestination;
42 0 : InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
43 0 : aDestination.x);
44 0 : InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
45 0 : aDestination.y);
46 0 : mIsFirstIteration = false;
47 : }
48 :
49 : TimeDuration
50 0 : AsyncScrollBase::ComputeDuration(TimeStamp aTime)
51 : {
52 : // Average last 3 delta durations (rounding errors up to 2ms are negligible for us)
53 0 : int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
54 0 : mPrevEventTime[2] = mPrevEventTime[1];
55 0 : mPrevEventTime[1] = mPrevEventTime[0];
56 0 : mPrevEventTime[0] = aTime;
57 :
58 : // Modulate duration according to events rate (quicker events -> shorter durations).
59 : // The desired effect is to use longer duration when scrolling slowly, such that
60 : // it's easier to follow, but reduce the duration to make it feel more snappy when
61 : // scrolling quickly. To reduce fluctuations of the duration, we average event
62 : // intervals using the recent 4 timestamps (now + three prev -> 3 intervals).
63 0 : int32_t durationMS = clamped<int32_t>(eventsDeltaMs * mIntervalRatio, mOriginMinMS, mOriginMaxMS);
64 :
65 0 : return TimeDuration::FromMilliseconds(durationMS);
66 : }
67 :
68 : void
69 0 : AsyncScrollBase::InitializeHistory(TimeStamp aTime)
70 : {
71 : // Starting a new scroll (i.e. not when extending an existing scroll animation),
72 : // create imaginary prev timestamps with maximum relevant intervals between them.
73 :
74 : // Longest relevant interval (which results in maximum duration)
75 0 : TimeDuration maxDelta = TimeDuration::FromMilliseconds(mOriginMaxMS / mIntervalRatio);
76 0 : mPrevEventTime[0] = aTime - maxDelta;
77 0 : mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
78 0 : mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
79 0 : }
80 :
81 : void
82 0 : AsyncScrollBase::InitTimingFunction(nsSMILKeySpline& aTimingFunction,
83 : nscoord aCurrentPos,
84 : nscoord aCurrentVelocity,
85 : nscoord aDestination)
86 : {
87 0 : if (aDestination == aCurrentPos || gfxPrefs::SmoothScrollCurrentVelocityWeighting() == 0) {
88 0 : aTimingFunction.Init(0, 0, 1 - gfxPrefs::SmoothScrollStopDecelerationWeighting(), 1);
89 0 : return;
90 : }
91 :
92 0 : const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
93 0 : double slope = aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
94 0 : double normalization = sqrt(1.0 + slope * slope);
95 0 : double dt = 1.0 / normalization * gfxPrefs::SmoothScrollCurrentVelocityWeighting();
96 0 : double dxy = slope / normalization * gfxPrefs::SmoothScrollCurrentVelocityWeighting();
97 0 : aTimingFunction.Init(dt, dxy, 1 - gfxPrefs::SmoothScrollStopDecelerationWeighting(), 1);
98 : }
99 :
100 : nsPoint
101 0 : AsyncScrollBase::PositionAt(TimeStamp aTime) const
102 : {
103 0 : double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
104 0 : double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
105 0 : return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x + progressX * mDestination.x),
106 0 : NSToCoordRound((1 - progressY) * mStartPos.y + progressY * mDestination.y));
107 : }
108 :
109 : nsSize
110 0 : AsyncScrollBase::VelocityAt(TimeStamp aTime) const
111 : {
112 0 : double timeProgress = ProgressAt(aTime);
113 0 : return nsSize(VelocityComponent(timeProgress, mTimingFunctionX,
114 0 : mStartPos.x, mDestination.x),
115 : VelocityComponent(timeProgress, mTimingFunctionY,
116 0 : mStartPos.y, mDestination.y));
117 : }
118 :
119 : nscoord
120 0 : AsyncScrollBase::VelocityComponent(double aTimeProgress,
121 : const nsSMILKeySpline& aTimingFunction,
122 : nscoord aStart,
123 : nscoord aDestination) const
124 : {
125 : double dt, dxy;
126 0 : aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
127 0 : if (dt == 0)
128 0 : return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
129 :
130 0 : const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
131 0 : double slope = dxy / dt;
132 0 : return NSToCoordRound(slope * (aDestination - aStart) / (mDuration / oneSecond));
133 : }
|