use std::collections::BTreeMap;
use serde::Deserialize;
pub type SharedId = u32;
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Driver {
Timing {
to: f32,
#[serde(default = "default_duration")]
duration: f32,
#[serde(default)]
easing: Easing,
},
Spring {
to: f32,
#[serde(default = "default_stiffness")]
stiffness: f32,
#[serde(default = "default_damping")]
damping: f32,
#[serde(default = "default_mass")]
mass: f32,
},
Repeat {
animation: Box<Driver>,
#[serde(default = "default_count")]
count: i32,
#[serde(default)]
reverse: bool,
},
Sequence { steps: Vec<Driver> },
Delay { delay: f32, animation: Box<Driver> },
}
#[derive(Debug, Clone, Copy, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Easing {
#[default]
Linear,
EaseIn,
EaseOut,
EaseInOut,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "kind", rename_all = "camelCase")]
pub enum AnimationCommand {
Declare { id: SharedId, initial: f32 },
Set { id: SharedId, value: f32 },
Animate {
id: SharedId,
driver: Driver,
#[serde(default)]
token: Option<u64>,
},
Cancel { id: SharedId },
Clear,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Binding {
Shared { id: SharedId },
Interpolate {
id: SharedId,
input: Vec<f32>,
output: Vec<f32>,
},
InterpolateColor {
id: SharedId,
input: Vec<f32>,
output: Vec<[f32; 4]>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AnimatableProperty {
TranslateX,
TranslateY,
Scale,
ScaleX,
ScaleY,
Rotate,
Opacity,
BackgroundColor,
BorderColor,
Color,
Width,
Height,
MinWidth,
MinHeight,
MaxWidth,
MaxHeight,
Left,
Right,
Top,
Bottom,
FlexBasis,
Gap,
RowGap,
ColumnGap,
AspectRatio,
FilterParam {
index: u8,
name: String,
},
BackdropParam {
index: u8,
name: String,
},
Transform3d(Transform3dField),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Transform3dField {
Perspective,
TranslateX,
TranslateY,
TranslateZ,
RotateX,
RotateY,
RotateZ,
Scale,
ScaleX,
ScaleY,
OriginX,
OriginY,
}
impl AnimatableProperty {
pub fn value_kind(&self) -> ValueKind {
match self {
Self::TranslateX
| Self::TranslateY
| Self::Width
| Self::Height
| Self::MinWidth
| Self::MinHeight
| Self::MaxWidth
| Self::MaxHeight
| Self::Left
| Self::Right
| Self::Top
| Self::Bottom
| Self::FlexBasis
| Self::Gap
| Self::RowGap
| Self::ColumnGap => ValueKind::Length,
Self::Scale | Self::ScaleX | Self::ScaleY | Self::Opacity | Self::AspectRatio => {
ValueKind::Scalar
}
Self::Rotate => ValueKind::Angle,
Self::Transform3d(field) => match field {
Transform3dField::RotateX
| Transform3dField::RotateY
| Transform3dField::RotateZ => ValueKind::Angle,
Transform3dField::Scale | Transform3dField::ScaleX | Transform3dField::ScaleY => {
ValueKind::Scalar
}
_ => ValueKind::Length,
},
Self::BackgroundColor | Self::BorderColor | Self::Color => ValueKind::Color,
Self::FilterParam { .. } | Self::BackdropParam { .. } => ValueKind::Scalar,
}
}
pub fn is_transform(&self) -> bool {
matches!(
self,
Self::TranslateX
| Self::TranslateY
| Self::Scale
| Self::ScaleX
| Self::ScaleY
| Self::Rotate
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueKind {
Scalar,
Length,
Color,
Angle,
}
#[derive(Debug, Clone, Default)]
pub struct AnimatedBindings(pub BTreeMap<AnimatableProperty, Binding>);
impl AnimatedBindings {
pub fn get(&self, property: AnimatableProperty) -> Option<&Binding> {
self.0.get(&property)
}
pub fn contains(&self, property: AnimatableProperty) -> bool {
self.0.contains_key(&property)
}
pub fn has_transform(&self) -> bool {
self.0.keys().any(|p| p.is_transform())
}
pub fn has_filter_params(&self) -> bool {
self.0
.keys()
.any(|p| matches!(p, AnimatableProperty::FilterParam { .. }))
}
pub fn has_backdrop_params(&self) -> bool {
self.0
.keys()
.any(|p| matches!(p, AnimatableProperty::BackdropParam { .. }))
}
pub fn has_transform3d(&self) -> bool {
self.0
.keys()
.any(|p| matches!(p, AnimatableProperty::Transform3d(_)))
}
pub fn iter(&self) -> impl Iterator<Item = (&AnimatableProperty, &Binding)> {
self.0.iter()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
fn default_duration() -> f32 {
0.3
}
fn default_stiffness() -> f32 {
100.0
}
fn default_damping() -> f32 {
10.0
}
fn default_mass() -> f32 {
1.0
}
fn default_count() -> i32 {
1
}