blob: 8321b847328464709e9e2a2a9a85d8cf72aea0ee [file] [log] [blame] [view]
Wei Lifc6f3e02021-09-08 23:52:331
2## Layer Animation Builder
3
4
5### Purpose
6
7Integrating animation and kinetic UI movements can be difficult to set up. Where to start and what to override (if anything) isn’t very clear. Making animations more declarative in nature rather than manually gluing all the lower-level animation subsystems together will simplify their use.
8
9This system provides a programmatic model that is similar to how the UX team creates animations. This ensures more fidelity between the intent of the UX designer and the resulting code. The visual similarities between the presentation used by the UX and the actual code constructing those sequences will make maintenance and updates easier and quicker.
10
11
12### Example
13
14In order to better demonstrate how this system makes constructing animations easier an example should make this more clear.
15
16The following code will create a repeating cross-fading animation shown below.
17
18![animation_step1](images/animation_builder1.png) ![animation_step2](images/animation_builder1.png) ![animation_step3](images/animation_builder1.png)
19
20
21Traditionally this would be how the above animation would be created.
22
23
24``` cpp
25 auto primary_sequence = std::make_unique<ui::LayerAnimationSequence>();
26 auto secondary_sequence = std::make_unique<ui::LayerAnimationSequence>();
27 primary_sequence->set_is_repeating(true);
28 secondary_sequence->set_is_repeating(true);
29
30 primary_sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
31 ui::LayerAnimationElement::OPACITY, base::TimeDelta::FromSeconds(2)));
32 primary_sequence->AddElement(ui::LayerAnimationElement::CreateOpacityElement(
33 0.0f, base::TimeDelta::FromSeconds(1)));
34 primary_sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
35 ui::LayerAnimationElement::OPACITY, base::TimeDelta::FromSeconds(2)));
36 primary_sequence->AddElement(ui::LayerAnimationElement::CreateOpacityElement(
37 1.0f, base::TimeDelta::FromSeconds(1)));
38
39 secondary_sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
40 ui::LayerAnimationElement::OPACITY, base::TimeDelta::FromSeconds(2)));
41 secondary_sequence->AddElement(
42 ui::LayerAnimationElement::CreateOpacityElement(
43 1.0f, base::TimeDelta::FromSeconds(1)));
44 secondary_sequence->AddElement(ui::LayerAnimationElement::CreatePauseElement(
45 ui::LayerAnimationElement::OPACITY, base::TimeDelta::FromSeconds(2)));
46 secondary_sequence->AddElement(
47 ui::LayerAnimationElement::CreateOpacityElement(
48 0.0f, base::TimeDelta::FromSeconds(1)));
49
50 primary_view_->layer()->GetAnimator()->StartAnimation(
51 primary_sequence.release());
52 secondary_view_->layer()->GetAnimator()->StartAnimation(
53 secondary_sequence.release());
54```
55
56
57Using the new builder system described in this document, the above code could be done much simpler.
58
59
60``` cpp
61 AnimationBuilder()
62 .Repeatedly()
63 .Offset(base::TimeDelta::FromSeconds(2))
64 .SetDuration(base::TimeDelta::FromSeconds(1))
65 .SetOpacity(primary_view_, 0.0f)
66 .SetOpacity(secondary_view_, 1.0f)
67 .Offset(base::TimeDelta::FromSeconds(2))
68 .SetDuration(base::TimeDelta::FromSeconds(1))
69 .SetOpacity(primary_view_, 1.0f)
70 .SetOpacity(secondary_view_, 0.0f);
71```
72
73
74The AnimationBuilder will handle proper insertion of pause elements into the timeline, so no specific need to insert them. It will also handle coordination of the animations across multiple [LayerOwner](https://source.chromium.org/chromium/chromium/src/+/main:ui/compositor/layer_owner.h;l=19)s or across specific [Layers](https://source.chromium.org/chromium/chromium/src/+/main:ui/compositor/layer.h;drc=af5bb21d9ee6585e4111fbc9089d1f5c7edde034;l=69).
75
76It should be obvious that the above code is both shorter and more immediately clear what the intent of the code is doing along with how the animations interact across the two views.
77
78
79### API Reference
80
Elaine Chien46fb5092021-09-15 21:55:0381Currently AnimationBuilder operates only on a LayerOwner or directly on a Layer. Since views::View is also a LayerOwner, it will also operate directly on Views. To animate a View, the View must first paint to it’s layer by calling View::SetPaintToLayer.
Wei Lifc6f3e02021-09-08 23:52:3382
Elaine Chien46fb5092021-09-15 21:55:0383The AnimationBuilder is a scoped object that starts the animations after the AnimationBuilder goes out of scope. This can be done as a single statement (as shown above) or across multiple statements with local temps.
Wei Lifc6f3e02021-09-08 23:52:3384
85NOTE: It is generally _not_ recommended to hold onto an AnimationBuilder instance beyond the current scope or store it in instance variables. This could lead to UAFs or other undesirable behaviors. Simply construct the animation at the point at which it is needed.
86
87To use AnimationBuilder include the following header:
88
89
90``` cpp
91#include "ui/views/animation/animation_builder.h"
92```
93
Elaine Chien46fb5092021-09-15 21:55:0394The AnimationBuilder consists of the main object along with an inner AnimationSequenceBlock.
Wei Lifc6f3e02021-09-08 23:52:3395
Elaine Chien46fb5092021-09-15 21:55:0396An AnimationSequenceBlock is a single unit of a larger animation sequence, which has a start time, duration, and zero or more (target, property) animations.
97* There may be multiple properties animating on a single target, and/or multiple targets animating, but the same property on the same target may only be animated at most once per block. Animations can be added by calling SetXXX().
98* Calling At(), Offset(), or Then() creates a new block. All animations in the same AnimationSequenceBlock will run in parallel. Create multiple AnimationSequenceBlocks to run animations in sequence.
Wei Lifc6f3e02021-09-08 23:52:3399
Elaine Chien46fb5092021-09-15 21:55:03100Calls on AnimationBuilder affect the whole animations and calls on AnimationSequenceBlock affect only that particular block.
101
102When setting callbacks for the animations note that the AnimationBuilder’s observer that calls these callbacks may outlive the callback's parameters. Developers may need to use weak pointers or force animations to be cancelled in the object’s destructor to prevent accessing destroyed objects.
Wei Lifc6f3e02021-09-08 23:52:33103
104``` cpp
105class VIEWS_EXPORT AnimationBuilder {
106 public:
107 AnimationBuilder();
108 ~AnimationBuilder();
109
110 // Options for the whole animation
Elaine Chien46fb5092021-09-15 21:55:03111
Wei Lifc6f3e02021-09-08 23:52:33112 AnimationBuilder& SetPreemptionStrategy(
113 ui::LayerAnimator::PreemptionStrategy preemption_strategy);
Elaine Chien46fb5092021-09-15 21:55:03114 // Called when the animation starts.
115 AnimationBuilder& OnStarted(base::OnceClosure callback);
116 // Called when the animation ends. Not called if animation is aborted.
117 AnimationBuilder& OnEnded(base::OnceClosure callback);
118 // Called when a sequence repetition ends and will repeat. Not called if
119 // sequence is aborted.
120 AnimationBuilder& OnWillRepeat(base::RepeatingClosure callback);
121 // Called if animation is aborted for any reason. Should never do anything
122 // that may cause another animation to be started.
123 AnimationBuilder& OnAborted(base::OnceClosure callback);
124 // Called when the animation is scheduled.
125 AnimationBuilder& OnScheduled(base::OnceClosure callback);
Wei Lifc6f3e02021-09-08 23:52:33126
127 // Creates a new sequence (that optionally repeats).
128 AnimationSequenceBlock Once();
129 AnimationSequenceBlock Repeatedly();
130
131 // Returns a handle that can be destroyed later to abort all running
132 // animations.
133 // Caveat: ALL properties will be aborted, including those not initiated
134 // by the builder.
135 std::unique_ptr<AnimationAbortHandle> GetAbortHandle();
136};
137
Elaine Chien46fb5092021-09-15 21:55:03138
Wei Lifc6f3e02021-09-08 23:52:33139class VIEWS_EXPORT AnimationSequenceBlock {
140 public:
141 AnimationSequenceBlock(base::PassKey<AnimationBuilder> builder_key,
142 AnimationBuilder* owner,
143 base::TimeDelta start);
144 AnimationSequenceBlock(AnimationSequenceBlock&& other);
145 AnimationSequenceBlock& operator=(AnimationSequenceBlock&& other);
146 ~AnimationSequenceBlock();
147
148 // Sets the duration of this block. The duration may be set at most once and
149 // will be zero if unspecified.
150 AnimationSequenceBlock& SetDuration(base::TimeDelta duration);
151
152 // Adds animation elements to this block. Each (target, property) pair may be
153 // added at most once.
Elaine Chien46fb5092021-09-15 21:55:03154 // These property setter methods can also take in a ui::Layer as the target.
Wei Lifc6f3e02021-09-08 23:52:33155 AnimationSequenceBlock& SetBounds(
156 ui::LayerOwner* target,
157 const gfx::Rect& bounds,
158 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
159 AnimationSequenceBlock& SetBrightness(
Wei Lifc6f3e02021-09-08 23:52:33160 ui::LayerOwner* target,
161 float brightness,
162 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
163 AnimationSequenceBlock& SetClipRect(
Wei Lifc6f3e02021-09-08 23:52:33164 ui::LayerOwner* target,
165 const gfx::Rect& clip_rect,
166 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
167 AnimationSequenceBlock& SetColor(
Wei Lifc6f3e02021-09-08 23:52:33168 ui::LayerOwner* target,
169 SkColor color,
170 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
171 AnimationSequenceBlock& SetGrayscale(
Wei Lifc6f3e02021-09-08 23:52:33172 ui::LayerOwner* target,
173 float grayscale,
174 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
175 AnimationSequenceBlock& SetOpacity(
Wei Lifc6f3e02021-09-08 23:52:33176 ui::LayerOwner* target,
177 float opacity,
178 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
179 AnimationSequenceBlock& SetTransform(
Wei Lifc6f3e02021-09-08 23:52:33180 ui::LayerOwner* target,
181 gfx::Transform transform,
182 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
183 AnimationSequenceBlock& SetRoundedCorners(
Wei Lifc6f3e02021-09-08 23:52:33184 ui::LayerOwner* target,
185 const gfx::RoundedCornersF& rounded_corners,
186 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
187 AnimationSequenceBlock& SetVisibility(
Wei Lifc6f3e02021-09-08 23:52:33188 ui::LayerOwner* target,
189 bool visible,
190 gfx::Tween::Type tween_type = gfx::Tween::LINEAR);
191
192 // Creates a new block.
193 AnimationSequenceBlock At(base::TimeDelta since_sequence_start);
194 AnimationSequenceBlock Offset(base::TimeDelta since_last_block_start);
195 AnimationSequenceBlock Then();
Wei Lifc6f3e02021-09-08 23:52:33196};
197```
198