1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/animation/timing.h"
6
7 #include "third_party/blink/renderer/bindings/core/v8/v8_computed_effect_timing.h"
8 #include "third_party/blink/renderer/bindings/core/v8/v8_effect_timing.h"
9 #include "third_party/blink/renderer/core/animation/timing_calculations.h"
10
11 namespace blink {
12
FillModeString(FillMode fill_mode)13 String Timing::FillModeString(FillMode fill_mode) {
14 switch (fill_mode) {
15 case FillMode::NONE:
16 return "none";
17 case FillMode::FORWARDS:
18 return "forwards";
19 case FillMode::BACKWARDS:
20 return "backwards";
21 case FillMode::BOTH:
22 return "both";
23 case FillMode::AUTO:
24 return "auto";
25 }
26 NOTREACHED();
27 return "none";
28 }
29
StringToFillMode(const String & fill_mode)30 Timing::FillMode Timing::StringToFillMode(const String& fill_mode) {
31 if (fill_mode == "none")
32 return Timing::FillMode::NONE;
33 if (fill_mode == "backwards")
34 return Timing::FillMode::BACKWARDS;
35 if (fill_mode == "both")
36 return Timing::FillMode::BOTH;
37 if (fill_mode == "forwards")
38 return Timing::FillMode::FORWARDS;
39 DCHECK_EQ(fill_mode, "auto");
40 return Timing::FillMode::AUTO;
41 }
42
PlaybackDirectionString(PlaybackDirection playback_direction)43 String Timing::PlaybackDirectionString(PlaybackDirection playback_direction) {
44 switch (playback_direction) {
45 case PlaybackDirection::NORMAL:
46 return "normal";
47 case PlaybackDirection::REVERSE:
48 return "reverse";
49 case PlaybackDirection::ALTERNATE_NORMAL:
50 return "alternate";
51 case PlaybackDirection::ALTERNATE_REVERSE:
52 return "alternate-reverse";
53 }
54 NOTREACHED();
55 return "normal";
56 }
57
ResolvedFillMode(bool is_keyframe_effect) const58 Timing::FillMode Timing::ResolvedFillMode(bool is_keyframe_effect) const {
59 if (fill_mode != Timing::FillMode::AUTO)
60 return fill_mode;
61
62 // https://drafts.csswg.org/web-animations/#the-effecttiming-dictionaries
63 if (is_keyframe_effect)
64 return Timing::FillMode::NONE;
65 return Timing::FillMode::BOTH;
66 }
67
IterationDuration() const68 AnimationTimeDelta Timing::IterationDuration() const {
69 AnimationTimeDelta result = iteration_duration.value_or(AnimationTimeDelta());
70 DCHECK_GE(result, AnimationTimeDelta());
71 return result;
72 }
73
ActiveDuration() const74 double Timing::ActiveDuration() const {
75 const double result =
76 MultiplyZeroAlwaysGivesZero(IterationDuration(), iteration_count);
77 DCHECK_GE(result, 0);
78 return result;
79 }
80
EndTimeInternal() const81 double Timing::EndTimeInternal() const {
82 // Per the spec, the end time has a lower bound of 0.0:
83 // https://drafts.csswg.org/web-animations-1/#end-time
84 return std::max(start_delay + ActiveDuration() + end_delay, 0.0);
85 }
86
ConvertToEffectTiming() const87 EffectTiming* Timing::ConvertToEffectTiming() const {
88 EffectTiming* effect_timing = EffectTiming::Create();
89
90 effect_timing->setDelay(start_delay * 1000);
91 effect_timing->setEndDelay(end_delay * 1000);
92 effect_timing->setFill(FillModeString(fill_mode));
93 effect_timing->setIterationStart(iteration_start);
94 effect_timing->setIterations(iteration_count);
95 UnrestrictedDoubleOrString duration;
96 if (iteration_duration) {
97 duration.SetUnrestrictedDouble(iteration_duration->InMillisecondsF());
98 } else {
99 duration.SetString("auto");
100 }
101 effect_timing->setDuration(duration);
102 effect_timing->setDirection(PlaybackDirectionString(direction));
103 effect_timing->setEasing(timing_function->ToString());
104
105 return effect_timing;
106 }
107
getComputedTiming(const CalculatedTiming & calculated_timing,bool is_keyframe_effect) const108 ComputedEffectTiming* Timing::getComputedTiming(
109 const CalculatedTiming& calculated_timing,
110 bool is_keyframe_effect) const {
111 ComputedEffectTiming* computed_timing = ComputedEffectTiming::Create();
112
113 // ComputedEffectTiming members.
114 computed_timing->setEndTime(EndTimeInternal() * 1000);
115 computed_timing->setActiveDuration(ActiveDuration() * 1000);
116
117 if (calculated_timing.local_time)
118 computed_timing->setLocalTime(calculated_timing.local_time.value() * 1000);
119 else
120 computed_timing->setLocalTimeToNull();
121
122 if (calculated_timing.is_in_effect) {
123 DCHECK(calculated_timing.current_iteration);
124 DCHECK(calculated_timing.progress);
125 computed_timing->setProgress(calculated_timing.progress.value());
126 computed_timing->setCurrentIteration(
127 calculated_timing.current_iteration.value());
128 } else {
129 computed_timing->setProgressToNull();
130 computed_timing->setCurrentIterationToNull();
131 }
132
133 // For the EffectTiming members, getComputedTiming is equivalent to getTiming
134 // except that the fill and duration must be resolved.
135 //
136 // https://drafts.csswg.org/web-animations-1/#dom-animationeffect-getcomputedtiming
137 computed_timing->setDelay(start_delay * 1000);
138 computed_timing->setEndDelay(end_delay * 1000);
139 computed_timing->setFill(
140 Timing::FillModeString(ResolvedFillMode(is_keyframe_effect)));
141 computed_timing->setIterationStart(iteration_start);
142 computed_timing->setIterations(iteration_count);
143
144 UnrestrictedDoubleOrString duration;
145 duration.SetUnrestrictedDouble(IterationDuration().InMillisecondsF());
146 computed_timing->setDuration(duration);
147
148 computed_timing->setDirection(Timing::PlaybackDirectionString(direction));
149 computed_timing->setEasing(timing_function->ToString());
150
151 return computed_timing;
152 }
153
CalculateTimings(base::Optional<double> local_time,AnimationDirection animation_direction,bool is_keyframe_effect,base::Optional<double> playback_rate) const154 Timing::CalculatedTiming Timing::CalculateTimings(
155 base::Optional<double> local_time,
156 AnimationDirection animation_direction,
157 bool is_keyframe_effect,
158 base::Optional<double> playback_rate) const {
159 const double active_duration = ActiveDuration();
160
161 const Timing::Phase current_phase =
162 CalculatePhase(active_duration, local_time, animation_direction, *this);
163 const base::Optional<AnimationTimeDelta> active_time =
164 CalculateActiveTime(active_duration, ResolvedFillMode(is_keyframe_effect),
165 local_time, current_phase, *this);
166
167 base::Optional<double> progress;
168 const double iteration_duration = IterationDuration().InSecondsF();
169
170 const base::Optional<double> overall_progress =
171 CalculateOverallProgress(current_phase, active_time, iteration_duration,
172 iteration_count, iteration_start);
173 const base::Optional<double> simple_iteration_progress =
174 CalculateSimpleIterationProgress(current_phase, overall_progress,
175 iteration_start, active_time,
176 active_duration, iteration_count);
177 const base::Optional<double> current_iteration =
178 CalculateCurrentIteration(current_phase, active_time, iteration_count,
179 overall_progress, simple_iteration_progress);
180 const bool current_direction_is_forwards =
181 IsCurrentDirectionForwards(current_iteration, direction);
182 const base::Optional<double> directed_progress = CalculateDirectedProgress(
183 simple_iteration_progress, current_iteration, direction);
184
185 progress = CalculateTransformedProgress(current_phase, directed_progress,
186 current_direction_is_forwards,
187 timing_function);
188
189 AnimationTimeDelta time_to_next_iteration = AnimationTimeDelta::Max();
190 // Conditionally compute the time to next iteration, which is only
191 // applicable if the iteration duration is non-zero.
192 if (iteration_duration) {
193 const double start_offset =
194 MultiplyZeroAlwaysGivesZero(iteration_start, iteration_duration);
195 DCHECK_GE(start_offset, 0);
196 const base::Optional<AnimationTimeDelta> offset_active_time =
197 CalculateOffsetActiveTime(active_duration, active_time, start_offset);
198 const base::Optional<AnimationTimeDelta> iteration_time =
199 CalculateIterationTime(iteration_duration, active_duration,
200 offset_active_time, start_offset, current_phase,
201 *this);
202 if (iteration_time) {
203 // active_time cannot be null if iteration_time is not null.
204 DCHECK(active_time);
205 time_to_next_iteration =
206 AnimationTimeDelta::FromSecondsD(iteration_duration) -
207 iteration_time.value();
208 if (AnimationTimeDelta::FromSecondsD(active_duration) -
209 active_time.value() <
210 time_to_next_iteration)
211 time_to_next_iteration = AnimationTimeDelta::Max();
212 }
213 }
214
215 CalculatedTiming calculated = CalculatedTiming();
216 calculated.phase = current_phase;
217 calculated.current_iteration = current_iteration;
218 calculated.progress = progress;
219 calculated.is_in_effect = active_time.has_value();
220 // If active_time is not null then current_iteration and (transformed)
221 // progress are also non-null).
222 DCHECK(!calculated.is_in_effect ||
223 (current_iteration.has_value() && progress.has_value()));
224 calculated.is_in_play = calculated.phase == Timing::kPhaseActive;
225 // https://drafts.csswg.org/web-animations-1/#current
226 calculated.is_current = calculated.is_in_play ||
227 (playback_rate.has_value() && playback_rate > 0 &&
228 calculated.phase == Timing::kPhaseBefore) ||
229 (playback_rate.has_value() && playback_rate < 0 &&
230 calculated.phase == Timing::kPhaseAfter);
231 calculated.local_time = local_time;
232 calculated.time_to_next_iteration = time_to_next_iteration;
233
234 return calculated;
235 }
236
237 } // namespace blink
238