1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168

cc / animation / scroll_offset_animation_curve.h [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_ANIMATION_SCROLL_OFFSET_ANIMATION_CURVE_H_
#define CC_ANIMATION_SCROLL_OFFSET_ANIMATION_CURVE_H_

#include <memory>
#include <optional>

#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "cc/animation/animation_export.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/vector2d_f.h"

namespace gfx {
class TimingFunction;
}  // namespace gfx

namespace cc {

// ScrollOffsetAnimationCurve computes scroll offset as a function of time
// during a scroll offset animation.
//
// Scroll offset animations can run either in Blink or in cc, in response to
// user input or programmatic scroll operations.  For more information about
// scheduling and servicing scroll animations, see blink::ScrollAnimator and
// blink::ProgrammaticScrollAnimator.
class CC_ANIMATION_EXPORT ScrollOffsetAnimationCurve
    : public gfx::AnimationCurve {
 public:
  class Target {
   public:
    ~Target() = default;

    virtual void OnScrollOffsetAnimated(const gfx::PointF& value,
                                        int target_property_id,
                                        gfx::KeyframeModel* keyframe_model) = 0;
  };

  // Indicates how the animation duration should be computed for Ease-in-out
  // style scroll animation curves.
  enum class DurationBehavior {
    // Duration proportional to scroll delta; used for programmatic scrolls.
    kDeltaBased,
    // Constant duration; used for keyboard scrolls.
    kConstant,
    // Duration inversely proportional to scroll delta within certain bounds.
    // Used for mouse wheels, makes fast wheel flings feel "snappy" while
    // preserving smoothness of slow wheel movements.
    kInverseDelta
  };

  static const ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve(
      const AnimationCurve* c);

  static ScrollOffsetAnimationCurve* ToScrollOffsetAnimationCurve(
      AnimationCurve* c);

  // There is inherent delay in input processing; it may take many milliseconds
  // from the time of user input to when when we're actually able to handle it
  // here. This delay is represented by the |delayed_by| value. The way we have
  // decided to factor this in is by reducing the duration of the resulting
  // animation by this delayed amount. This also applies to
  // LinearSegmentDuration.
  static base::TimeDelta EaseInOutSegmentDuration(
      const gfx::Vector2dF& delta,
      DurationBehavior duration_behavior,
      base::TimeDelta delayed_by);

  static base::TimeDelta LinearSegmentDuration(const gfx::Vector2dF& delta,
                                               base::TimeDelta delayed_by,
                                               float velocity);

  ScrollOffsetAnimationCurve(const ScrollOffsetAnimationCurve&) = delete;
  ~ScrollOffsetAnimationCurve() override;

  ScrollOffsetAnimationCurve& operator=(const ScrollOffsetAnimationCurve&) =
      delete;

  // Sets the initial offset and velocity (in pixels per second).
  void SetInitialValue(const gfx::PointF& initial_value,
                       base::TimeDelta delayed_by = base::TimeDelta(),
                       float velocity = 0);
  bool HasSetInitialValue() const;
  gfx::PointF GetValue(base::TimeDelta t) const;
  gfx::PointF target_value() const { return target_value_; }

  // Updates the current curve to aim at a new target, starting at time t
  // relative to the start of the animation. The duration is recomputed based
  // on the animation type the curve was constructed with. The timing function
  // is modified to preserve velocity at t.
  void UpdateTarget(base::TimeDelta t, const gfx::PointF& new_target);

  // Shifts the entire curve by a delta without affecting its shape or timing.
  // Used for scroll anchoring adjustments that happen during scroll animations
  // (see blink::ScrollAnimator::AdjustAnimation).
  void ApplyAdjustment(const gfx::Vector2dF& adjustment);

  // AnimationCurve implementation
  base::TimeDelta Duration() const override;
  int Type() const override;
  const char* TypeName() const override;
  std::unique_ptr<gfx::AnimationCurve> Clone() const override;
  std::unique_ptr<ScrollOffsetAnimationCurve>
  CloneToScrollOffsetAnimationCurve() const;
  void Tick(base::TimeDelta t,
            int property_id,
            gfx::KeyframeModel* keyframe_model,
            gfx::TimingFunction::LimitDirection limit_direction =
                gfx::TimingFunction::LimitDirection::RIGHT) const override;
  static void SetAnimationDurationForTesting(base::TimeDelta duration);
  void set_target(Target* target) { target_ = target; }

 private:
  friend class ScrollOffsetAnimationCurveFactory;
  enum class AnimationType { kLinear, kEaseInOut };

  // |duration_behavior| should be provided if (and only if) |animation_type| is
  // kEaseInOut.
  ScrollOffsetAnimationCurve(
      const gfx::PointF& target_value,
      AnimationType animation_type,
      std::optional<DurationBehavior> duration_behavior = std::nullopt);
  ScrollOffsetAnimationCurve(
      const gfx::PointF& target_value,
      std::unique_ptr<gfx::TimingFunction> timing_function,
      AnimationType animation_type,
      std::optional<DurationBehavior> duration_behavior);

  base::TimeDelta SegmentDuration(
      const gfx::Vector2dF& delta,
      base::TimeDelta delayed_by,
      std::optional<double> velocity = std::nullopt);

  base::TimeDelta EaseInOutBoundedSegmentDuration(
      const gfx::Vector2dF& new_delta,
      base::TimeDelta t,
      base::TimeDelta delayed_by);

  // Returns the velocity at time t in units of pixels per second.
  double CalculateVelocity(base::TimeDelta t);

  gfx::PointF initial_value_;
  gfx::PointF target_value_;
  base::TimeDelta total_animation_duration_;

  // Time from animation start to most recent UpdateTarget.
  base::TimeDelta last_retarget_;

  std::unique_ptr<gfx::TimingFunction> timing_function_;
  AnimationType animation_type_;

  // Only valid when |animation_type_| is EASE_IN_OUT.
  std::optional<DurationBehavior> duration_behavior_;

  bool has_set_initial_value_;

  static std::optional<double> animation_duration_for_testing_;

  raw_ptr<Target, DanglingUntriaged> target_ = nullptr;
};

}  // namespace cc

#endif  // CC_ANIMATION_SCROLL_OFFSET_ANIMATION_CURVE_H_