tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ScrollAnimationMSDPhysics.cpp (8021B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "ScrollAnimationMSDPhysics.h"
      8 
      9 #include "mozilla/Logging.h"
     10 #include "mozilla/StaticPrefs_general.h"
     11 #include "mozilla/StaticPrefs_layout.h"
     12 #include "mozilla/ToString.h"
     13 
     14 static mozilla::LazyLogModule sApzMsdLog("apz.msd");
     15 #define MSD_LOG(...) MOZ_LOG(sApzMsdLog, LogLevel::Debug, (__VA_ARGS__))
     16 
     17 using namespace mozilla;
     18 
     19 ScrollAnimationMSDPhysics::ScrollAnimationMSDPhysics(
     20    ScrollAnimationKind aAnimationKind, const nsPoint& aStartPos,
     21    nscoord aSmallestVisibleIncrement)
     22    : mAnimationKind(aAnimationKind),
     23      mSmallestVisibleIncrement(aSmallestVisibleIncrement),
     24      mStartPos(aStartPos),
     25      mModelX(
     26          0, 0, 0,
     27          StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
     28          1),
     29      mModelY(
     30          0, 0, 0,
     31          StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant(),
     32          1),
     33      mIsFirstIteration(true) {}
     34 
     35 void ScrollAnimationMSDPhysics::Update(const TimeStamp& aTime,
     36                                       const nsPoint& aDestination,
     37                                       const nsSize& aCurrentVelocity) {
     38  double springConstant = ComputeSpringConstant(aTime);
     39  double dampingRatio = GetDampingRatio();
     40 
     41  // mLastSimulatedTime is the most recent time that this animation has been
     42  // "observed" at. We don't want to update back to a state in the past, so we
     43  // set mStartTime to the more recent of mLastSimulatedTime and aTime.
     44  // aTime can be in the past if we're processing an input event whose internal
     45  // timestamp is in the past.
     46  if (mLastSimulatedTime && aTime < mLastSimulatedTime) {
     47    mStartTime = mLastSimulatedTime;
     48  } else {
     49    mStartTime = aTime;
     50  }
     51 
     52  if (!mIsFirstIteration) {
     53    mStartPos = PositionAt(mStartTime);
     54  }
     55 
     56  mLastSimulatedTime = mStartTime;
     57  mDestination = aDestination;
     58  mModelX = NonOscillatingAxisPhysicsMSDModel(mStartPos.x, aDestination.x,
     59                                              aCurrentVelocity.width,
     60                                              springConstant, dampingRatio);
     61  mModelY = NonOscillatingAxisPhysicsMSDModel(mStartPos.y, aDestination.y,
     62                                              aCurrentVelocity.height,
     63                                              springConstant, dampingRatio);
     64  mIsFirstIteration = false;
     65 }
     66 
     67 void ScrollAnimationMSDPhysics::ApplyContentShift(const CSSPoint& aShiftDelta) {
     68  nsPoint shiftDelta = CSSPoint::ToAppUnits(aShiftDelta);
     69  mStartPos += shiftDelta;
     70  mDestination += shiftDelta;
     71  TimeStamp currentTime = mLastSimulatedTime;
     72  nsPoint currentPosition = PositionAt(currentTime) + shiftDelta;
     73  nsSize currentVelocity = VelocityAt(currentTime);
     74  double springConstant = ComputeSpringConstant(currentTime);
     75  double dampingRatio = GetDampingRatio();
     76  mModelX = NonOscillatingAxisPhysicsMSDModel(currentPosition.x, mDestination.x,
     77                                              currentVelocity.width,
     78                                              springConstant, dampingRatio);
     79  mModelY = NonOscillatingAxisPhysicsMSDModel(currentPosition.y, mDestination.y,
     80                                              currentVelocity.height,
     81                                              springConstant, dampingRatio);
     82 }
     83 
     84 double ScrollAnimationMSDPhysics::GetDampingRatio() const {
     85  if (mAnimationKind == ScrollAnimationKind::SmoothMsd) {
     86    return StaticPrefs::layout_css_scroll_snap_damping_ratio();
     87  }
     88  return 1.0;
     89 }
     90 
     91 double ScrollAnimationMSDPhysics::ComputeSpringConstant(
     92    const TimeStamp& aTime) {
     93  if (mAnimationKind == ScrollAnimationKind::SmoothMsd) {
     94    return StaticPrefs::layout_css_scroll_snap_spring_constant();
     95  }
     96 
     97  if (!mPreviousEventTime) {
     98    mPreviousEventTime = aTime;
     99    mPreviousDelta = TimeDuration();
    100    return StaticPrefs::
    101        general_smoothScroll_msdPhysics_motionBeginSpringConstant();
    102  }
    103 
    104  TimeDuration delta = aTime - mPreviousEventTime;
    105  TimeDuration previousDelta = mPreviousDelta;
    106 
    107  mPreviousEventTime = aTime;
    108  mPreviousDelta = delta;
    109 
    110  double deltaMS = delta.ToMilliseconds();
    111  if (deltaMS >=
    112      StaticPrefs::
    113          general_smoothScroll_msdPhysics_continuousMotionMaxDeltaMS()) {
    114    return StaticPrefs::
    115        general_smoothScroll_msdPhysics_motionBeginSpringConstant();
    116  }
    117 
    118  if (previousDelta &&
    119      deltaMS >=
    120          StaticPrefs::general_smoothScroll_msdPhysics_slowdownMinDeltaMS() &&
    121      deltaMS >=
    122          previousDelta.ToMilliseconds() *
    123              StaticPrefs::
    124                  general_smoothScroll_msdPhysics_slowdownMinDeltaRatio()) {
    125    // The rate of events has slowed (the time delta between events has
    126    // increased) enough that we think that the current scroll motion is coming
    127    // to a stop. Use a stiffer spring in order to reach the destination more
    128    // quickly.
    129    return StaticPrefs::
    130        general_smoothScroll_msdPhysics_slowdownSpringConstant();
    131  }
    132 
    133  return StaticPrefs::general_smoothScroll_msdPhysics_regularSpringConstant();
    134 }
    135 
    136 void ScrollAnimationMSDPhysics::SimulateUntil(const TimeStamp& aTime) {
    137  if (!mLastSimulatedTime || aTime <= mLastSimulatedTime) {
    138    return;
    139  }
    140  TimeDuration delta = aTime - mLastSimulatedTime;
    141  mModelX.Simulate(delta);
    142  mModelY.Simulate(delta);
    143  mLastSimulatedTime = aTime;
    144  MSD_LOG("Simulated for duration %f, finished %d position %s velocity %s\n",
    145          delta.ToMilliseconds(), IsFinished(aTime),
    146          ToString(CSSPoint::FromAppUnits(PositionAt(aTime))).c_str(),
    147          ToString(CSSPoint::FromAppUnits(VelocityAt(aTime))).c_str());
    148 }
    149 
    150 bool mozilla::ScrollAnimationMSDPhysics::IsFinished(const TimeStamp& aTime) {
    151  SimulateUntil(aTime);
    152  return mModelX.IsFinished(mSmallestVisibleIncrement) &&
    153         mModelY.IsFinished(mSmallestVisibleIncrement);
    154 }
    155 
    156 nsPoint ScrollAnimationMSDPhysics::PositionAt(const TimeStamp& aTime) {
    157  SimulateUntil(aTime);
    158  return nsPoint(NSToCoordRound(mModelX.GetPosition()),
    159                 NSToCoordRound(mModelY.GetPosition()));
    160 }
    161 
    162 nsSize ScrollAnimationMSDPhysics::VelocityAt(const TimeStamp& aTime) {
    163  SimulateUntil(aTime);
    164  return nsSize(NSToCoordRound(mModelX.GetVelocity()),
    165                NSToCoordRound(mModelY.GetVelocity()));
    166 }
    167 
    168 static double ClampVelocityToMaximum(double aVelocity, double aInitialPosition,
    169                                     double aDestination,
    170                                     double aSpringConstant) {
    171  // Clamp velocity to the maximum value it could obtain if we started at this
    172  // position with zero velocity (see bug 1866904 comment 3). With a damping
    173  // ratio >= 1.0, this should be low enough to avoid overshooting the
    174  // destination.
    175  double velocityLimit =
    176      sqrt(aSpringConstant) * abs(aDestination - aInitialPosition);
    177  return std::clamp(aVelocity, -velocityLimit, velocityLimit);
    178 }
    179 
    180 ScrollAnimationMSDPhysics::NonOscillatingAxisPhysicsMSDModel::
    181    NonOscillatingAxisPhysicsMSDModel(double aInitialPosition,
    182                                      double aInitialDestination,
    183                                      double aInitialVelocity,
    184                                      double aSpringConstant,
    185                                      double aDampingRatio)
    186    : AxisPhysicsMSDModel(
    187          aInitialPosition, aInitialDestination,
    188          ClampVelocityToMaximum(aInitialVelocity, aInitialPosition,
    189                                 aInitialDestination, aSpringConstant),
    190          aSpringConstant, aDampingRatio) {
    191  MSD_LOG("Constructing axis physics model with parameters %f %f %f %f %f\n",
    192          aInitialPosition, aInitialDestination, aInitialVelocity,
    193          aSpringConstant, aDampingRatio);
    194  MOZ_ASSERT(aDampingRatio >= 1.0,
    195             "Damping ratio must be >= 1.0 to avoid oscillation");
    196 }