Source code

Revision control

Other Tools

1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
* License, v. 2.0. If a copy of the MPL was not distributed with this
3
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5
//! CSS transitions and animations.
6
7
// NOTE(emilio): This code isn't really executed in Gecko, but we don't want to
8
// compile it out so that people remember it exists, thus the cfg'd Sender
9
// import.
10
11
use crate::bezier::Bezier;
12
use crate::context::SharedStyleContext;
13
use crate::dom::{OpaqueNode, TElement};
14
use crate::font_metrics::FontMetricsProvider;
15
use crate::properties::animated_properties::AnimatedProperty;
16
use crate::properties::longhands::animation_direction::computed_value::single_value::T as AnimationDirection;
17
use crate::properties::longhands::animation_play_state::computed_value::single_value::T as AnimationPlayState;
18
use crate::properties::{self, CascadeMode, ComputedValues, LonghandId};
19
use crate::rule_tree::CascadeLevel;
20
use crate::stylesheets::keyframes_rule::{KeyframesAnimation, KeyframesStep, KeyframesStepValue};
21
use crate::timer::Timer;
22
use crate::values::computed::box_::TransitionProperty;
23
use crate::values::computed::Time;
24
use crate::values::computed::TimingFunction;
25
use crate::values::generics::box_::AnimationIterationCount;
26
use crate::values::generics::easing::{StepPosition, TimingFunction as GenericTimingFunction};
27
use crate::Atom;
28
#[cfg(feature = "servo")]
29
use crossbeam_channel::Sender;
30
use servo_arc::Arc;
31
use std::fmt;
32
#[cfg(feature = "gecko")]
33
use std::sync::mpsc::Sender;
34
35
/// This structure represents a keyframes animation current iteration state.
36
///
37
/// If the iteration count is infinite, there's no other state, otherwise we
38
/// have to keep track the current iteration and the max iteration count.
39
#[derive(Clone, Debug)]
40
pub enum KeyframesIterationState {
41
/// Infinite iterations, so no need to track a state.
42
Infinite,
43
/// Current and max iterations.
44
Finite(f32, f32),
45
}
46
47
/// This structure represents wether an animation is actually running.
48
///
49
/// An animation can be running, or paused at a given time.
50
#[derive(Clone, Debug)]
51
pub enum KeyframesRunningState {
52
/// This animation is paused. The inner field is the percentage of progress
53
/// when it was paused, from 0 to 1.
54
Paused(f64),
55
/// This animation is actually running.
56
Running,
57
}
58
59
/// This structure represents the current keyframe animation state, i.e., the
60
/// duration, the current and maximum iteration count, and the state (either
61
/// playing or paused).
62
// TODO: unify the use of f32/f64 in this file.
63
#[derive(Clone)]
64
pub struct KeyframesAnimationState {
65
/// The time this animation started at.
66
pub started_at: f64,
67
/// The duration of this animation.
68
pub duration: f64,
69
/// The delay of the animation.
70
pub delay: f64,
71
/// The current iteration state for the animation.
72
pub iteration_state: KeyframesIterationState,
73
/// Werther this animation is paused.
74
pub running_state: KeyframesRunningState,
75
/// The declared animation direction of this animation.
76
pub direction: AnimationDirection,
77
/// The current animation direction. This can only be `normal` or `reverse`.
78
pub current_direction: AnimationDirection,
79
/// Werther this keyframe animation is outdated due to a restyle.
80
pub expired: bool,
81
/// The original cascade style, needed to compute the generated keyframes of
82
/// the animation.
83
pub cascade_style: Arc<ComputedValues>,
84
}
85
86
impl KeyframesAnimationState {
87
/// Performs a tick in the animation state, i.e., increments the counter of
88
/// the current iteration count, updates times and then toggles the
89
/// direction if appropriate.
90
///
91
/// Returns true if the animation should keep running.
92
pub fn tick(&mut self) -> bool {
93
debug!("KeyframesAnimationState::tick");
94
debug_assert!(!self.expired);
95
96
self.started_at += self.duration + self.delay;
97
match self.running_state {
98
// If it's paused, don't update direction or iteration count.
99
KeyframesRunningState::Paused(_) => return true,
100
KeyframesRunningState::Running => {},
101
}
102
103
if let KeyframesIterationState::Finite(ref mut current, ref max) = self.iteration_state {
104
*current += 1.0;
105
// NB: This prevent us from updating the direction, which might be
106
// needed for the correct handling of animation-fill-mode.
107
if *current >= *max {
108
return false;
109
}
110
}
111
112
// Update the next iteration direction if applicable.
113
match self.direction {
114
AnimationDirection::Alternate | AnimationDirection::AlternateReverse => {
115
self.current_direction = match self.current_direction {
116
AnimationDirection::Normal => AnimationDirection::Reverse,
117
AnimationDirection::Reverse => AnimationDirection::Normal,
118
_ => unreachable!(),
119
};
120
},
121
_ => {},
122
}
123
124
true
125
}
126
127
/// Updates the appropiate state from other animation.
128
///
129
/// This happens when an animation is re-submitted to layout, presumably
130
/// because of an state change.
131
///
132
/// There are some bits of state we can't just replace, over all taking in
133
/// account times, so here's that logic.
134
pub fn update_from_other(&mut self, other: &Self, timer: &Timer) {
135
use self::KeyframesRunningState::*;
136
137
debug!(
138
"KeyframesAnimationState::update_from_other({:?}, {:?})",
139
self, other
140
);
141
142
// NB: We shall not touch the started_at field, since we don't want to
143
// restart the animation.
144
let old_started_at = self.started_at;
145
let old_duration = self.duration;
146
let old_direction = self.current_direction;
147
let old_running_state = self.running_state.clone();
148
let old_iteration_state = self.iteration_state.clone();
149
*self = other.clone();
150
151
let mut new_started_at = old_started_at;
152
153
// If we're unpausing the animation, fake the start time so we seem to
154
// restore it.
155
//
156
// If the animation keeps paused, keep the old value.
157
//
158
// If we're pausing the animation, compute the progress value.
159
match (&mut self.running_state, old_running_state) {
160
(&mut Running, Paused(progress)) => {
161
new_started_at = timer.seconds() - (self.duration * progress)
162
},
163
(&mut Paused(ref mut new), Paused(old)) => *new = old,
164
(&mut Paused(ref mut progress), Running) => {
165
*progress = (timer.seconds() - old_started_at) / old_duration
166
},
167
_ => {},
168
}
169
170
// Don't update the iteration count, just the iteration limit.
171
// TODO: see how changing the limit affects rendering in other browsers.
172
// We might need to keep the iteration count even when it's infinite.
173
match (&mut self.iteration_state, old_iteration_state) {
174
(
175
&mut KeyframesIterationState::Finite(ref mut iters, _),
176
KeyframesIterationState::Finite(old_iters, _),
177
) => *iters = old_iters,
178
_ => {},
179
}
180
181
self.current_direction = old_direction;
182
self.started_at = new_started_at;
183
}
184
}
185
186
impl fmt::Debug for KeyframesAnimationState {
187
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
188
f.debug_struct("KeyframesAnimationState")
189
.field("started_at", &self.started_at)
190
.field("duration", &self.duration)
191
.field("delay", &self.delay)
192
.field("iteration_state", &self.iteration_state)
193
.field("running_state", &self.running_state)
194
.field("direction", &self.direction)
195
.field("current_direction", &self.current_direction)
196
.field("expired", &self.expired)
197
.field("cascade_style", &())
198
.finish()
199
}
200
}
201
202
/// State relating to an animation.
203
#[derive(Clone, Debug)]
204
pub enum Animation {
205
/// A transition is just a single frame triggered at a time, with a reflow.
206
///
207
/// the f64 field is the start time as returned by `time::precise_time_s()`.
208
Transition(OpaqueNode, f64, AnimationFrame),
209
/// A keyframes animation is identified by a name, and can have a
210
/// node-dependent state (i.e. iteration count, etc.).
211
///
212
/// TODO(emilio): The animation object could be refcounted.
213
Keyframes(
214
OpaqueNode,
215
KeyframesAnimation,
216
Atom,
217
KeyframesAnimationState,
218
),
219
}
220
221
impl Animation {
222
/// Whether this animation is expired.
223
#[inline]
224
pub fn is_expired(&self) -> bool {
225
match *self {
226
Animation::Transition(..) => false,
227
Animation::Keyframes(_, _, _, ref state) => state.expired,
228
}
229
}
230
231
/// The opaque node that owns the animation.
232
#[inline]
233
pub fn node(&self) -> &OpaqueNode {
234
match *self {
235
Animation::Transition(ref node, _, _) => node,
236
Animation::Keyframes(ref node, _, _, _) => node,
237
}
238
}
239
240
/// Whether this animation is a transition.
241
#[inline]
242
pub fn is_transition(&self) -> bool {
243
match *self {
244
Animation::Transition(..) => true,
245
Animation::Keyframes(..) => false,
246
}
247
}
248
}
249
250
/// A single animation frame of a single property.
251
#[derive(Clone, Debug)]
252
pub struct AnimationFrame {
253
/// A description of the property animation that is occurring.
254
pub property_animation: PropertyAnimation,
255
/// The duration of the animation. This is either relative in the keyframes
256
/// case (a number between 0 and 1), or absolute in the transition case.
257
pub duration: f64,
258
}
259
260
/// Represents an animation for a given property.
261
#[derive(Clone, Debug)]
262
pub struct PropertyAnimation {
263
property: AnimatedProperty,
264
timing_function: TimingFunction,
265
duration: Time, // TODO: isn't this just repeated?
266
}
267
268
impl PropertyAnimation {
269
/// Returns the given property name.
270
pub fn property_name(&self) -> &'static str {
271
self.property.name()
272
}
273
274
/// Creates a new property animation for the given transition index and old
275
/// and new styles. Any number of animations may be returned, from zero (if
276
/// the property did not animate) to one (for a single transition property)
277
/// to arbitrarily many (for `all`).
278
pub fn from_transition(
279
transition_index: usize,
280
old_style: &ComputedValues,
281
new_style: &mut ComputedValues,
282
) -> Vec<PropertyAnimation> {
283
let mut result = vec![];
284
let box_style = new_style.get_box();
285
let transition_property = box_style.transition_property_at(transition_index);
286
let timing_function = box_style.transition_timing_function_mod(transition_index);
287
let duration = box_style.transition_duration_mod(transition_index);
288
289
match transition_property {
290
TransitionProperty::Custom(..) | TransitionProperty::Unsupported(..) => result,
291
TransitionProperty::Shorthand(ref shorthand_id) => shorthand_id
292
.longhands()
293
.filter_map(|longhand| {
294
PropertyAnimation::from_longhand(
295
longhand,
296
timing_function,
297
duration,
298
old_style,
299
new_style,
300
)
301
})
302
.collect(),
303
TransitionProperty::Longhand(longhand_id) => {
304
let animation = PropertyAnimation::from_longhand(
305
longhand_id,
306
timing_function,
307
duration,
308
old_style,
309
new_style,
310
);
311
312
if let Some(animation) = animation {
313
result.push(animation);
314
}
315
result
316
},
317
}
318
}
319
320
fn from_longhand(
321
longhand: LonghandId,
322
timing_function: TimingFunction,
323
duration: Time,
324
old_style: &ComputedValues,
325
new_style: &ComputedValues,
326
) -> Option<PropertyAnimation> {
327
let animated_property = AnimatedProperty::from_longhand(longhand, old_style, new_style)?;
328
329
let property_animation = PropertyAnimation {
330
property: animated_property,
331
timing_function: timing_function,
332
duration: duration,
333
};
334
335
if property_animation.does_animate() {
336
Some(property_animation)
337
} else {
338
None
339
}
340
}
341
342
/// Update the given animation at a given point of progress.
343
pub fn update(&self, style: &mut ComputedValues, time: f64) {
344
let epsilon = 1. / (200. * (self.duration.seconds() as f64));
345
let progress = match self.timing_function {
346
GenericTimingFunction::CubicBezier { x1, y1, x2, y2 } => {
347
Bezier::new(x1, y1, x2, y2).solve(time, epsilon)
348
},
349
GenericTimingFunction::Steps(steps, pos) => {
350
let mut current_step = (time * (steps as f64)).floor() as i32;
351
352
if pos == StepPosition::Start ||
353
pos == StepPosition::JumpStart ||
354
pos == StepPosition::JumpBoth
355
{
356
current_step = current_step + 1;
357
}
358
359
// FIXME: We should update current_step according to the "before flag".
360
// In order to get the before flag, we have to know the current animation phase
361
// and whether the iteration is reversed. For now, we skip this calculation.
362
// (i.e. Treat before_flag is unset,)
364
365
if time >= 0.0 && current_step < 0 {
366
current_step = 0;
367
}
368
369
let jumps = match pos {
370
StepPosition::JumpBoth => steps + 1,
371
StepPosition::JumpNone => steps - 1,
372
StepPosition::JumpStart |
373
StepPosition::JumpEnd |
374
StepPosition::Start |
375
StepPosition::End => steps,
376
};
377
378
if time <= 1.0 && current_step > jumps {
379
current_step = jumps;
380
}
381
382
(current_step as f64) / (jumps as f64)
383
},
384
GenericTimingFunction::Keyword(keyword) => {
385
let (x1, x2, y1, y2) = keyword.to_bezier();
386
Bezier::new(x1, x2, y1, y2).solve(time, epsilon)
387
},
388
};
389
390
self.property.update(style, progress);
391
}
392
393
#[inline]
394
fn does_animate(&self) -> bool {
395
self.property.does_animate() && self.duration.seconds() != 0.0
396
}
397
398
/// Whether this animation has the same end value as another one.
399
#[inline]
400
pub fn has_the_same_end_value_as(&self, other: &Self) -> bool {
401
self.property.has_the_same_end_value_as(&other.property)
402
}
403
}
404
405
/// Inserts transitions into the queue of running animations as applicable for
406
/// the given style difference. This is called from the layout worker threads.
407
/// Returns true if any animations were kicked off and false otherwise.
408
pub fn start_transitions_if_applicable(
409
new_animations_sender: &Sender<Animation>,
410
opaque_node: OpaqueNode,
411
old_style: &ComputedValues,
412
new_style: &mut Arc<ComputedValues>,
413
timer: &Timer,
414
possibly_expired_animations: &[PropertyAnimation],
415
) -> bool {
416
let mut had_animations = false;
417
for i in 0..new_style.get_box().transition_property_count() {
418
// Create any property animations, if applicable.
419
let property_animations =
420
PropertyAnimation::from_transition(i, old_style, Arc::make_mut(new_style));
421
for property_animation in property_animations {
422
// Set the property to the initial value.
423
//
424
// NB: get_mut is guaranteed to succeed since we called make_mut()
425
// above.
426
property_animation.update(Arc::get_mut(new_style).unwrap(), 0.0);
427
428
// Per [1], don't trigger a new transition if the end state for that
429
// transition is the same as that of a transition that's already
430
// running on the same node.
431
//
433
if possibly_expired_animations
434
.iter()
435
.any(|animation| animation.has_the_same_end_value_as(&property_animation))
436
{
437
debug!(
438
"Not initiating transition for {}, other transition \
439
found with the same end value",
440
property_animation.property_name()
441
);
442
continue;
443
}
444
445
debug!("Kicking off transition of {:?}", property_animation);
446
447
// Kick off the animation.
448
let box_style = new_style.get_box();
449
let now = timer.seconds();
450
let start_time = now + (box_style.transition_delay_mod(i).seconds() as f64);
451
new_animations_sender
452
.send(Animation::Transition(
453
opaque_node,
454
start_time,
455
AnimationFrame {
456
duration: box_style.transition_duration_mod(i).seconds() as f64,
457
property_animation,
458
},
459
))
460
.unwrap();
461
462
had_animations = true;
463
}
464
}
465
466
had_animations
467
}
468
469
fn compute_style_for_animation_step<E>(
470
context: &SharedStyleContext,
471
step: &KeyframesStep,
472
previous_style: &ComputedValues,
473
style_from_cascade: &Arc<ComputedValues>,
474
font_metrics_provider: &dyn FontMetricsProvider,
475
) -> Arc<ComputedValues>
476
where
477
E: TElement,
478
{
479
match step.value {
480
KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
481
KeyframesStepValue::Declarations {
482
block: ref declarations,
483
} => {
484
let guard = declarations.read_with(context.guards.author);
485
486
let iter = || {
487
// It's possible to have !important properties in keyframes
488
// so we have to filter them out.
490
// Also we filter our non-animatable properties.
491
guard
492
.normal_declaration_iter()
493
.filter(|declaration| declaration.is_animatable())
494
.map(|decl| (decl, CascadeLevel::Animations))
495
};
496
497
// This currently ignores visited styles, which seems acceptable,
498
// as existing browsers don't appear to animate visited styles.
499
let computed = properties::apply_declarations::<E, _, _>(
500
context.stylist.device(),
501
/* pseudo = */ None,
502
previous_style.rules(),
503
&context.guards,
504
iter,
505
Some(previous_style),
506
Some(previous_style),
507
Some(previous_style),
508
font_metrics_provider,
509
CascadeMode::Unvisited {
510
visited_rules: None,
511
},
512
context.quirks_mode(),
513
/* rule_cache = */ None,
514
&mut Default::default(),
515
/* element = */ None,
516
);
517
computed
518
},
519
}
520
}
521
522
/// Triggers animations for a given node looking at the animation property
523
/// values.
524
pub fn maybe_start_animations<E>(
525
element: E,
526
context: &SharedStyleContext,
527
new_animations_sender: &Sender<Animation>,
528
node: OpaqueNode,
529
new_style: &Arc<ComputedValues>,
530
) -> bool
531
where
532
E: TElement,
533
{
534
let mut had_animations = false;
535
536
let box_style = new_style.get_box();
537
for (i, name) in box_style.animation_name_iter().enumerate() {
538
let name = match name.as_atom() {
539
Some(atom) => atom,
540
None => continue,
541
};
542
543
debug!("maybe_start_animations: name={}", name);
544
let total_duration = box_style.animation_duration_mod(i).seconds();
545
if total_duration == 0. {
546
continue;
547
}
548
549
let anim = match context.stylist.get_animation(name, element) {
550
Some(animation) => animation,
551
None => continue,
552
};
553
554
debug!("maybe_start_animations: animation {} found", name);
555
556
// If this animation doesn't have any keyframe, we can just continue
557
// without submitting it to the compositor, since both the first and
558
// the second keyframes would be synthetised from the computed
559
// values.
560
if anim.steps.is_empty() {
561
continue;
562
}
563
564
let delay = box_style.animation_delay_mod(i).seconds();
565
let now = context.timer.seconds();
566
let animation_start = now + delay as f64;
567
let duration = box_style.animation_duration_mod(i).seconds();
568
let iteration_state = match box_style.animation_iteration_count_mod(i) {
569
AnimationIterationCount::Infinite => KeyframesIterationState::Infinite,
570
AnimationIterationCount::Number(n) => KeyframesIterationState::Finite(0.0, n),
571
};
572
573
let animation_direction = box_style.animation_direction_mod(i);
574
575
let initial_direction = match animation_direction {
576
AnimationDirection::Normal | AnimationDirection::Alternate => {
577
AnimationDirection::Normal
578
},
579
AnimationDirection::Reverse | AnimationDirection::AlternateReverse => {
580
AnimationDirection::Reverse
581
},
582
};
583
584
let running_state = match box_style.animation_play_state_mod(i) {
585
AnimationPlayState::Paused => KeyframesRunningState::Paused(0.),
586
AnimationPlayState::Running => KeyframesRunningState::Running,
587
};
588
589
new_animations_sender
590
.send(Animation::Keyframes(
591
node,
592
anim.clone(),
593
name.clone(),
594
KeyframesAnimationState {
595
started_at: animation_start,
596
duration: duration as f64,
597
delay: delay as f64,
598
iteration_state,
599
running_state,
600
direction: animation_direction,
601
current_direction: initial_direction,
602
expired: false,
603
cascade_style: new_style.clone(),
604
},
605
))
606
.unwrap();
607
had_animations = true;
608
}
609
610
had_animations
611
}
612
613
/// Updates a given computed style for a given animation frame. Returns a bool
614
/// representing if the style was indeed updated.
615
pub fn update_style_for_animation_frame(
616
mut new_style: &mut Arc<ComputedValues>,
617
now: f64,
618
start_time: f64,
619
frame: &AnimationFrame,
620
) -> bool {
621
let mut progress = (now - start_time) / frame.duration;
622
if progress > 1.0 {
623
progress = 1.0
624
}
625
626
if progress <= 0.0 {
627
return false;
628
}
629
630
frame
631
.property_animation
632
.update(Arc::make_mut(&mut new_style), progress);
633
634
true
635
}
636
637
/// Returns the kind of animation update that happened.
638
pub enum AnimationUpdate {
639
/// The style was successfully updated, the animation is still running.
640
Regular,
641
/// A style change canceled this animation.
642
AnimationCanceled,
643
}
644
645
/// Updates a single animation and associated style based on the current time.
646
///
647
/// FIXME(emilio): This doesn't handle any kind of dynamic change to the
648
/// animation or transition properties in any reasonable way.
649
///
650
/// This should probably be split in two, one from updating animations and
651
/// transitions in response to a style change (that is,
652
/// consider_starting_transitions + maybe_start_animations, but handling
653
/// canceled animations, duration changes, etc, there instead of here), and this
654
/// function should be only about the style update in response of a transition.
655
pub fn update_style_for_animation<E>(
656
context: &SharedStyleContext,
657
animation: &Animation,
658
style: &mut Arc<ComputedValues>,
659
font_metrics_provider: &dyn FontMetricsProvider,
660
) -> AnimationUpdate
661
where
662
E: TElement,
663
{
664
debug!("update_style_for_animation: {:?}", animation);
665
debug_assert!(!animation.is_expired());
666
667
match *animation {
668
Animation::Transition(_, start_time, ref frame) => {
669
let now = context.timer.seconds();
670
let mut new_style = (*style).clone();
671
let updated_style =
672
update_style_for_animation_frame(&mut new_style, now, start_time, frame);
673
if updated_style {
674
*style = new_style
675
}
676
// FIXME(emilio): Should check before updating the style that the
677
// transition_property still transitions this, or bail out if not.
678
//
679
// Or doing it in process_animations, only if transition_property
680
// changed somehow (even better).
681
AnimationUpdate::Regular
682
},
683
Animation::Keyframes(_, ref animation, ref name, ref state) => {
684
let duration = state.duration;
685
let started_at = state.started_at;
686
687
let now = match state.running_state {
688
KeyframesRunningState::Running => context.timer.seconds(),
689
KeyframesRunningState::Paused(progress) => started_at + duration * progress,
690
};
691
692
debug_assert!(!animation.steps.is_empty());
693
694
let maybe_index = style
695
.get_box()
696
.animation_name_iter()
697
.position(|animation_name| Some(name) == animation_name.as_atom());
698
699
let index = match maybe_index {
700
Some(index) => index,
701
None => return AnimationUpdate::AnimationCanceled,
702
};
703
704
let total_duration = style.get_box().animation_duration_mod(index).seconds() as f64;
705
if total_duration == 0. {
706
return AnimationUpdate::AnimationCanceled;
707
}
708
709
let mut total_progress = (now - started_at) / total_duration;
710
if total_progress < 0. {
711
warn!("Negative progress found for animation {:?}", name);
712
return AnimationUpdate::Regular;
713
}
714
if total_progress > 1. {
715
total_progress = 1.;
716
}
717
718
// Get the target and the last keyframe position.
719
let last_keyframe_position;
720
let target_keyframe_position;
721
match state.current_direction {
722
AnimationDirection::Normal => {
723
target_keyframe_position = animation
724
.steps
725
.iter()
726
.position(|step| total_progress as f32 <= step.start_percentage.0);
727
728
last_keyframe_position = target_keyframe_position
729
.and_then(|pos| if pos != 0 { Some(pos - 1) } else { None })
730
.unwrap_or(0);
731
},
732
AnimationDirection::Reverse => {
733
target_keyframe_position = animation
734
.steps
735
.iter()
736
.rev()
737
.position(|step| total_progress as f32 <= 1. - step.start_percentage.0)
738
.map(|pos| animation.steps.len() - pos - 1);
739
740
last_keyframe_position = target_keyframe_position
741
.and_then(|pos| {
742
if pos != animation.steps.len() - 1 {
743
Some(pos + 1)
744
} else {
745
None
746
}
747
})
748
.unwrap_or(animation.steps.len() - 1);
749
},
750
_ => unreachable!(),
751
}
752
753
debug!(
754
"update_style_for_animation: keyframe from {:?} to {:?}",
755
last_keyframe_position, target_keyframe_position
756
);
757
758
let target_keyframe = match target_keyframe_position {
759
Some(target) => &animation.steps[target],
760
None => return AnimationUpdate::Regular,
761
};
762
763
let last_keyframe = &animation.steps[last_keyframe_position];
764
765
let relative_timespan =
766
(target_keyframe.start_percentage.0 - last_keyframe.start_percentage.0).abs();
767
let relative_duration = relative_timespan as f64 * duration;
768
let last_keyframe_ended_at = match state.current_direction {
769
AnimationDirection::Normal => {
770
state.started_at + (total_duration * last_keyframe.start_percentage.0 as f64)
771
},
772
AnimationDirection::Reverse => {
773
state.started_at +
774
(total_duration * (1. - last_keyframe.start_percentage.0 as f64))
775
},
776
_ => unreachable!(),
777
};
778
let relative_progress = (now - last_keyframe_ended_at) / relative_duration;
779
780
// TODO: How could we optimise it? Is it such a big deal?
781
let from_style = compute_style_for_animation_step::<E>(
782
context,
783
last_keyframe,
784
&**style,
785
&state.cascade_style,
786
font_metrics_provider,
787
);
788
789
// NB: The spec says that the timing function can be overwritten
790
// from the keyframe style.
791
let mut timing_function = style.get_box().animation_timing_function_mod(index);
792
if last_keyframe.declared_timing_function {
793
// NB: animation_timing_function can never be empty, always has
794
// at least the default value (`ease`).
795
timing_function = from_style.get_box().animation_timing_function_at(0);
796
}
797
798
let target_style = compute_style_for_animation_step::<E>(
799
context,
800
target_keyframe,
801
&from_style,
802
&state.cascade_style,
803
font_metrics_provider,
804
);
805
806
let mut new_style = (*style).clone();
807
808
for property in animation.properties_changed.iter() {
809
debug!(
810
"update_style_for_animation: scanning prop {:?} for animation \"{}\"",
811
property, name
812
);
813
let animation = PropertyAnimation::from_longhand(
814
property,
815
timing_function,
816
Time::from_seconds(relative_duration as f32),
817
&from_style,
818
&target_style,
819
);
820
821
match animation {
822
Some(property_animation) => {
823
debug!(
824
"update_style_for_animation: got property animation for prop {:?}",
825
property
826
);
827
debug!("update_style_for_animation: {:?}", property_animation);
828
property_animation.update(Arc::make_mut(&mut new_style), relative_progress);
829
},
830
None => {
831
debug!(
832
"update_style_for_animation: property animation {:?} not animating",
833
property
834
);
835
},
836
}
837
}
838
839
debug!(
840
"update_style_for_animation: got style change in animation \"{}\"",
841
name
842
);
843
*style = new_style;
844
AnimationUpdate::Regular
845
},
846
}
847
}
848
849
/// Update the style in the node when it finishes.
850
#[cfg(feature = "servo")]
851
pub fn complete_expired_transitions(
852
node: OpaqueNode,
853
style: &mut Arc<ComputedValues>,
854
context: &SharedStyleContext,
855
) -> bool {
856
let had_animations_to_expire;
857
{
858
let all_expired_animations = context.expired_animations.read();
859
let animations_to_expire = all_expired_animations.get(&node);
860
had_animations_to_expire = animations_to_expire.is_some();
861
if let Some(ref animations) = animations_to_expire {
862
for animation in *animations {
863
debug!("Updating expired animation {:?}", animation);
864
// TODO: support animation-fill-mode
865
if let Animation::Transition(_, _, ref frame) = *animation {
866
frame.property_animation.update(Arc::make_mut(style), 1.0);
867
}
868
}
869
}
870
}
871
872
if had_animations_to_expire {
873
context.expired_animations.write().remove(&node);
874
}
875
876
had_animations_to_expire
877
}