tor-browser

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

ScrollAnimationBezierPhysics.cpp (6064B)


      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 "ScrollAnimationBezierPhysics.h"
      8 
      9 #include "mozilla/StaticPrefs_general.h"
     10 
     11 using namespace mozilla;
     12 
     13 ScrollAnimationBezierPhysics::ScrollAnimationBezierPhysics(
     14    const nsPoint& aStartPos,
     15    const ScrollAnimationBezierPhysicsSettings& aSettings)
     16    : mSettings(aSettings), mStartPos(aStartPos), mIsFirstIteration(true) {}
     17 
     18 void ScrollAnimationBezierPhysics::Update(const TimeStamp& aTime,
     19                                          const nsPoint& aDestination,
     20                                          const nsSize& aCurrentVelocity) {
     21  if (mIsFirstIteration) {
     22    InitializeHistory(aTime);
     23  }
     24 
     25  TimeDuration duration = ComputeDuration(aTime);
     26  nsSize currentVelocity = aCurrentVelocity;
     27 
     28  if (!mIsFirstIteration) {
     29    // If an additional event has not changed the destination, then do not let
     30    // another minimum duration reset slow things down.  If it would then
     31    // instead continue with the existing timing function.
     32    if (aDestination == mDestination &&
     33        aTime + duration > mStartTime + mDuration) {
     34      return;
     35    }
     36 
     37    currentVelocity = VelocityAt(aTime);
     38    mStartPos = PositionAt(aTime);
     39  }
     40 
     41  mStartTime = aTime;
     42  mDuration = duration;
     43  mDestination = aDestination;
     44  InitTimingFunction(mTimingFunctionX, mStartPos.x, currentVelocity.width,
     45                     aDestination.x);
     46  InitTimingFunction(mTimingFunctionY, mStartPos.y, currentVelocity.height,
     47                     aDestination.y);
     48  mIsFirstIteration = false;
     49 }
     50 
     51 void ScrollAnimationBezierPhysics::ApplyContentShift(
     52    const CSSPoint& aShiftDelta) {
     53  nsPoint shiftDelta = CSSPoint::ToAppUnits(aShiftDelta);
     54  mStartPos += shiftDelta;
     55  mDestination += shiftDelta;
     56 }
     57 
     58 TimeDuration ScrollAnimationBezierPhysics::ComputeDuration(
     59    const TimeStamp& aTime) {
     60  // Average last 3 delta durations (rounding errors up to 2ms are negligible
     61  // for us)
     62  int32_t eventsDeltaMs = (aTime - mPrevEventTime[2]).ToMilliseconds() / 3;
     63  mPrevEventTime[2] = mPrevEventTime[1];
     64  mPrevEventTime[1] = mPrevEventTime[0];
     65  mPrevEventTime[0] = aTime;
     66 
     67  // Modulate duration according to events rate (quicker events -> shorter
     68  // durations). The desired effect is to use longer duration when scrolling
     69  // slowly, such that it's easier to follow, but reduce the duration to make it
     70  // feel more snappy when scrolling quickly. To reduce fluctuations of the
     71  // duration, we average event intervals using the recent 4 timestamps (now +
     72  // three prev -> 3 intervals).
     73  int32_t durationMS =
     74      std::clamp<int32_t>(eventsDeltaMs * mSettings.mIntervalRatio,
     75                          mSettings.mMinMS, mSettings.mMaxMS);
     76 
     77  return TimeDuration::FromMilliseconds(durationMS);
     78 }
     79 
     80 void ScrollAnimationBezierPhysics::InitializeHistory(const TimeStamp& aTime) {
     81  // Starting a new scroll (i.e. not when extending an existing scroll
     82  // animation), create imaginary prev timestamps with maximum relevant
     83  // intervals between them.
     84 
     85  // Longest relevant interval (which results in maximum duration)
     86  TimeDuration maxDelta = TimeDuration::FromMilliseconds(
     87      mSettings.mMaxMS / mSettings.mIntervalRatio);
     88  mPrevEventTime[0] = aTime - maxDelta;
     89  mPrevEventTime[1] = mPrevEventTime[0] - maxDelta;
     90  mPrevEventTime[2] = mPrevEventTime[1] - maxDelta;
     91 }
     92 
     93 void ScrollAnimationBezierPhysics::InitTimingFunction(
     94    SMILKeySpline& aTimingFunction, nscoord aCurrentPos,
     95    nscoord aCurrentVelocity, nscoord aDestination) {
     96  if (aDestination == aCurrentPos ||
     97      StaticPrefs::general_smoothScroll_currentVelocityWeighting() == 0) {
     98    aTimingFunction.Init(
     99        0, 0, 1 - StaticPrefs::general_smoothScroll_stopDecelerationWeighting(),
    100        1);
    101    return;
    102  }
    103 
    104  const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
    105  double slope =
    106      aCurrentVelocity * (mDuration / oneSecond) / (aDestination - aCurrentPos);
    107  double normalization = sqrt(1.0 + slope * slope);
    108  double dt = 1.0 / normalization *
    109              StaticPrefs::general_smoothScroll_currentVelocityWeighting();
    110  double dxy = slope / normalization *
    111               StaticPrefs::general_smoothScroll_currentVelocityWeighting();
    112  aTimingFunction.Init(
    113      dt, dxy,
    114      1 - StaticPrefs::general_smoothScroll_stopDecelerationWeighting(), 1);
    115 }
    116 
    117 nsPoint ScrollAnimationBezierPhysics::PositionAt(const TimeStamp& aTime) {
    118  if (IsFinished(aTime)) {
    119    return mDestination;
    120  }
    121 
    122  double progressX = mTimingFunctionX.GetSplineValue(ProgressAt(aTime));
    123  double progressY = mTimingFunctionY.GetSplineValue(ProgressAt(aTime));
    124  return nsPoint(NSToCoordRound((1 - progressX) * mStartPos.x +
    125                                progressX * mDestination.x),
    126                 NSToCoordRound((1 - progressY) * mStartPos.y +
    127                                progressY * mDestination.y));
    128 }
    129 
    130 nsSize ScrollAnimationBezierPhysics::VelocityAt(const TimeStamp& aTime) {
    131  if (IsFinished(aTime)) {
    132    return nsSize(0, 0);
    133  }
    134 
    135  double timeProgress = ProgressAt(aTime);
    136  return nsSize(VelocityComponent(timeProgress, mTimingFunctionX, mStartPos.x,
    137                                  mDestination.x),
    138                VelocityComponent(timeProgress, mTimingFunctionY, mStartPos.y,
    139                                  mDestination.y));
    140 }
    141 
    142 nscoord ScrollAnimationBezierPhysics::VelocityComponent(
    143    double aTimeProgress, const SMILKeySpline& aTimingFunction, nscoord aStart,
    144    nscoord aDestination) const {
    145  double dt, dxy;
    146  aTimingFunction.GetSplineDerivativeValues(aTimeProgress, dt, dxy);
    147  if (dt == 0) {
    148    return dxy >= 0 ? nscoord_MAX : nscoord_MIN;
    149  }
    150 
    151  const TimeDuration oneSecond = TimeDuration::FromSeconds(1);
    152  double slope = dxy / dt;
    153  return NSToCoordRound(slope * (aDestination - aStart) /
    154                        (mDuration / oneSecond));
    155 }