910e62b5创建于 1月15日历史提交
// Copyright 2017 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_TIMELINE_H_
#define CC_ANIMATION_SCROLL_TIMELINE_H_

#include <optional>
#include <vector>

#include "base/time/time.h"
#include "cc/animation/animation_export.h"
#include "cc/animation/animation_timeline.h"
#include "cc/animation/keyframe_model.h"
#include "cc/paint/element_id.h"

namespace cc {

class ScrollTree;

// A ScrollTimeline is an animation timeline that bases its current time on the
// progress of scrolling in some scroll container.
//
// This is the compositor-side representation of the web concept expressed in
// https://wicg.github.io/scroll-animations/#scrolltimeline-interface.
class CC_ANIMATION_EXPORT ScrollTimeline : public AnimationTimeline {
 public:
  // cc does not know about writing modes. The ScrollDirection below is
  // converted using blink::scroll_timeline_util::ConvertOrientation which takes
  // the spec-compliant ScrollDirection enumeration.
  // https://drafts.csswg.org/scroll-animations/#scrolldirection-enumeration
  enum ScrollDirection {
    ScrollUp,
    ScrollDown,
    ScrollLeft,
    ScrollRight,
  };

  struct ScrollOffsets {
    ScrollOffsets() = default;
    ScrollOffsets(double start_offset, double end_offset) {
      start = start_offset;
      end = end_offset;
    }
    bool operator==(const ScrollOffsets& other) const {
      return start == other.start && end == other.end;
    }
    bool operator!=(const ScrollOffsets& other) const {
      return !(*this == other);
    }

    double start = 0;
    double end = 0;
  };

  // Fixed time scale converting from pixels to microseconds.
  // The value is derived from error analysis of the quantization of pixels in
  // LayoutUnits.  The quantization is 1/64 of a pixel, and maximum possible
  // error in current time calculations is 4 times that amount as shown below.
  //
  // progress = (scroll - start) / (end - start)
  // Positions are subject to imprecision based on quantization.
  // For worst case analysis, we compute the difference between the maximum
  // and minimum progress based on the allowable error:
  // progress = ((current offset +/- delta) - (start +/- delta) /
  //            ((end +/- delta) - (start +/- delta))
  // where delta = kLengthPrecision = 1 / kFixedPointDenominator = 1 / 64
  //
  // To minimum, we take the smallest possible numerator and largest possible
  // denominator, which means minimizing current offset and maximizing cover
  // end time.  The cover start time appears in both the numerator and
  // denominator, but has a large impact on the numerator. Thus,
  //
  // min = ((current offset - delta) - (start + delta)) /
  //       ((end + delta) - (start + delta))
  //     = (current offset - start - 2 * delta) / range
  // max = ((current offset + delta) - (start + delta)) /
  //       ((end - delta) - (start + delta))
  //     = (current offset - start + 2 * delta) / range;
  // max error = max - min = 4 * delta / range
  // duration = 1 [microsecond] / error
  //          = range / (4 / 64)
  //          = 16 range
  static constexpr double kScrollTimelineMicrosecondsPerPixel = 16;

  ScrollTimeline(std::optional<ElementId> scroller_id,
                 ScrollDirection direction,
                 std::optional<ScrollOffsets> scroll_offsets,
                 int animation_timeline_id);

  static scoped_refptr<ScrollTimeline> Create(
      std::optional<ElementId> scroller_id,
      ScrollDirection direction,
      std::optional<ScrollOffsets> scroll_offsets);

  // Create a copy of this ScrollTimeline intended for the impl thread in the
  // compositor.
  scoped_refptr<AnimationTimeline> CreateImplInstance() const override;

  // ScrollTimeline is active if the scroll node exists in active or pending
  // scroll tree.
  virtual bool IsActive(const ScrollTree& scroll_tree,
                        bool is_active_tree) const;

  // Calculate the current time of the ScrollTimeline. This is either a
  // base::TimeTicks value or std::nullopt if the current time is unresolved.
  // The internal calculations are performed using doubles and the result is
  // converted to base::TimeTicks. This limits the precision to 1us.
  virtual std::optional<base::TimeTicks> CurrentTime(
      const ScrollTree& scroll_tree,
      bool is_active_tree) const;

  virtual std::optional<base::TimeTicks> Duration(const ScrollTree& scroll_tree,
                                                  bool is_active_tree) const;

  void UpdateScrollerIdAndScrollOffsets(
      std::optional<ElementId> scroller_id,
      std::optional<ScrollOffsets> scroll_offsets);

  void PushPropertiesTo(AnimationTimeline* impl_timeline) override;
  void ActivateTimeline() override;

  bool TickScrollLinkedAnimations(
      const std::vector<scoped_refptr<Animation>>& ticking_animations,
      const ScrollTree& scroll_tree,
      bool is_active_tree) override;

  std::optional<ElementId> GetActiveIdForTest() const { return active_id(); }
  std::optional<ElementId> GetPendingIdForTest() const { return pending_id(); }
  ScrollDirection GetDirectionForTest() const { return direction(); }
  std::optional<double> GetStartScrollOffsetForTest() const {
    std::optional<ScrollOffsets> offsets = pending_offsets();
    if (offsets) {
      return offsets->start;
    }
    return std::nullopt;
  }
  std::optional<double> GetEndScrollOffsetForTest() const {
    std::optional<ScrollOffsets> offsets = pending_offsets();
    if (offsets) {
      return offsets->end;
    }
    return std::nullopt;
  }

  bool IsScrollTimeline() const override;
  bool IsLinkedToScroller(ElementId scroller) const override;

 protected:
  ~ScrollTimeline() override;

 private:
  const std::optional<ElementId>& active_id() const {
    return active_id_.Read(*this);
  }

  const std::optional<ElementId>& pending_id() const {
    return pending_id_.Read(*this);
  }

  const ScrollDirection& direction() const { return direction_.Read(*this); }

  const std::optional<ScrollOffsets>& active_offsets() const {
    return active_offsets_.Read(*this);
  }

  const std::optional<ScrollOffsets>& pending_offsets() const {
    return pending_offsets_.Read(*this);
  }

  // The scroller which this ScrollTimeline is based on. The same underlying
  // scroll source may have different ids in the pending and active tree (see
  // http://crbug.com/847588).

  // Only the impl thread can set active properties.
  ProtectedSequenceForbidden<std::optional<ElementId>> active_id_;
  ProtectedSequenceWritable<std::optional<ElementId>> pending_id_;

  // The direction of the ScrollTimeline indicates which axis of the scroller
  // it should base its current time on, and where the origin point is.
  ProtectedSequenceReadable<ScrollDirection> direction_;

  ProtectedSequenceForbidden<std::optional<ScrollOffsets>> active_offsets_;
  ProtectedSequenceWritable<std::optional<ScrollOffsets>> pending_offsets_;
};

inline ScrollTimeline* ToScrollTimeline(AnimationTimeline* timeline) {
  DCHECK(timeline->IsScrollTimeline());
  return static_cast<ScrollTimeline*>(timeline);
}

inline const ScrollTimeline* ToScrollTimeline(
    const AnimationTimeline* timeline) {
  DCHECK(timeline->IsScrollTimeline());
  return static_cast<const ScrollTimeline*>(timeline);
}

}  // namespace cc

#endif  // CC_ANIMATION_SCROLL_TIMELINE_H_