зеркало из https://github.com/mozilla/gecko-dev.git
Factor the guts of AsyncScroll into a base helper class. (bug 1139220 part 2, r=kgilbert)
This commit is contained in:
Родитель
8c72d87fb0
Коммит
0dccb5ef39
|
@ -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));
|
||||
}
|
|
@ -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')
|
||||
|
||||
EXPORTS += [
|
||||
'AsyncScrollBase.h',
|
||||
'nsCanvasFrame.h',
|
||||
'nsContainerFrame.h',
|
||||
'nsDirection.h',
|
||||
|
@ -104,6 +105,7 @@ EXPORTS.mozilla.layout += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AsyncScrollBase.cpp',
|
||||
'FrameChildList.cpp',
|
||||
'MathMLTextRunFactory.cpp',
|
||||
'nsAbsoluteContainingBlock.cpp',
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "nsIFrameInlines.h"
|
||||
#include "gfxPlatform.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "AsyncScrollBase.h"
|
||||
#include <mozilla/layers/AxisPhysicsModel.h>
|
||||
#include <mozilla/layers/AxisPhysicsMSDModel.h>
|
||||
#include <algorithm>
|
||||
|
@ -1447,9 +1448,6 @@ NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
|
|||
|
||||
#define SMOOTH_SCROLL_PREF_NAME "general.smoothScroll"
|
||||
|
||||
const double kCurrentVelocityWeighting = 0.25;
|
||||
const double kStopDecelerationWeighting = 0.4;
|
||||
|
||||
// AsyncSmoothMSDScroll has ref counting.
|
||||
class ScrollFrameHelper::AsyncSmoothMSDScroll final : public nsARefreshObserver {
|
||||
public:
|
||||
|
@ -1581,14 +1579,16 @@ private:
|
|||
};
|
||||
|
||||
// AsyncScroll has ref counting.
|
||||
class ScrollFrameHelper::AsyncScroll final : public nsARefreshObserver {
|
||||
class ScrollFrameHelper::AsyncScroll final
|
||||
: public nsARefreshObserver,
|
||||
public AsyncScrollBase
|
||||
{
|
||||
public:
|
||||
typedef mozilla::TimeStamp TimeStamp;
|
||||
typedef mozilla::TimeDuration TimeDuration;
|
||||
|
||||
explicit AsyncScroll(nsPoint aStartPos)
|
||||
: mIsFirstIteration(true)
|
||||
, mStartPos(aStartPos)
|
||||
: AsyncScrollBase(aStartPos)
|
||||
, mCallee(nullptr)
|
||||
{}
|
||||
|
||||
|
@ -1599,9 +1599,6 @@ private:
|
|||
}
|
||||
|
||||
public:
|
||||
nsPoint PositionAt(TimeStamp aTime);
|
||||
nsSize VelocityAt(TimeStamp aTime); // In nscoords per second
|
||||
|
||||
void InitSmoothScroll(TimeStamp aTime, nsPoint aDestination,
|
||||
nsIAtom *aOrigin, const nsRect& aRange,
|
||||
const nsSize& aCurrentVelocity);
|
||||
|
@ -1613,53 +1610,15 @@ public:
|
|||
return aTime > mStartTime + mDuration; // XXX or if we've hit the wall
|
||||
}
|
||||
|
||||
TimeStamp mStartTime;
|
||||
|
||||
// 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)
|
||||
// Most recent scroll origin.
|
||||
nsCOMPtr<nsIAtom> mOrigin;
|
||||
int32_t mOriginMinMS;
|
||||
int32_t mOriginMaxMS;
|
||||
double mIntervalRatio;
|
||||
|
||||
TimeDuration mDuration;
|
||||
nsPoint mStartPos;
|
||||
nsPoint mDestination;
|
||||
// Allowed destination positions around mDestination
|
||||
nsRect mRange;
|
||||
nsSMILKeySpline mTimingFunctionX;
|
||||
nsSMILKeySpline mTimingFunctionY;
|
||||
bool mIsSmoothScroll;
|
||||
|
||||
protected:
|
||||
double ProgressAt(TimeStamp aTime) {
|
||||
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);
|
||||
private:
|
||||
void InitPreferences(TimeStamp aTime, nsIAtom *aOrigin);
|
||||
|
||||
// The next section is observer/callback management
|
||||
// Bodies of WillRefresh and RefreshDriver contain ScrollFrameHelper specific code.
|
||||
|
@ -1707,162 +1666,72 @@ 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.
|
||||
* (also maintain previous timestamps - which are only used here).
|
||||
*/
|
||||
TimeDuration
|
||||
ScrollFrameHelper::
|
||||
AsyncScroll::CalcDurationForEventTime(TimeStamp aTime, nsIAtom *aOrigin) {
|
||||
void
|
||||
ScrollFrameHelper::AsyncScroll::InitPreferences(TimeStamp aTime, nsIAtom *aOrigin)
|
||||
{
|
||||
if (!aOrigin){
|
||||
aOrigin = nsGkAtoms::other;
|
||||
}
|
||||
|
||||
// Read preferences only on first iteration or for a different event origin.
|
||||
if (mIsFirstIteration || aOrigin != mOrigin) {
|
||||
mOrigin = aOrigin;
|
||||
mOriginMinMS = mOriginMaxMS = 0;
|
||||
bool isOriginSmoothnessEnabled = false;
|
||||
mIntervalRatio = 1;
|
||||
|
||||
// Default values for all preferences are defined in all.js
|
||||
static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
|
||||
static const bool kDefaultIsSmoothEnabled = true;
|
||||
|
||||
nsAutoCString originName;
|
||||
aOrigin->ToUTF8String(originName);
|
||||
nsAutoCString prefBase = NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
|
||||
|
||||
isOriginSmoothnessEnabled = Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
|
||||
if (isOriginSmoothnessEnabled) {
|
||||
nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
|
||||
nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
|
||||
mOriginMinMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
|
||||
mOriginMaxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
|
||||
|
||||
static const int32_t kSmoothScrollMaxAllowedAnimationDurationMS = 10000;
|
||||
mOriginMaxMS = clamped(mOriginMaxMS, 0, kSmoothScrollMaxAllowedAnimationDurationMS);
|
||||
mOriginMinMS = clamped(mOriginMinMS, 0, mOriginMaxMS);
|
||||
}
|
||||
|
||||
// Keep the animation duration longer than the average event intervals
|
||||
// (to "connect" consecutive scroll animations before the scroll comes to a stop).
|
||||
static const double kDefaultDurationToIntervalRatio = 2; // Duplicated at all.js
|
||||
mIntervalRatio = Preferences::GetInt("general.smoothScroll.durationToIntervalRatio",
|
||||
kDefaultDurationToIntervalRatio * 100) / 100.0;
|
||||
|
||||
// Duration should be at least as long as the intervals -> ratio is at least 1
|
||||
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
||||
|
||||
if (mIsFirstIteration) {
|
||||
// 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;
|
||||
}
|
||||
if (!mIsFirstIteration && aOrigin == mOrigin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
mOrigin = aOrigin;
|
||||
mOriginMinMS = mOriginMaxMS = 0;
|
||||
bool isOriginSmoothnessEnabled = false;
|
||||
mIntervalRatio = 1;
|
||||
|
||||
// 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);
|
||||
// Default values for all preferences are defined in all.js
|
||||
static const int32_t kDefaultMinMS = 150, kDefaultMaxMS = 150;
|
||||
static const bool kDefaultIsSmoothEnabled = true;
|
||||
|
||||
return TimeDuration::FromMilliseconds(durationMS);
|
||||
nsAutoCString originName;
|
||||
aOrigin->ToUTF8String(originName);
|
||||
nsAutoCString prefBase = NS_LITERAL_CSTRING("general.smoothScroll.") + originName;
|
||||
|
||||
isOriginSmoothnessEnabled = Preferences::GetBool(prefBase.get(), kDefaultIsSmoothEnabled);
|
||||
if (isOriginSmoothnessEnabled) {
|
||||
nsAutoCString prefMin = prefBase + NS_LITERAL_CSTRING(".durationMinMS");
|
||||
nsAutoCString prefMax = prefBase + NS_LITERAL_CSTRING(".durationMaxMS");
|
||||
mOriginMinMS = Preferences::GetInt(prefMin.get(), kDefaultMinMS);
|
||||
mOriginMaxMS = Preferences::GetInt(prefMax.get(), kDefaultMaxMS);
|
||||
|
||||
static const int32_t kSmoothScrollMaxAllowedAnimationDurationMS = 10000;
|
||||
mOriginMaxMS = clamped(mOriginMaxMS, 0, kSmoothScrollMaxAllowedAnimationDurationMS);
|
||||
mOriginMinMS = clamped(mOriginMinMS, 0, mOriginMaxMS);
|
||||
}
|
||||
|
||||
// Keep the animation duration longer than the average event intervals
|
||||
// (to "connect" consecutive scroll animations before the scroll comes to a stop).
|
||||
static const double kDefaultDurationToIntervalRatio = 2; // Duplicated at all.js
|
||||
mIntervalRatio = Preferences::GetInt("general.smoothScroll.durationToIntervalRatio",
|
||||
kDefaultDurationToIntervalRatio * 100) / 100.0;
|
||||
|
||||
// Duration should be at least as long as the intervals -> ratio is at least 1
|
||||
mIntervalRatio = std::max(1.0, mIntervalRatio);
|
||||
|
||||
if (mIsFirstIteration) {
|
||||
InitializeHistory(aTime);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ScrollFrameHelper::AsyncScroll::InitSmoothScroll(TimeStamp aTime,
|
||||
nsPoint aDestination,
|
||||
nsIAtom *aOrigin,
|
||||
const nsRect& aRange,
|
||||
const nsSize& aCurrentVelocity) {
|
||||
nsPoint aDestination,
|
||||
nsIAtom *aOrigin,
|
||||
const nsRect& aRange,
|
||||
const nsSize& aCurrentVelocity)
|
||||
{
|
||||
InitPreferences(aTime, aOrigin);
|
||||
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);
|
||||
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);
|
||||
Update(aTime, aDestination, aCurrentVelocity);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
Загрузка…
Ссылка в новой задаче