cranpose_ui/widgets/
animated_visibility.rs1use cranpose_animation::AnimationType;
9
10use crate::{
11 composable,
12 modifier::{GraphicsLayer, Modifier},
13 widgets::{
14 box_widget::{Box, BoxSpec},
15 crossfade::animate_float_with_initial,
16 },
17};
18
19const VISIBILITY_PROGRESS_EPSILON: f32 = 0.001;
22
23#[derive(Clone, Copy, Debug, PartialEq)]
28pub struct EnterTransition {
29 fade: bool,
30 slide_vertical_fraction: Option<f32>,
31 animation: Option<AnimationType>,
32}
33
34impl EnterTransition {
35 pub fn none() -> Self {
39 Self {
40 fade: false,
41 slide_vertical_fraction: None,
42 animation: None,
43 }
44 }
45
46 pub fn with_animation(mut self, animation: AnimationType) -> Self {
51 self.animation = Some(animation);
52 self
53 }
54
55 pub(crate) fn has_fade(&self) -> bool {
56 self.fade
57 }
58
59 pub(crate) fn slide_vertical_fraction(&self) -> Option<f32> {
60 self.slide_vertical_fraction
61 }
62
63 pub(crate) fn animation(&self) -> AnimationType {
64 self.animation.unwrap_or_default()
65 }
66}
67
68impl std::ops::Add for EnterTransition {
69 type Output = Self;
70
71 fn add(self, other: Self) -> Self {
72 Self {
73 fade: self.fade || other.fade,
74 slide_vertical_fraction: self
75 .slide_vertical_fraction
76 .or(other.slide_vertical_fraction),
77 animation: self.animation.or(other.animation),
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq)]
87pub struct ExitTransition {
88 fade: bool,
89 slide_vertical_fraction: Option<f32>,
90 animation: Option<AnimationType>,
91}
92
93impl ExitTransition {
94 pub fn none() -> Self {
98 Self {
99 fade: false,
100 slide_vertical_fraction: None,
101 animation: None,
102 }
103 }
104
105 pub fn with_animation(mut self, animation: AnimationType) -> Self {
108 self.animation = Some(animation);
109 self
110 }
111
112 pub(crate) fn has_fade(&self) -> bool {
113 self.fade
114 }
115
116 pub(crate) fn slide_vertical_fraction(&self) -> Option<f32> {
117 self.slide_vertical_fraction
118 }
119
120 pub(crate) fn animation(&self) -> AnimationType {
121 self.animation.unwrap_or_default()
122 }
123}
124
125impl std::ops::Add for ExitTransition {
126 type Output = Self;
127
128 fn add(self, other: Self) -> Self {
129 Self {
130 fade: self.fade || other.fade,
131 slide_vertical_fraction: self
132 .slide_vertical_fraction
133 .or(other.slide_vertical_fraction),
134 animation: self.animation.or(other.animation),
135 }
136 }
137}
138
139pub fn fade_in() -> EnterTransition {
144 EnterTransition {
145 fade: true,
146 ..EnterTransition::none()
147 }
148}
149
150pub fn fade_out() -> ExitTransition {
155 ExitTransition {
156 fade: true,
157 ..ExitTransition::none()
158 }
159}
160
161pub fn slide_in_vertically(initial_offset_fraction: f32) -> EnterTransition {
169 EnterTransition {
170 slide_vertical_fraction: Some(initial_offset_fraction),
171 ..EnterTransition::none()
172 }
173}
174
175pub fn slide_out_vertically(target_offset_fraction: f32) -> ExitTransition {
182 ExitTransition {
183 slide_vertical_fraction: Some(target_offset_fraction),
184 ..ExitTransition::none()
185 }
186}
187
188#[composable]
206pub fn AnimatedVisibility<F>(
207 visible: bool,
208 enter: EnterTransition,
209 exit: ExitTransition,
210 content: F,
211) where
212 F: FnMut() + 'static,
213{
214 let target = if visible { 1.0 } else { 0.0 };
215 let animation = if visible {
216 enter.animation()
217 } else {
218 exit.animation()
219 };
220 let progress_state = animate_float_with_initial(target, target, animation);
221 let progress = progress_state.value();
222
223 let composed = visible || progress > VISIBILITY_PROGRESS_EPSILON;
224 if composed {
225 let fade = if visible {
226 enter.has_fade()
227 } else {
228 exit.has_fade()
229 };
230 let slide_fraction = if visible {
231 enter.slide_vertical_fraction()
232 } else {
233 exit.slide_vertical_fraction()
234 };
235
236 let alpha = if fade { progress.clamp(0.0, 1.0) } else { 1.0 };
237 let mut modifier = Modifier::empty().graphics_layer_value(GraphicsLayer {
238 alpha,
239 ..Default::default()
240 });
241 if let Some(fraction) = slide_fraction {
242 modifier = modifier.offset_fraction(0.0, fraction * (1.0 - progress));
243 }
244
245 Box(modifier, BoxSpec::new(), content);
246 }
247}