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 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
|
// 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_
|