use cranpose_animation::AnimationType;
use cranpose_ui_graphics::Size;
use crate::{
composable,
modifier::GraphicsLayer,
widgets::animated_content::{ContentTransform, SeekableAnimatedContent},
};
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub(crate) struct TransitionEffects {
fade: bool,
slide_x: Option<f32>,
slide_y: Option<f32>,
scale: Option<f32>,
}
impl TransitionEffects {
fn merged(self, other: Self) -> Self {
Self {
fade: self.fade || other.fade,
slide_x: self.slide_x.or(other.slide_x),
slide_y: self.slide_y.or(other.slide_y),
scale: self.scale.or(other.scale),
}
}
pub(crate) fn layer(self, progress: f32, size: Size) -> GraphicsLayer {
let away = 1.0 - progress;
GraphicsLayer {
alpha: if self.fade {
progress.clamp(0.0, 1.0)
} else {
1.0
},
translation_x: self.slide_x.map_or(0.0, |x| x * size.width * away),
translation_y: self.slide_y.map_or(0.0, |y| y * size.height * away),
scale: self
.scale
.map_or(1.0, |scale| scale + (1.0 - scale) * progress),
..GraphicsLayer::default()
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct EnterTransition {
effects: TransitionEffects,
animation: Option<AnimationType>,
}
impl EnterTransition {
pub fn none() -> Self {
Self {
effects: TransitionEffects::default(),
animation: None,
}
}
pub fn with_animation(mut self, animation: AnimationType) -> Self {
self.animation = Some(animation);
self
}
pub fn together_with(self, exit: ExitTransition) -> ContentTransform {
ContentTransform {
enter: self,
exit,
size_transform: None,
}
}
pub(crate) fn effects(&self) -> TransitionEffects {
self.effects
}
pub(crate) fn animation(&self) -> AnimationType {
self.animation.unwrap_or_default()
}
}
impl std::ops::Add for EnterTransition {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
effects: self.effects.merged(other.effects),
animation: self.animation.or(other.animation),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ExitTransition {
effects: TransitionEffects,
animation: Option<AnimationType>,
}
impl ExitTransition {
pub fn none() -> Self {
Self {
effects: TransitionEffects::default(),
animation: None,
}
}
pub fn with_animation(mut self, animation: AnimationType) -> Self {
self.animation = Some(animation);
self
}
pub(crate) fn effects(&self) -> TransitionEffects {
self.effects
}
pub(crate) fn animation(&self) -> AnimationType {
self.animation.unwrap_or_default()
}
}
impl std::ops::Add for ExitTransition {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
effects: self.effects.merged(other.effects),
animation: self.animation.or(other.animation),
}
}
}
pub fn fade_in() -> EnterTransition {
enter(TransitionEffects {
fade: true,
..TransitionEffects::default()
})
}
pub fn fade_out() -> ExitTransition {
exit(TransitionEffects {
fade: true,
..TransitionEffects::default()
})
}
pub fn slide_in_vertically(initial_offset_fraction: f32) -> EnterTransition {
enter(TransitionEffects {
slide_y: Some(initial_offset_fraction),
..TransitionEffects::default()
})
}
pub fn slide_out_vertically(target_offset_fraction: f32) -> ExitTransition {
exit(TransitionEffects {
slide_y: Some(target_offset_fraction),
..TransitionEffects::default()
})
}
pub fn slide_in_horizontally(initial_offset_fraction: f32) -> EnterTransition {
enter(TransitionEffects {
slide_x: Some(initial_offset_fraction),
..TransitionEffects::default()
})
}
pub fn slide_out_horizontally(target_offset_fraction: f32) -> ExitTransition {
exit(TransitionEffects {
slide_x: Some(target_offset_fraction),
..TransitionEffects::default()
})
}
pub fn scale_in(initial_scale: f32) -> EnterTransition {
enter(TransitionEffects {
scale: Some(initial_scale),
..TransitionEffects::default()
})
}
pub fn scale_out(target_scale: f32) -> ExitTransition {
exit(TransitionEffects {
scale: Some(target_scale),
..TransitionEffects::default()
})
}
fn enter(effects: TransitionEffects) -> EnterTransition {
EnterTransition {
effects,
animation: None,
}
}
fn exit(effects: TransitionEffects) -> ExitTransition {
ExitTransition {
effects,
animation: None,
}
}
#[composable]
pub fn AnimatedVisibility<F>(
visible: bool,
enter: EnterTransition,
exit: ExitTransition,
mut content: F,
) where
F: FnMut() + 'static,
{
SeekableAnimatedContent(
visible,
None,
move |_, &shown| {
if shown {
enter.together_with(ExitTransition::none().with_animation(enter.animation()))
} else {
EnterTransition::none()
.with_animation(exit.animation())
.together_with(exit)
}
},
move |shown| {
if shown {
content();
}
},
);
}