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 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_KEYFRAME_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_KEYFRAME_H_
#include <optional>
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/animation/effect_model.h"
#include "third_party/blink/renderer/core/animation/property_handle.h"
#include "third_party/blink/renderer/core/animation/timeline_offset.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/animation/timing_function.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"
#include "third_party/blink/renderer/platform/wtf/hash_set.h"
namespace blink {
using PropertyHandleSet = HashSet<PropertyHandle>;
class Element;
class ComputedStyle;
class CompositorKeyframeValue;
class TimelineRange;
class V8ObjectBuilder;
// A base class representing an animation keyframe.
//
// Generically a keyframe is a set of (property, value) pairs. In the
// web-animations spec keyframes have a few additional properties:
//
// * A possibly-null keyframe offset, which represents the keyframe's position
// relative to other keyframes in the same effect.
// * A non-null timing function, which applies to the period of time between
// this keyframe and the next keyframe in the same effect and influences
// the interpolation between them.
// * An keyframe-specific composite operation, which specifies a specific
// composite operation used to combine values in this keyframe with an
// underlying value. If this is 'auto', the keyframe effect composite
// operation is used instead.
//
// For spec details, refer to: https://w3.org/TR/web-animations-1/#keyframe
//
// Implementation-wise the base Keyframe class captures the offset, composite
// operation, and timing function. It is left to subclasses to define and store
// the set of (property, value) pairs.
//
// === PropertySpecificKeyframes ===
//
// When calculating the effect value of a keyframe effect, the web-animations
// spec requires that a set of 'property-specific' keyframes are created.
// Property-specific keyframes resolve any unspecified offsets in the keyframes,
// calculate computed values for the specified properties, convert shorthand
// properties to multiple longhand properties, and resolve any conflicting
// shorthand properties.
//
// In this implementation property-specific keyframes are created only once and
// cached for subsequent calls, rather than re-computing them for every sample
// from the keyframe effect. See KeyframeEffectModelBase::EnsureKeyframeGroups.
//
// FIXME: Make Keyframe immutable
class CORE_EXPORT Keyframe : public GarbageCollected<Keyframe> {
public:
struct EndIterator {};
class VirtualPropertyIterator {
public:
virtual ~VirtualPropertyIterator() = default;
virtual void Advance(const Keyframe* keyframe) = 0;
virtual PropertyHandle Deref(const Keyframe* keyframe) const = 0;
virtual bool AtEnd(const Keyframe* keyframe) const = 0;
};
class CORE_EXPORT PropertyIteratorWrapper {
STACK_ALLOCATED();
public:
explicit PropertyIteratorWrapper(
const Keyframe* keyframe,
std::unique_ptr<VirtualPropertyIterator> impl)
: keyframe_(keyframe), impl_(std::move(impl)) {}
bool operator==(EndIterator other) const { return impl_->AtEnd(keyframe_); }
PropertyIteratorWrapper& operator++() {
impl_->Advance(keyframe_);
return *this;
}
PropertyHandle operator*() const { return impl_->Deref(keyframe_); }
private:
const Keyframe* keyframe_;
std::unique_ptr<VirtualPropertyIterator> impl_;
};
class CORE_EXPORT IterableProperties
: public GarbageCollected<IterableProperties> {
public:
IterableProperties() = default;
virtual ~IterableProperties() = default;
virtual PropertyIteratorWrapper begin() const = 0;
EndIterator end() const { return EndIterator(); }
virtual size_t size() const = 0;
bool empty() const { return begin() == end(); }
virtual void Trace(Visitor*) const {}
virtual bool IsTransitionProperties() const { return false; }
};
Keyframe(const Keyframe&) = delete;
Keyframe& operator=(const Keyframe&) = delete;
virtual ~Keyframe() = default;
static const double kNullComputedOffset;
// TODO(smcgruer): The keyframe offset should be immutable.
void SetOffset(std::optional<double> offset) { offset_ = offset; }
std::optional<double> Offset() const { return offset_; }
// Offsets are computed for programmatic keyframes that do not have a
// specified offset (either as a percentage or timeline offset). These are
// explicitly stored in the keyframe rather than computed on demand since
// keyframes can be reordered to accommodate changes to the resolved timeline
// offsets and computed offsets need to be sorted into the correct position.
void SetComputedOffset(std::optional<double> offset) {
computed_offset_ = offset;
}
std::optional<double> ComputedOffset() const { return computed_offset_; }
// In order to have a valid computed offset, it must be evaluated and finite.
// NaN Is used as the null value for computed offset. Note as NaN != NaN we
// cannot check that the value matches kNullComputedOffset.
bool HasComputedOffset() const {
return computed_offset_ && !std::isnan(computed_offset_.value());
}
double CheckedOffset() const { return offset_.value_or(-1); }
void SetTimelineOffset(std::optional<TimelineOffset> timeline_offset) {
timeline_offset_ = timeline_offset;
}
const std::optional<TimelineOffset>& GetTimelineOffset() const {
return timeline_offset_;
}
// TODO(smcgruer): The keyframe composite operation should be immutable.
void SetComposite(EffectModel::CompositeOperation composite) {
composite_ = composite;
}
std::optional<EffectModel::CompositeOperation> Composite() const {
return composite_;
}
void SetEasing(scoped_refptr<TimingFunction> easing) {
if (easing)
easing_ = std::move(easing);
else
easing_ = LinearTimingFunction::Shared();
}
TimingFunction& Easing() const { return *easing_; }
void CopyEasing(const Keyframe& other) { SetEasing(other.easing_); }
// Track the original positioning in the list for tiebreaking during sort
// when two keyframes have the same offset.
void SetIndex(int index) { original_index_ = index; }
std::optional<int> Index() { return original_index_; }
// Returns an iterable collection of the properties represented in this
// keyframe.
const IterableProperties& Properties() const { return *properties_; }
// Returns a copy of the properties represented in this keyframe.
// Prefer iterating over Properties() when a copy is not needed.
Vector<PropertyHandle> PropertiesVector() const;
// Creates a clone of this keyframe.
//
// The clone should have the same (property, value) pairs, offset value,
// composite operation, and timing function, as well as any other
// subclass-specific data.
virtual Keyframe* Clone() const = 0;
// Comparator for stable sorting keyframes by offset. In the event of a tie
// we sort by original index of the keyframe if specified.
static bool LessThan(const Member<Keyframe>& a, const Member<Keyframe>& b);
// Compute the offset if dependent on a timeline range. Returns true if the
// offset changed.
bool ResolveTimelineOffset(const TimelineRange&,
double range_start,
double range_end);
// Add the properties represented by this keyframe to the given V8 object.
//
// Subclasses should override this to add the (property, value) pairs they
// store, and call into the base version to add the basic Keyframe properties.
virtual void AddKeyframePropertiesToV8Object(V8ObjectBuilder&,
Element*) const;
virtual bool IsStringKeyframe() const { return false; }
virtual bool IsTransitionKeyframe() const { return false; }
virtual void Trace(Visitor* visitor) const { visitor->Trace(properties_); }
// Represents a property-specific keyframe as defined in the spec. Refer to
// the Keyframe class-level documentation for more details.
class CORE_EXPORT PropertySpecificKeyframe
: public GarbageCollected<PropertySpecificKeyframe> {
public:
PropertySpecificKeyframe(double offset,
scoped_refptr<TimingFunction> easing,
EffectModel::CompositeOperation);
PropertySpecificKeyframe(const PropertySpecificKeyframe&) = delete;
PropertySpecificKeyframe& operator=(const PropertySpecificKeyframe&) =
delete;
virtual ~PropertySpecificKeyframe() = default;
double Offset() const { return offset_; }
TimingFunction& Easing() const { return *easing_; }
EffectModel::CompositeOperation Composite() const { return composite_; }
double UnderlyingFraction() const {
return composite_ == EffectModel::kCompositeReplace ? 0 : 1;
}
virtual bool IsNeutral() const = 0;
virtual bool IsRevert() const = 0;
virtual bool IsRevertLayer() const = 0;
// FIXME: Remove this once CompositorAnimations no longer depends on
// CompositorKeyframeValues
virtual bool PopulateCompositorKeyframeValue(
const PropertyHandle&,
Element&,
const ComputedStyle& base_style,
const ComputedStyle* parent_style) const {
return false;
}
virtual const CompositorKeyframeValue* GetCompositorKeyframeValue()
const = 0;
virtual bool IsCSSPropertySpecificKeyframe() const { return false; }
virtual bool IsTransitionPropertySpecificKeyframe() const { return false; }
virtual PropertySpecificKeyframe* NeutralKeyframe(
double offset,
scoped_refptr<TimingFunction> easing) const = 0;
virtual Interpolation* CreateInterpolation(
const PropertyHandle&,
const Keyframe::PropertySpecificKeyframe& end) const;
virtual void Trace(Visitor*) const {}
protected:
double offset_;
scoped_refptr<TimingFunction> easing_;
EffectModel::CompositeOperation composite_;
};
// Construct and return a property-specific keyframe for this keyframe.
//
// The 'effect_composite' parameter is the composite operation of the effect
// that owns the keyframe. If the keyframe has a keyframe-specific composite
// operation it should ignore this value when creating the property specific
// keyframe.
//
// The 'offset' parameter is the offset to use in the resultant
// PropertySpecificKeyframe. For CSS Transitions and CSS Animations, this is
// the normal offset from the keyframe itself. However in web-animations this
// will be a computed offset value which may differ from the keyframe offset.
virtual PropertySpecificKeyframe* CreatePropertySpecificKeyframe(
const PropertyHandle&,
EffectModel::CompositeOperation effect_composite,
double offset) const = 0;
protected:
explicit Keyframe(IterableProperties* properties)
: properties_(properties), easing_(LinearTimingFunction::Shared()) {}
Keyframe(IterableProperties* properties,
std::optional<double> offset,
std::optional<TimelineOffset> timeline_offset,
std::optional<EffectModel::CompositeOperation> composite,
scoped_refptr<TimingFunction> easing)
: properties_(properties),
offset_(offset),
timeline_offset_(timeline_offset),
composite_(composite),
easing_(std::move(easing)) {
if (!easing_)
easing_ = LinearTimingFunction::Shared();
}
Member<IterableProperties> properties_;
// Either the specified offset or the offset resolved from a timeline offset.
std::optional<double> offset_;
// The computed offset will equal the specified or resolved timeline offset
// if non-null. The computed offset is null if the keyframe has an unresolved
// timeline offset. Otherwise, it is calculated based on a rule to equally
// space within an anchored range.
// See KeyframeEffectModelBase::GetComputedOffsets.
std::optional<double> computed_offset_;
// Offsets of the form <name> <percent>. These offsets are layout depending
// and need to be re-resolved on a style change affecting the corresponding
// timeline range. If the effect is not associated with an animation that is
// attached to a timeline with a non-empty timeline range,
// then the offset and computed offset will be null.
std::optional<TimelineOffset> timeline_offset_;
// The original index in the keyframe list is used to resolve ties in the
// offset when sorting, and to conditionally recover the original order when
// reporting.
std::optional<int> original_index_;
// To avoid having multiple CompositeOperation enums internally (one with
// 'auto' and one without), we use a std::optional for composite_. A
// std::nullopt value represents 'auto'.
std::optional<EffectModel::CompositeOperation> composite_;
scoped_refptr<TimingFunction> easing_;
};
using PropertySpecificKeyframe = Keyframe::PropertySpecificKeyframe;
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_KEYFRAME_H_
|