Factor the guts of AsyncScroll into a base helper class. (bug 1139220 part 2, r=kgilbert)
This commit is contained in:
135
layout/generic/AsyncScrollBase.cpp
Normal file
135
layout/generic/AsyncScrollBase.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "AsyncScrollBase.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
|
AsyncScrollBase::AsyncScrollBase(nsPoint aStartPos)
|
||||||
|
: mIsFirstIteration(true)
|
||||||
|
, mStartPos(aStartPos)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AsyncScrollBase::Update(TimeStamp aTime,
|
||||||
|
nsPoint aDestination,
|
||||||
|
const nsSize& aCurrentVelocity)
|
||||||
|
{
|
||||||
|
TimeDuration duration = ComputeDuration(aTime);
|
||||||
|
nsSize currentVelocity = aCurrentVelocity;
|
||||||
|
|
||||||
|
if (!mIsFirstIteration) {
|
||||||
|
// If an additional event has not changed the destination, then do not let
|
||||||
|
// another minimum duration reset slow things down. If it would then
|
||||||
|
// instead continue with the existing timing function.
|
||||||
|
if (aDestination == mDestination &&
|
||||||
|
aTime + duration > mStartTime + mDuration)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVelocity = VelocityAt(aTime);
|
||||||
|
mStartPos = PositionAt(aTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
mStartTime = aTime;
|
||||||
|
mDuration = duration;
|
||||||
|
mDestination = aDestination;
|
||||||
|
InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
|
||||||
|
aDestination.x);
|
||||||
|
InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
|
||||||
|
aDestination.y);
|
||||||
|
mIsFirstIteration = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeDuration
|
||||||
|
AsyncScrollBase::ComputeDuration(TimeStamp aTime)
|
||||||
|
{
|
||||||
|
// Average last 3 delta durations (rounding errors up to 2ms are negligible for us)
|
||||||
|
int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
|
||||||
|
mPrevEventTime[2] = mPrevEventTime[1];
|
||||||
|
mPrevEventTime[1] = mPrevEventTime[0];
|
||||||
|
mPrevEventTime[0] = aTime;
|
||||||
|
|
||||||
|
// Modulate duration according to events rate (quicker events -> shorter durations).
|
||||||
|
// The desired effect is to use longer duration when scrolling slowly, such that
|
||||||
|
// it's easier to follow, but reduce the duration to make it feel more snappy when
|
||||||
|
// scrolling quickly. To reduce fluctuations of the duration, we average event
|
||||||
|
// intervals using the recent 4 timestamps (now + three prev -> 3 intervals).
|
||||||
|
int32_t durationMS = clamped<int32_t>(eventsDeltaMs * mIntervalRatio, mOriginMinMS, mOriginMaxMS);
|
||||||
|
|
||||||
|
return TimeDuration::FromMilliseconds(durationMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AsyncScrollBase::InitializeHistory(TimeStamp aTime)
|
||||||
|
{
|
||||||
|
// Starting a new scroll (i.e. not when extending an existing scroll animation),
|
||||||
|
// create imaginary prev timestamps with maximum relevant intervals between them.
|
||||||
|
|
||||||
|
// Longest relevant interval (which results in maximum duration)
|
||||||
|
TimeDuration maxDelta = TimeDuration::FromMilliseconds(mOriginMaxMS / mIntervalRatio);
|
||||||
|
mPrevEventTime[0] = aTime - maxDelta;
|
||||||
|
mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
|
||||||
|
mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double kCurrentVelocityWeighting = 0.25;
|
||||||
|
const double kStopDecelerationWeighting = 0.4;
|
||||||
|
|
||||||
|
void
|
||||||
|
AsyncScrollBase::InitTimingFunction(nsSMILKeySpline& aTimingFunction,
|
||||||
|
nscoord aCurrentPos,
|
||||||
|
nscoord aCurrentVelocity,
|
||||||
|
nscoord aDestination)
|
||||||
|
{
|
||||||
|
if (aDestination == aCurrentPos || kCurrentVelocityWeighting == 0) {
|
||||||
|
aTimingFunction.Init(0, 0, 1 - kStopDecelerationWeighting, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
|
||||||
|
double slope = aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
|
||||||
|
double normalization = sqrt(1.0 + slope * slope);
|
||||||
|
double dt = 1.0 / normalization * kCurrentVelocityWeighting;
|
||||||
|
double dxy = slope / normalization * kCurrentVelocityWeighting;
|
||||||
|
aTimingFunction.Init(dt, dxy, 1 - kStopDecelerationWeighting, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPoint
|
||||||
|
AsyncScrollBase::PositionAt(TimeStamp aTime) const
|
||||||
|
{
|
||||||
|
double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
|
||||||
|
double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
|
||||||
|
return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x + progressX * mDestination.x),
|
||||||
|
NSToCoordRound((1 - progressY) * mStartPos.y + progressY * mDestination.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSize
|
||||||
|
AsyncScrollBase::VelocityAt(TimeStamp aTime) const
|
||||||
|
{
|
||||||
|
double timeProgress = ProgressAt(aTime);
|
||||||
|
return nsSize(VelocityComponent(timeProgress, mTimingFunctionX,
|
||||||
|
mStartPos.x, mDestination.x),
|
||||||
|
VelocityComponent(timeProgress, mTimingFunctionY,
|
||||||
|
mStartPos.y, mDestination.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
nscoord
|
||||||
|
AsyncScrollBase::VelocityComponent(double aTimeProgress,
|
||||||
|
const nsSMILKeySpline& aTimingFunction,
|
||||||
|
nscoord aStart,
|
||||||
|
nscoord aDestination) const
|
||||||
|
{
|
||||||
|
double dt, dxy;
|
||||||
|
aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
|
||||||
|
if (dt == 0)
|
||||||
|
return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
|
||||||
|
|
||||||
|
const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
|
||||||
|
double slope = dxy / dt;
|
||||||
|
return NSToCoordRound(slope * (aDestination - aStart) / (mDuration / oneSecond));
|
||||||
|
}
|
||||||
88
layout/generic/AsyncScrollBase.h
Normal file
88
layout/generic/AsyncScrollBase.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_layout_AsyncScrollBase_h_
|
||||||
|
#define mozilla_layout_AsyncScrollBase_h_
|
||||||
|
|
||||||
|
#include "mozilla/TimeStamp.h"
|
||||||
|
#include "nsPoint.h"
|
||||||
|
#include "nsSMILKeySpline.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
// This is the base class for driving scroll wheel animation on both the
|
||||||
|
// compositor and main thread.
|
||||||
|
class AsyncScrollBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef mozilla::TimeStamp TimeStamp;
|
||||||
|
typedef mozilla::TimeDuration TimeDuration;
|
||||||
|
|
||||||
|
AsyncScrollBase(nsPoint aStartPos);
|
||||||
|
|
||||||
|
void Update(TimeStamp aTime,
|
||||||
|
nsPoint aDestination,
|
||||||
|
const nsSize& aCurrentVelocity);
|
||||||
|
|
||||||
|
// Get the velocity at a point in time in nscoords/sec.
|
||||||
|
nsSize VelocityAt(TimeStamp aTime) const;
|
||||||
|
|
||||||
|
// Returns the expected scroll position at a given point in time, in app
|
||||||
|
// units, relative to the scroll frame.
|
||||||
|
nsPoint PositionAt(TimeStamp aTime) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
double ProgressAt(TimeStamp aTime) const {
|
||||||
|
return clamped((aTime - mStartTime) / mDuration, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
nscoord VelocityComponent(double aTimeProgress,
|
||||||
|
const nsSMILKeySpline& aTimingFunction,
|
||||||
|
nscoord aStart, nscoord aDestination) const;
|
||||||
|
|
||||||
|
// Calculate duration, possibly dynamically according to events rate and
|
||||||
|
// event origin. (also maintain previous timestamps - which are only used
|
||||||
|
// here).
|
||||||
|
TimeDuration ComputeDuration(TimeStamp aTime);
|
||||||
|
|
||||||
|
// Initialize event history.
|
||||||
|
void InitializeHistory(TimeStamp aTime);
|
||||||
|
|
||||||
|
// Initializes the timing function in such a way that the current velocity is
|
||||||
|
// preserved.
|
||||||
|
void InitTimingFunction(nsSMILKeySpline& aTimingFunction,
|
||||||
|
nscoord aCurrentPos, nscoord aCurrentVelocity,
|
||||||
|
nscoord aDestination);
|
||||||
|
|
||||||
|
// mPrevEventTime holds previous 3 timestamps for intervals averaging (to
|
||||||
|
// reduce duration fluctuations). When AsyncScroll is constructed and no
|
||||||
|
// previous timestamps are available (indicated with mIsFirstIteration),
|
||||||
|
// initialize mPrevEventTime using imaginary previous timestamps with maximum
|
||||||
|
// relevant intervals between them.
|
||||||
|
TimeStamp mPrevEventTime[3];
|
||||||
|
bool mIsFirstIteration;
|
||||||
|
|
||||||
|
TimeStamp mStartTime;
|
||||||
|
|
||||||
|
// Cached Preferences value.
|
||||||
|
//
|
||||||
|
// These values are minimum and maximum animation duration per event origin,
|
||||||
|
// and a global ratio which defines how longer is the animation's duration
|
||||||
|
// compared to the average recent events intervals (such that for a relatively
|
||||||
|
// consistent events rate, the next event arrives before current animation ends)
|
||||||
|
int32_t mOriginMinMS;
|
||||||
|
int32_t mOriginMaxMS;
|
||||||
|
double mIntervalRatio;
|
||||||
|
|
||||||
|
nsPoint mStartPos;
|
||||||
|
TimeDuration mDuration;
|
||||||
|
nsPoint mDestination;
|
||||||
|
nsSMILKeySpline mTimingFunctionX;
|
||||||
|
nsSMILKeySpline mTimingFunctionY;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // mozilla_layout_AsyncScrollBase_h_
|
||||||
@@ -61,6 +61,7 @@ with Files('nsVideoFrame.*'):
|
|||||||
BUG_COMPONENT = ('Core', 'Video/Audio')
|
BUG_COMPONENT = ('Core', 'Video/Audio')
|
||||||
|
|
||||||
EXPORTS += [
|
EXPORTS += [
|
||||||
|
'AsyncScrollBase.h',
|
||||||
'nsCanvasFrame.h',
|
'nsCanvasFrame.h',
|
||||||
'nsContainerFrame.h',
|
'nsContainerFrame.h',
|
||||||
'nsDirection.h',
|
'nsDirection.h',
|
||||||
@@ -104,6 +105,7 @@ EXPORTS.mozilla.layout += [
|
|||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
'AsyncScrollBase.cpp',
|
||||||
'FrameChildList.cpp',
|
'FrameChildList.cpp',
|
||||||
'MathMLTextRunFactory.cpp',
|
'MathMLTextRunFactory.cpp',
|
||||||
'nsAbsoluteContainingBlock.cpp',
|
'nsAbsoluteContainingBlock.cpp',
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
#include "nsIFrameInlines.h"
|
#include "nsIFrameInlines.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
#include "gfxPrefs.h"
|
#include "gfxPrefs.h"
|
||||||
|
#include "AsyncScrollBase.h"
|
||||||
#include <mozilla/layers/AxisPhysicsModel.h>
|
#include <mozilla/layers/AxisPhysicsModel.h>
|
||||||
#include <mozilla/layers/AxisPhysicsMSDModel.h>
|
#include <mozilla/layers/AxisPhysicsMSDModel.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -1447,9 +1448,6 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
|
|||||||
|
|
||||||
#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
|
#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
|
||||||
|
|
||||||
const double kCurrentVelocityWeighting = 0.25;
|
|
||||||
const double kStopDecelerationWeighting = 0.4;
|
|
||||||
|
|
||||||
// AsyncSmoothMSDScroll has ref counting.
|
// AsyncSmoothMSDScroll has ref counting.
|
||||||
class ScrollFrameHelper::AsyncSmoothMSDScroll final : public nsARefreshObserver {
|
class ScrollFrameHelper::AsyncSmoothMSDScroll final : public nsARefreshObserver {
|
||||||
public:
|
public:
|
||||||
@@ -1581,14 +1579,16 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// AsyncScroll has ref counting.
|
// AsyncScroll has ref counting.
|
||||||
class ScrollFrameHelper::AsyncScroll final : public nsARefreshObserver {
|
class ScrollFrameHelper::AsyncScroll final
|
||||||
|
: public nsARefreshObserver,
|
||||||
|
public AsyncScrollBase
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
typedef mozilla::TimeStamp TimeStamp;
|
typedef mozilla::TimeStamp TimeStamp;
|
||||||
typedef mozilla::TimeDuration TimeDuration;
|
typedef mozilla::TimeDuration TimeDuration;
|
||||||
|
|
||||||
explicit AsyncScroll(nsPoint aStartPos)
|
explicit AsyncScroll(nsPoint aStartPos)
|
||||||
: mIsFirstIteration(true)
|
: AsyncScrollBase(aStartPos)
|
||||||
, mStartPos(aStartPos)
|
|
||||||
, mCallee(nullptr)
|
, mCallee(nullptr)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -1599,9 +1599,6 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
nsPoint PositionAt(TimeStamp aTime);
|
|
||||||
nsSize VelocityAt(TimeStamp aTime); // In nscoords per second
|
|
||||||
|
|
||||||
void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
|
void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
|
||||||
nsIAtom *aOrigin, const nsRect& aRange,
|
nsIAtom *aOrigin, const nsRect& aRange,
|
||||||
const nsSize& aCurrentVelocity);
|
const nsSize& aCurrentVelocity);
|
||||||
@@ -1613,53 +1610,15 @@ public:
|
|||||||
return aTime > mStartTime + mDuration; // XXX or if we've hit the wall
|
return aTime > mStartTime + mDuration; // XXX or if we've hit the wall
|
||||||
}
|
}
|
||||||
|
|
||||||
TimeStamp mStartTime;
|
// Most recent scroll origin.
|
||||||
|
|
||||||
// mPrevEventTime holds previous 3 timestamps for intervals averaging (to
|
|
||||||
// reduce duration fluctuations). When AsyncScroll is constructed and no
|
|
||||||
// previous timestamps are available (indicated with mIsFirstIteration),
|
|
||||||
// initialize mPrevEventTime using imaginary previous timestamps with maximum
|
|
||||||
// relevant intervals between them.
|
|
||||||
TimeStamp mPrevEventTime[3];
|
|
||||||
bool mIsFirstIteration;
|
|
||||||
|
|
||||||
// Cached Preferences values to avoid re-reading them when extending an existing
|
|
||||||
// animation for the same event origin (can be as frequent as every 10(!)ms for
|
|
||||||
// a quick roll of the mouse wheel).
|
|
||||||
// These values are minimum and maximum animation duration per event origin,
|
|
||||||
// and a global ratio which defines how longer is the animation's duration
|
|
||||||
// compared to the average recent events intervals (such that for a relatively
|
|
||||||
// consistent events rate, the next event arrives before current animation ends)
|
|
||||||
nsCOMPtr<nsIAtom> mOrigin;
|
nsCOMPtr<nsIAtom> mOrigin;
|
||||||
int32_t mOriginMinMS;
|
|
||||||
int32_t mOriginMaxMS;
|
|
||||||
double mIntervalRatio;
|
|
||||||
|
|
||||||
TimeDuration mDuration;
|
|
||||||
nsPoint mStartPos;
|
|
||||||
nsPoint mDestination;
|
|
||||||
// Allowed destination positions around mDestination
|
// Allowed destination positions around mDestination
|
||||||
nsRect mRange;
|
nsRect mRange;
|
||||||
nsSMILKeySpline mTimingFunctionX;
|
|
||||||
nsSMILKeySpline mTimingFunctionY;
|
|
||||||
bool mIsSmoothScroll;
|
bool mIsSmoothScroll;
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
double ProgressAt(TimeStamp aTime) {
|
void InitPreferences(TimeStamp aTime, nsIAtom *aOrigin);
|
||||||
return clamped((aTime - mStartTime) / mDuration, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
nscoord VelocityComponent(double aTimeProgress,
|
|
||||||
nsSMILKeySpline& aTimingFunction,
|
|
||||||
nscoord aStart, nscoord aDestination);
|
|
||||||
|
|
||||||
// Initializes the timing function in such a way that the current velocity is
|
|
||||||
// preserved.
|
|
||||||
void InitTimingFunction(nsSMILKeySpline& aTimingFunction,
|
|
||||||
nscoord aCurrentPos, nscoord aCurrentVelocity,
|
|
||||||
nscoord aDestination);
|
|
||||||
|
|
||||||
TimeDuration CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin);
|
|
||||||
|
|
||||||
// The next section is observer/callback management
|
// The next section is observer/callback management
|
||||||
// Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
|
// Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
|
||||||
@@ -1707,36 +1666,22 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
nsPoint
|
|
||||||
ScrollFrameHelper::AsyncScroll::PositionAt(TimeStamp aTime) {
|
|
||||||
double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
|
|
||||||
double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
|
|
||||||
return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x + progressX * mDestination.x),
|
|
||||||
NSToCoordRound((1 - progressY) * mStartPos.y + progressY * mDestination.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
nsSize
|
|
||||||
ScrollFrameHelper::AsyncScroll::VelocityAt(TimeStamp aTime) {
|
|
||||||
double timeProgress = ProgressAt(aTime);
|
|
||||||
return nsSize(VelocityComponent(timeProgress, mTimingFunctionX,
|
|
||||||
mStartPos.x, mDestination.x),
|
|
||||||
VelocityComponent(timeProgress, mTimingFunctionY,
|
|
||||||
mStartPos.y, mDestination.y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate duration, possibly dynamically according to events rate and event origin.
|
* Calculate duration, possibly dynamically according to events rate and event origin.
|
||||||
* (also maintain previous timestamps - which are only used here).
|
* (also maintain previous timestamps - which are only used here).
|
||||||
*/
|
*/
|
||||||
TimeDuration
|
void
|
||||||
ScrollFrameHelper::
|
ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin)
|
||||||
AsyncScroll::CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin) {
|
{
|
||||||
if (!aOrigin){
|
if (!aOrigin){
|
||||||
aOrigin = nsGkAtoms::other;
|
aOrigin = nsGkAtoms::other;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read preferences only on first iteration or for a different event origin.
|
// Read preferences only on first iteration or for a different event origin.
|
||||||
if (mIsFirstIteration || aOrigin != mOrigin) {
|
if (!mIsFirstIteration && aOrigin == mOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mOrigin = aOrigin;
|
mOrigin = aOrigin;
|
||||||
mOriginMinMS = mOriginMaxMS = 0;
|
mOriginMinMS = mOriginMaxMS = 0;
|
||||||
bool isOriginSmoothnessEnabled = false;
|
bool isOriginSmoothnessEnabled = false;
|
||||||
@@ -1772,97 +1717,21 @@ AsyncScroll::CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin) {
|
|||||||
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
||||||
|
|
||||||
if (mIsFirstIteration) {
|
if (mIsFirstIteration) {
|
||||||
// Starting a new scroll (i.e. not when extending an existing scroll animation),
|
InitializeHistory(aTime);
|
||||||
// create imaginary prev timestamps with maximum relevant intervals between them.
|
|
||||||
|
|
||||||
// Longest relevant interval (which results in maximum duration)
|
|
||||||
TimeDuration maxDelta = TimeDuration::FromMilliseconds(mOriginMaxMS / mIntervalRatio);
|
|
||||||
mPrevEventTime[0] = aTime - maxDelta;
|
|
||||||
mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
|
|
||||||
mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Average last 3 delta durations (rounding errors up to 2ms are negligible for us)
|
|
||||||
int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
|
|
||||||
mPrevEventTime[2] = mPrevEventTime[1];
|
|
||||||
mPrevEventTime[1] = mPrevEventTime[0];
|
|
||||||
mPrevEventTime[0] = aTime;
|
|
||||||
|
|
||||||
// Modulate duration according to events rate (quicker events -> shorter durations).
|
|
||||||
// The desired effect is to use longer duration when scrolling slowly, such that
|
|
||||||
// it's easier to follow, but reduce the duration to make it feel more snappy when
|
|
||||||
// scrolling quickly. To reduce fluctuations of the duration, we average event
|
|
||||||
// intervals using the recent 4 timestamps (now + three prev -> 3 intervals).
|
|
||||||
int32_t durationMS = clamped<int32_t>(eventsDeltaMs * mIntervalRatio, mOriginMinMS, mOriginMaxMS);
|
|
||||||
|
|
||||||
return TimeDuration::FromMilliseconds(durationMS);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
|
ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
|
||||||
nsPoint aDestination,
|
nsPoint aDestination,
|
||||||
nsIAtom *aOrigin,
|
nsIAtom *aOrigin,
|
||||||
const nsRect& aRange,
|
const nsRect& aRange,
|
||||||
const nsSize& aCurrentVelocity) {
|
const nsSize& aCurrentVelocity)
|
||||||
|
{
|
||||||
|
InitPreferences(aTime, aOrigin);
|
||||||
mRange = aRange;
|
mRange = aRange;
|
||||||
TimeDuration duration = CalcDurationForEventTime(aTime, aOrigin);
|
|
||||||
nsSize currentVelocity = aCurrentVelocity;
|
|
||||||
if (!mIsFirstIteration) {
|
|
||||||
// If an additional event has not changed the destination, then do not let
|
|
||||||
// another minimum duration reset slow things down. If it would then
|
|
||||||
// instead continue with the existing timing function.
|
|
||||||
if (aDestination == mDestination &&
|
|
||||||
aTime + duration > mStartTime + mDuration)
|
|
||||||
return;
|
|
||||||
|
|
||||||
currentVelocity = VelocityAt(aTime);
|
Update(aTime, aDestination, aCurrentVelocity);
|
||||||
mStartPos = PositionAt(aTime);
|
|
||||||
}
|
|
||||||
mStartTime = aTime;
|
|
||||||
mDuration = duration;
|
|
||||||
mDestination = aDestination;
|
|
||||||
InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
|
|
||||||
aDestination.x);
|
|
||||||
InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
|
|
||||||
aDestination.y);
|
|
||||||
mIsFirstIteration = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
nscoord
|
|
||||||
ScrollFrameHelper::AsyncScroll::VelocityComponent(double aTimeProgress,
|
|
||||||
nsSMILKeySpline& aTimingFunction,
|
|
||||||
nscoord aStart,
|
|
||||||
nscoord aDestination)
|
|
||||||
{
|
|
||||||
double dt, dxy;
|
|
||||||
aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
|
|
||||||
if (dt == 0)
|
|
||||||
return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
|
|
||||||
|
|
||||||
const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
|
|
||||||
double slope = dxy / dt;
|
|
||||||
return NSToCoordRound(slope * (aDestination - aStart) / (mDuration / oneSecond));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ScrollFrameHelper::AsyncScroll::InitTimingFunction(nsSMILKeySpline& aTimingFunction,
|
|
||||||
nscoord aCurrentPos,
|
|
||||||
nscoord aCurrentVelocity,
|
|
||||||
nscoord aDestination)
|
|
||||||
{
|
|
||||||
if (aDestination == aCurrentPos || kCurrentVelocityWeighting == 0) {
|
|
||||||
aTimingFunction.Init(0, 0, 1 - kStopDecelerationWeighting, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
|
|
||||||
double slope = aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
|
|
||||||
double normalization = sqrt(1.0 + slope * slope);
|
|
||||||
double dt = 1.0 / normalization * kCurrentVelocityWeighting;
|
|
||||||
double dxy = slope / normalization * kCurrentVelocityWeighting;
|
|
||||||
aTimingFunction.Init(dt, dxy, 1 - kStopDecelerationWeighting, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|||||||
Reference in New Issue
Block a user