mod.rs (16749B)
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 //! Animated values. 6 //! 7 //! Some values, notably colors, cannot be interpolated directly with their 8 //! computed values and need yet another intermediate representation. This 9 //! module's raison d'ĂȘtre is to ultimately contain all these types. 10 11 use crate::color::AbsoluteColor; 12 use crate::properties::{ComputedValues, PropertyId}; 13 use crate::values::computed::url::ComputedUrl; 14 use crate::values::computed::{Angle, Image, Length}; 15 use crate::values::generics::{ClampToNonNegative, NonNegative}; 16 use crate::values::specified::SVGPathData; 17 use crate::values::CSSFloat; 18 use app_units::Au; 19 use smallvec::SmallVec; 20 use std::cmp; 21 22 pub mod color; 23 pub mod effects; 24 mod font; 25 mod grid; 26 pub mod lists; 27 mod svg; 28 pub mod transform; 29 30 /// The category a property falls into for ordering purposes. 31 /// 32 /// https://drafts.csswg.org/web-animations/#calculating-computed-keyframes 33 #[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] 34 enum PropertyCategory { 35 Custom, 36 PhysicalLonghand, 37 LogicalLonghand, 38 Shorthand, 39 } 40 41 impl PropertyCategory { 42 fn of(id: &PropertyId) -> Self { 43 match *id { 44 PropertyId::NonCustom(id) => match id.longhand_or_shorthand() { 45 Ok(id) => { 46 if id.is_logical() { 47 PropertyCategory::LogicalLonghand 48 } else { 49 PropertyCategory::PhysicalLonghand 50 } 51 }, 52 Err(..) => PropertyCategory::Shorthand, 53 }, 54 PropertyId::Custom(..) => PropertyCategory::Custom, 55 } 56 } 57 } 58 59 /// A comparator to sort PropertyIds such that physical longhands are sorted 60 /// before logical longhands and shorthands, shorthands with fewer components 61 /// are sorted before shorthands with more components, and otherwise shorthands 62 /// are sorted by IDL name as defined by [Web Animations][property-order]. 63 /// 64 /// Using this allows us to prioritize values specified by longhands (or smaller 65 /// shorthand subsets) when longhands and shorthands are both specified on the 66 /// one keyframe. 67 /// 68 /// [property-order] https://drafts.csswg.org/web-animations/#calculating-computed-keyframes 69 pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering { 70 let a_category = PropertyCategory::of(a); 71 let b_category = PropertyCategory::of(b); 72 73 if a_category != b_category { 74 return a_category.cmp(&b_category); 75 } 76 77 if a_category != PropertyCategory::Shorthand { 78 return cmp::Ordering::Equal; 79 } 80 81 let a = a.as_shorthand().unwrap(); 82 let b = b.as_shorthand().unwrap(); 83 // Within shorthands, sort by the number of subproperties, then by IDL 84 // name. 85 let subprop_count_a = a.longhands().count(); 86 let subprop_count_b = b.longhands().count(); 87 subprop_count_a 88 .cmp(&subprop_count_b) 89 .then_with(|| a.idl_name_sort_order().cmp(&b.idl_name_sort_order())) 90 } 91 92 /// A helper function to animate two multiplicative factor. 93 pub fn animate_multiplicative_factor( 94 this: CSSFloat, 95 other: CSSFloat, 96 procedure: Procedure, 97 ) -> Result<CSSFloat, ()> { 98 Ok((this - 1.).animate(&(other - 1.), procedure)? + 1.) 99 } 100 101 /// Animate from one value to another. 102 /// 103 /// This trait is derivable with `#[derive(Animate)]`. The derived 104 /// implementation uses a `match` expression with identical patterns for both 105 /// `self` and `other`, calling `Animate::animate` on each fields of the values. 106 /// If a field is annotated with `#[animation(constant)]`, the two values should 107 /// be equal or an error is returned. 108 /// 109 /// If a variant is annotated with `#[animation(error)]`, the corresponding 110 /// `match` arm returns an error. 111 /// 112 /// Trait bounds for type parameter `Foo` can be opted out of with 113 /// `#[animation(no_bound(Foo))]` on the type definition, trait bounds for 114 /// fields can be opted into with `#[animation(field_bound)]` on the field. 115 pub trait Animate: Sized { 116 /// Animate a value towards another one, given an animation procedure. 117 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()>; 118 } 119 120 /// An animation procedure. 121 /// 122 /// <https://drafts.csswg.org/web-animations/#procedures-for-animating-properties> 123 #[allow(missing_docs)] 124 #[derive(Clone, Copy, Debug, PartialEq)] 125 pub enum Procedure { 126 /// <https://drafts.csswg.org/web-animations/#animation-interpolation> 127 Interpolate { progress: f64 }, 128 /// <https://drafts.csswg.org/web-animations/#animation-addition> 129 Add, 130 /// <https://drafts.csswg.org/web-animations/#animation-accumulation> 131 Accumulate { count: u64 }, 132 } 133 134 /// The context needed to provide an animated value from a computed value. 135 pub struct Context<'a> { 136 /// The computed style we're taking the value from. 137 pub style: &'a ComputedValues, 138 } 139 140 /// Conversion between computed values and intermediate values for animations. 141 /// 142 /// Notably, colors are represented as four floats during animations. 143 /// 144 /// This trait is derivable with `#[derive(ToAnimatedValue)]`. 145 pub trait ToAnimatedValue { 146 /// The type of the animated value. 147 type AnimatedValue; 148 149 /// Converts this value to an animated value. 150 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue; 151 152 /// Converts back an animated value into a computed value. 153 fn from_animated_value(animated: Self::AnimatedValue) -> Self; 154 } 155 156 /// Returns a value similar to `self` that represents zero. 157 /// 158 /// This trait is derivable with `#[derive(ToAnimatedValue)]`. If a field is 159 /// annotated with `#[animation(constant)]`, a clone of its value will be used 160 /// instead of calling `ToAnimatedZero::to_animated_zero` on it. 161 /// 162 /// If a variant is annotated with `#[animation(error)]`, the corresponding 163 /// `match` arm is not generated. 164 /// 165 /// Trait bounds for type parameter `Foo` can be opted out of with 166 /// `#[animation(no_bound(Foo))]` on the type definition. 167 pub trait ToAnimatedZero: Sized { 168 /// Returns a value that, when added with an underlying value, will produce the underlying 169 /// value. This is used for SMIL animation's "by-animation" where SMIL first interpolates from 170 /// the zero value to the 'by' value, and then adds the result to the underlying value. 171 /// 172 /// This is not the necessarily the same as the initial value of a property. For example, the 173 /// initial value of 'stroke-width' is 1, but the zero value is 0, since adding 1 to the 174 /// underlying value will not produce the underlying value. 175 fn to_animated_zero(&self) -> Result<Self, ()>; 176 } 177 178 impl Procedure { 179 /// Returns this procedure as a pair of weights. 180 /// 181 /// This is useful for animations that don't animate differently 182 /// depending on the used procedure. 183 #[inline] 184 pub fn weights(self) -> (f64, f64) { 185 match self { 186 Procedure::Interpolate { progress } => (1. - progress, progress), 187 Procedure::Add => (1., 1.), 188 Procedure::Accumulate { count } => (count as f64, 1.), 189 } 190 } 191 } 192 193 /// <https://drafts.csswg.org/css-transitions/#animtype-number> 194 impl Animate for i32 { 195 #[inline] 196 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 197 Ok(((*self as f64).animate(&(*other as f64), procedure)? + 0.5).floor() as i32) 198 } 199 } 200 201 /// <https://drafts.csswg.org/css-transitions/#animtype-number> 202 impl Animate for f32 { 203 #[inline] 204 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 205 let ret = (*self as f64).animate(&(*other as f64), procedure)?; 206 Ok(ret.min(f32::MAX as f64).max(f32::MIN as f64) as f32) 207 } 208 } 209 210 /// <https://drafts.csswg.org/css-transitions/#animtype-number> 211 impl Animate for f64 { 212 #[inline] 213 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 214 let (self_weight, other_weight) = procedure.weights(); 215 216 let ret = *self * self_weight + *other * other_weight; 217 Ok(ret.min(f64::MAX).max(f64::MIN)) 218 } 219 } 220 221 impl<T> Animate for Option<T> 222 where 223 T: Animate, 224 { 225 #[inline] 226 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 227 match (self.as_ref(), other.as_ref()) { 228 (Some(ref this), Some(ref other)) => Ok(Some(this.animate(other, procedure)?)), 229 (None, None) => Ok(None), 230 _ => Err(()), 231 } 232 } 233 } 234 235 impl<T: ToAnimatedValue + ClampToNonNegative> ToAnimatedValue for NonNegative<T> { 236 type AnimatedValue = NonNegative<<T as ToAnimatedValue>::AnimatedValue>; 237 238 #[inline] 239 fn to_animated_value(self, cx: &crate::values::animated::Context) -> Self::AnimatedValue { 240 NonNegative(self.0.to_animated_value(cx)) 241 } 242 243 #[inline] 244 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 245 Self(<T as ToAnimatedValue>::from_animated_value(animated.0).clamp_to_non_negative()) 246 } 247 } 248 249 impl ToAnimatedValue for Au { 250 type AnimatedValue = Length; 251 252 #[inline] 253 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 254 Length::new(self.to_f32_px()).to_animated_value(context) 255 } 256 257 #[inline] 258 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 259 Au::from_f32_px(Length::from_animated_value(animated).px()) 260 } 261 } 262 263 impl<T: Animate> Animate for Box<T> { 264 #[inline] 265 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 266 Ok(Box::new((**self).animate(&other, procedure)?)) 267 } 268 } 269 270 impl<T> ToAnimatedValue for Option<T> 271 where 272 T: ToAnimatedValue, 273 { 274 type AnimatedValue = Option<<T as ToAnimatedValue>::AnimatedValue>; 275 276 #[inline] 277 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 278 self.map(|v| T::to_animated_value(v, context)) 279 } 280 281 #[inline] 282 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 283 animated.map(T::from_animated_value) 284 } 285 } 286 287 impl<T> ToAnimatedValue for Vec<T> 288 where 289 T: ToAnimatedValue, 290 { 291 type AnimatedValue = Vec<<T as ToAnimatedValue>::AnimatedValue>; 292 293 #[inline] 294 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 295 self.into_iter() 296 .map(|v| v.to_animated_value(context)) 297 .collect() 298 } 299 300 #[inline] 301 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 302 animated.into_iter().map(T::from_animated_value).collect() 303 } 304 } 305 306 impl<T> ToAnimatedValue for thin_vec::ThinVec<T> 307 where 308 T: ToAnimatedValue, 309 { 310 type AnimatedValue = thin_vec::ThinVec<<T as ToAnimatedValue>::AnimatedValue>; 311 312 #[inline] 313 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 314 self.into_iter() 315 .map(|v| v.to_animated_value(context)) 316 .collect() 317 } 318 319 #[inline] 320 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 321 animated.into_iter().map(T::from_animated_value).collect() 322 } 323 } 324 325 impl<T> ToAnimatedValue for Box<T> 326 where 327 T: ToAnimatedValue, 328 { 329 type AnimatedValue = Box<<T as ToAnimatedValue>::AnimatedValue>; 330 331 #[inline] 332 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 333 Box::new((*self).to_animated_value(context)) 334 } 335 336 #[inline] 337 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 338 Box::new(T::from_animated_value(*animated)) 339 } 340 } 341 342 impl<T> ToAnimatedValue for Box<[T]> 343 where 344 T: ToAnimatedValue, 345 { 346 type AnimatedValue = Box<[<T as ToAnimatedValue>::AnimatedValue]>; 347 348 #[inline] 349 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 350 self.into_vec() 351 .into_iter() 352 .map(|v| v.to_animated_value(context)) 353 .collect() 354 } 355 356 #[inline] 357 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 358 animated 359 .into_vec() 360 .into_iter() 361 .map(T::from_animated_value) 362 .collect() 363 } 364 } 365 366 impl<T> ToAnimatedValue for crate::OwnedSlice<T> 367 where 368 T: ToAnimatedValue, 369 { 370 type AnimatedValue = crate::OwnedSlice<<T as ToAnimatedValue>::AnimatedValue>; 371 372 #[inline] 373 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 374 self.into_box().to_animated_value(context).into() 375 } 376 377 #[inline] 378 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 379 Self::from(Box::from_animated_value(animated.into_box())) 380 } 381 } 382 383 impl<T> ToAnimatedValue for SmallVec<[T; 1]> 384 where 385 T: ToAnimatedValue, 386 { 387 type AnimatedValue = SmallVec<[T::AnimatedValue; 1]>; 388 389 #[inline] 390 fn to_animated_value(self, context: &Context) -> Self::AnimatedValue { 391 self.into_iter() 392 .map(|v| v.to_animated_value(context)) 393 .collect() 394 } 395 396 #[inline] 397 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 398 animated.into_iter().map(T::from_animated_value).collect() 399 } 400 } 401 402 macro_rules! trivial_to_animated_value { 403 ($ty:ty) => { 404 impl $crate::values::animated::ToAnimatedValue for $ty { 405 type AnimatedValue = Self; 406 407 #[inline] 408 fn to_animated_value(self, _: &Context) -> Self { 409 self 410 } 411 412 #[inline] 413 fn from_animated_value(animated: Self::AnimatedValue) -> Self { 414 animated 415 } 416 } 417 }; 418 } 419 420 trivial_to_animated_value!(crate::Atom); 421 trivial_to_animated_value!(Angle); 422 trivial_to_animated_value!(ComputedUrl); 423 trivial_to_animated_value!(bool); 424 trivial_to_animated_value!(f32); 425 trivial_to_animated_value!(i32); 426 trivial_to_animated_value!(u32); 427 trivial_to_animated_value!(usize); 428 trivial_to_animated_value!(AbsoluteColor); 429 trivial_to_animated_value!(crate::values::generics::color::ColorMixFlags); 430 // Note: This implementation is for ToAnimatedValue of ShapeSource. 431 // 432 // SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the 433 // types, we have to do "impl ToAnimatedValue for Box<[T]>" first. 434 // However, the general version of "impl ToAnimatedValue for Box<[T]>" needs to 435 // clone |T| and convert it into |T::AnimatedValue|. However, for SVGPathData 436 // that is unnecessary--moving |T| is sufficient. So here, we implement this 437 // trait manually. 438 trivial_to_animated_value!(SVGPathData); 439 // FIXME: Bug 1514342, Image is not animatable, but we still need to implement 440 // this to avoid adding this derive to generic::Image and all its arms. We can 441 // drop this after landing Bug 1514342. 442 trivial_to_animated_value!(Image); 443 444 impl ToAnimatedZero for Au { 445 #[inline] 446 fn to_animated_zero(&self) -> Result<Self, ()> { 447 Ok(Au(0)) 448 } 449 } 450 451 impl ToAnimatedZero for f32 { 452 #[inline] 453 fn to_animated_zero(&self) -> Result<Self, ()> { 454 Ok(0.) 455 } 456 } 457 458 impl ToAnimatedZero for f64 { 459 #[inline] 460 fn to_animated_zero(&self) -> Result<Self, ()> { 461 Ok(0.) 462 } 463 } 464 465 impl ToAnimatedZero for i32 { 466 #[inline] 467 fn to_animated_zero(&self) -> Result<Self, ()> { 468 Ok(0) 469 } 470 } 471 472 impl<T> ToAnimatedZero for Box<T> 473 where 474 T: ToAnimatedZero, 475 { 476 #[inline] 477 fn to_animated_zero(&self) -> Result<Self, ()> { 478 Ok(Box::new((**self).to_animated_zero()?)) 479 } 480 } 481 482 impl<T> ToAnimatedZero for Option<T> 483 where 484 T: ToAnimatedZero, 485 { 486 #[inline] 487 fn to_animated_zero(&self) -> Result<Self, ()> { 488 match *self { 489 Some(ref value) => Ok(Some(value.to_animated_zero()?)), 490 None => Ok(None), 491 } 492 } 493 } 494 495 impl<T> ToAnimatedZero for Vec<T> 496 where 497 T: ToAnimatedZero, 498 { 499 #[inline] 500 fn to_animated_zero(&self) -> Result<Self, ()> { 501 self.iter().map(|v| v.to_animated_zero()).collect() 502 } 503 } 504 505 impl<T> ToAnimatedZero for thin_vec::ThinVec<T> 506 where 507 T: ToAnimatedZero, 508 { 509 #[inline] 510 fn to_animated_zero(&self) -> Result<Self, ()> { 511 self.iter().map(|v| v.to_animated_zero()).collect() 512 } 513 } 514 515 impl<T> ToAnimatedZero for Box<[T]> 516 where 517 T: ToAnimatedZero, 518 { 519 #[inline] 520 fn to_animated_zero(&self) -> Result<Self, ()> { 521 self.iter().map(|v| v.to_animated_zero()).collect() 522 } 523 } 524 525 impl<T> ToAnimatedZero for crate::OwnedSlice<T> 526 where 527 T: ToAnimatedZero, 528 { 529 #[inline] 530 fn to_animated_zero(&self) -> Result<Self, ()> { 531 self.iter().map(|v| v.to_animated_zero()).collect() 532 } 533 } 534 535 impl<T> ToAnimatedZero for crate::ArcSlice<T> 536 where 537 T: ToAnimatedZero, 538 { 539 #[inline] 540 fn to_animated_zero(&self) -> Result<Self, ()> { 541 let v = self 542 .iter() 543 .map(|v| v.to_animated_zero()) 544 .collect::<Result<Vec<_>, _>>()?; 545 Ok(crate::ArcSlice::from_iter(v.into_iter())) 546 } 547 }