style/values/animated/
lists.rs

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//! Lists have various ways of being animated, this module implements them.
6//!
7//! See https://drafts.csswg.org/web-animations-1/#animating-properties
8
9/// https://drafts.csswg.org/web-animations-1/#by-computed-value
10pub mod by_computed_value {
11    use crate::values::{
12        animated::{Animate, Procedure},
13        distance::{ComputeSquaredDistance, SquaredDistance},
14    };
15    use std::iter::FromIterator;
16
17    #[allow(missing_docs)]
18    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
19    where
20        T: Animate,
21        C: FromIterator<T>,
22    {
23        if left.len() != right.len() {
24            return Err(());
25        }
26        left.iter()
27            .zip(right.iter())
28            .map(|(left, right)| left.animate(right, procedure))
29            .collect()
30    }
31
32    #[allow(missing_docs)]
33    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
34    where
35        T: ComputeSquaredDistance,
36    {
37        if left.len() != right.len() {
38            return Err(());
39        }
40        left.iter()
41            .zip(right.iter())
42            .map(|(left, right)| left.compute_squared_distance(right))
43            .sum()
44    }
45}
46
47/// This is the animation used for some of the types like shadows and filters, where the
48/// interpolation happens with the zero value if one of the sides is not present.
49///
50/// https://drafts.csswg.org/web-animations-1/#animating-shadow-lists
51pub mod with_zero {
52    use crate::values::animated::ToAnimatedZero;
53    use crate::values::{
54        animated::{Animate, Procedure},
55        distance::{ComputeSquaredDistance, SquaredDistance},
56    };
57    use itertools::{EitherOrBoth, Itertools};
58    use std::iter::FromIterator;
59
60    #[allow(missing_docs)]
61    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
62    where
63        T: Animate + Clone + ToAnimatedZero,
64        C: FromIterator<T>,
65    {
66        if procedure == Procedure::Add {
67            return Ok(left.iter().chain(right.iter()).cloned().collect());
68        }
69        left.iter()
70            .zip_longest(right.iter())
71            .map(|it| match it {
72                EitherOrBoth::Both(left, right) => left.animate(right, procedure),
73                EitherOrBoth::Left(left) => left.animate(&left.to_animated_zero()?, procedure),
74                EitherOrBoth::Right(right) => right.to_animated_zero()?.animate(right, procedure),
75            })
76            .collect()
77    }
78
79    #[allow(missing_docs)]
80    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
81    where
82        T: ToAnimatedZero + ComputeSquaredDistance,
83    {
84        left.iter()
85            .zip_longest(right.iter())
86            .map(|it| match it {
87                EitherOrBoth::Both(left, right) => left.compute_squared_distance(right),
88                EitherOrBoth::Left(item) | EitherOrBoth::Right(item) => {
89                    item.to_animated_zero()?.compute_squared_distance(item)
90                },
91            })
92            .sum()
93    }
94}
95
96/// https://drafts.csswg.org/web-animations-1/#repeatable-list
97pub mod repeatable_list {
98    use crate::values::{
99        animated::{Animate, Procedure},
100        distance::{ComputeSquaredDistance, SquaredDistance},
101    };
102    use std::iter::FromIterator;
103
104    #[allow(missing_docs)]
105    pub fn animate<T, C>(left: &[T], right: &[T], procedure: Procedure) -> Result<C, ()>
106    where
107        T: Animate,
108        C: FromIterator<T>,
109    {
110        use num_integer::lcm;
111        // If the length of either list is zero, the least common multiple is undefined.
112        if left.is_empty() || right.is_empty() {
113            return Err(());
114        }
115        let len = lcm(left.len(), right.len());
116        left.iter()
117            .cycle()
118            .zip(right.iter().cycle())
119            .take(len)
120            .map(|(left, right)| left.animate(right, procedure))
121            .collect()
122    }
123
124    #[allow(missing_docs)]
125    pub fn squared_distance<T>(left: &[T], right: &[T]) -> Result<SquaredDistance, ()>
126    where
127        T: ComputeSquaredDistance,
128    {
129        use num_integer::lcm;
130        if left.is_empty() || right.is_empty() {
131            return Err(());
132        }
133        let len = lcm(left.len(), right.len());
134        left.iter()
135            .cycle()
136            .zip(right.iter().cycle())
137            .take(len)
138            .map(|(left, right)| left.compute_squared_distance(right))
139            .sum()
140    }
141}