i_slint_core/
animations.rs1#![warn(missing_docs)]
5use alloc::boxed::Box;
8use core::cell::Cell;
9#[cfg(not(feature = "std"))]
10use num_traits::Float;
11
12mod cubic_bezier {
13 type S = f32;
16 use euclid::default::Point2D as Point;
17 #[allow(unused)]
18 use num_traits::Float;
19 trait Scalar {
20 const ONE: f32 = 1.;
21 const THREE: f32 = 3.;
22 const HALF: f32 = 0.5;
23 const SIX: f32 = 6.;
24 const NINE: f32 = 9.;
25 fn value(v: f32) -> f32 {
26 v
27 }
28 }
29 impl Scalar for f32 {}
30 pub struct CubicBezierSegment {
31 pub from: Point<S>,
32 pub ctrl1: Point<S>,
33 pub ctrl2: Point<S>,
34 pub to: Point<S>,
35 }
36
37 impl CubicBezierSegment {
38 pub fn x(&self, t: S) -> S {
40 let t2 = t * t;
41 let t3 = t2 * t;
42 let one_t = S::ONE - t;
43 let one_t2 = one_t * one_t;
44 let one_t3 = one_t2 * one_t;
45
46 self.from.x * one_t3
47 + self.ctrl1.x * S::THREE * one_t2 * t
48 + self.ctrl2.x * S::THREE * one_t * t2
49 + self.to.x * t3
50 }
51
52 pub fn y(&self, t: S) -> S {
54 let t2 = t * t;
55 let t3 = t2 * t;
56 let one_t = S::ONE - t;
57 let one_t2 = one_t * one_t;
58 let one_t3 = one_t2 * one_t;
59
60 self.from.y * one_t3
61 + self.ctrl1.y * S::THREE * one_t2 * t
62 + self.ctrl2.y * S::THREE * one_t * t2
63 + self.to.y * t3
64 }
65
66 #[inline]
67 fn derivative_coefficients(&self, t: S) -> (S, S, S, S) {
68 let t2 = t * t;
69 (
70 -S::THREE * t2 + S::SIX * t - S::THREE,
71 S::NINE * t2 - S::value(12.0) * t + S::THREE,
72 -S::NINE * t2 + S::SIX * t,
73 S::THREE * t2,
74 )
75 }
76
77 pub fn dx(&self, t: S) -> S {
79 let (c0, c1, c2, c3) = self.derivative_coefficients(t);
80 self.from.x * c0 + self.ctrl1.x * c1 + self.ctrl2.x * c2 + self.to.x * c3
81 }
82 }
83
84 impl CubicBezierSegment {
85 pub fn solve_t_for_x(&self, x: S, t_range: core::ops::Range<S>, tolerance: S) -> S {
87 debug_assert!(t_range.start <= t_range.end);
88 let from = self.x(t_range.start);
89 let to = self.x(t_range.end);
90 if x <= from {
91 return t_range.start;
92 }
93 if x >= to {
94 return t_range.end;
95 }
96
97 let mut t = x - from / (to - from);
99 for _ in 0..8 {
100 let x2 = self.x(t);
101
102 if S::abs(x2 - x) <= tolerance {
103 return t;
104 }
105
106 let dx = self.dx(t);
107
108 if dx <= S::EPSILON {
109 break;
110 }
111
112 t -= (x2 - x) / dx;
113 }
114
115 let mut min = t_range.start;
117 let mut max = t_range.end;
118 let mut t = S::HALF;
119
120 while min < max {
121 let x2 = self.x(t);
122
123 if S::abs(x2 - x) < tolerance {
124 return t;
125 }
126
127 if x > x2 {
128 min = t;
129 } else {
130 max = t;
131 }
132
133 t = (max - min) * S::HALF + min;
134 }
135
136 t
137 }
138 }
139}
140
141#[repr(C, u32)]
143#[derive(Debug, Clone, Copy, PartialEq, Default)]
144pub enum EasingCurve {
145 #[default]
147 Linear,
148 CubicBezier([f32; 4]),
150 EaseInElastic,
152 EaseOutElastic,
154 EaseInOutElastic,
156 EaseInBounce,
158 EaseOutBounce,
160 EaseInOutBounce,
162 }
164
165#[repr(transparent)]
167#[derive(Copy, Clone, Debug, Default, PartialEq, Ord, PartialOrd, Eq)]
168pub struct Instant(pub u64);
169
170impl core::ops::Sub<Instant> for Instant {
171 type Output = core::time::Duration;
172 fn sub(self, other: Self) -> core::time::Duration {
173 core::time::Duration::from_millis(self.0 - other.0)
174 }
175}
176
177impl core::ops::Sub<core::time::Duration> for Instant {
178 type Output = Instant;
179 fn sub(self, other: core::time::Duration) -> Instant {
180 Self(self.0 - other.as_millis() as u64)
181 }
182}
183
184impl core::ops::Add<core::time::Duration> for Instant {
185 type Output = Instant;
186 fn add(self, other: core::time::Duration) -> Instant {
187 Self(self.0 + other.as_millis() as u64)
188 }
189}
190
191impl core::ops::AddAssign<core::time::Duration> for Instant {
192 fn add_assign(&mut self, other: core::time::Duration) {
193 self.0 += other.as_millis() as u64;
194 }
195}
196
197impl core::ops::SubAssign<core::time::Duration> for Instant {
198 fn sub_assign(&mut self, other: core::time::Duration) {
199 self.0 -= other.as_millis() as u64;
200 }
201}
202
203impl Instant {
204 pub fn duration_since(self, earlier: Instant) -> core::time::Duration {
208 self - earlier
209 }
210
211 pub fn now() -> Self {
214 Self(Self::duration_since_start().as_millis() as u64)
215 }
216
217 fn duration_since_start() -> core::time::Duration {
218 crate::context::GLOBAL_CONTEXT
219 .with(|p| p.get().map(|p| p.platform().duration_since_start()))
220 .unwrap_or_default()
221 }
222
223 pub fn as_millis(&self) -> u64 {
225 self.0
226 }
227}
228
229pub struct AnimationDriver {
231 active_animations: Cell<bool>,
233 global_instant: core::pin::Pin<Box<crate::Property<Instant>>>,
234}
235
236impl Default for AnimationDriver {
237 fn default() -> Self {
238 AnimationDriver {
239 active_animations: Cell::default(),
240 global_instant: Box::pin(crate::Property::new_named(
241 Instant::default(),
242 "i_slint_core::AnimationDriver::global_instant",
243 )),
244 }
245 }
246}
247
248impl AnimationDriver {
249 pub fn update_animations(&self, new_tick: Instant) {
252 let current_tick = self.global_instant.as_ref().get_untracked();
253 assert!(current_tick <= new_tick, "The platform's clock is not monotonic!");
254 if current_tick != new_tick {
255 self.active_animations.set(false);
256 self.global_instant.as_ref().set(new_tick);
257 }
258 }
259
260 pub fn has_active_animations(&self) -> bool {
263 self.active_animations.get()
264 }
265
266 pub fn set_has_active_animations(&self) {
268 self.active_animations.set(true);
269 }
270 pub fn current_tick(&self) -> Instant {
273 self.global_instant.as_ref().get()
274 }
275}
276
277crate::thread_local!(
278pub static CURRENT_ANIMATION_DRIVER : AnimationDriver = AnimationDriver::default()
281);
282
283pub fn current_tick() -> Instant {
286 CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick())
287}
288
289pub fn animation_tick() -> u64 {
292 CURRENT_ANIMATION_DRIVER.with(|driver| {
293 driver.set_has_active_animations();
294 driver.current_tick().0
295 })
296}
297
298fn ease_out_bounce_curve(value: f32) -> f32 {
299 const N1: f32 = 7.5625;
300 const D1: f32 = 2.75;
301
302 if value < 1.0 / D1 {
303 N1 * value * value
304 } else if value < 2.0 / D1 {
305 let value = value - (1.5 / D1);
306 N1 * value * value + 0.75
307 } else if value < 2.5 / D1 {
308 let value = value - (2.25 / D1);
309 N1 * value * value + 0.9375
310 } else {
311 let value = value - (2.625 / D1);
312 N1 * value * value + 0.984375
313 }
314}
315
316pub fn easing_curve(curve: &EasingCurve, value: f32) -> f32 {
318 match curve {
319 EasingCurve::Linear => value,
320 EasingCurve::CubicBezier([a, b, c, d]) => {
321 if !(0.0..=1.0).contains(a) && !(0.0..=1.0).contains(c) {
322 return value;
323 };
324 let curve = cubic_bezier::CubicBezierSegment {
325 from: (0., 0.).into(),
326 ctrl1: (*a, *b).into(),
327 ctrl2: (*c, *d).into(),
328 to: (1., 1.).into(),
329 };
330 curve.y(curve.solve_t_for_x(value, 0.0..1.0, 0.01))
331 }
332 EasingCurve::EaseInElastic => {
333 const C4: f32 = 2.0 * core::f32::consts::PI / 3.0;
334
335 if value == 0.0 {
336 0.0
337 } else if value == 1.0 {
338 1.0
339 } else {
340 -f32::powf(2.0, 10.0 * value - 10.0) * f32::sin((value * 10.0 - 10.75) * C4)
341 }
342 }
343 EasingCurve::EaseOutElastic => {
344 let c4 = (2.0 * core::f32::consts::PI) / 3.0;
345
346 if value == 0.0 {
347 0.0
348 } else if value == 1.0 {
349 1.0
350 } else {
351 2.0f32.powf(-10.0 * value) * ((value * 10.0 - 0.75) * c4).sin() + 1.0
352 }
353 }
354 EasingCurve::EaseInOutElastic => {
355 const C5: f32 = 2.0 * core::f32::consts::PI / 4.5;
356
357 if value == 0.0 {
358 0.0
359 } else if value == 1.0 {
360 1.0
361 } else if value < 0.5 {
362 -(f32::powf(2.0, 20.0 * value - 10.0) * f32::sin((20.0 * value - 11.125) * C5))
363 / 2.0
364 } else {
365 (f32::powf(2.0, -20.0 * value + 10.0) * f32::sin((20.0 * value - 11.125) * C5))
366 / 2.0
367 + 1.0
368 }
369 }
370 EasingCurve::EaseInBounce => 1.0 - ease_out_bounce_curve(1.0 - value),
371 EasingCurve::EaseOutBounce => ease_out_bounce_curve(value),
372 EasingCurve::EaseInOutBounce => {
373 if value < 0.5 {
374 (1.0 - ease_out_bounce_curve(1.0 - 2.0 * value)) / 2.0
375 } else {
376 (1.0 + ease_out_bounce_curve(2.0 * value - 1.0)) / 2.0
377 }
378 }
379 }
380}
381
382pub fn update_animations() {
415 CURRENT_ANIMATION_DRIVER.with(|driver| {
416 #[allow(unused_mut)]
417 let mut duration = Instant::duration_since_start().as_millis() as u64;
418 #[cfg(feature = "std")]
419 if let Ok(val) = std::env::var("SLINT_SLOW_ANIMATIONS") {
420 let factor = val.parse().unwrap_or(2);
421 duration /= factor;
422 };
423 driver.update_animations(Instant(duration))
424 });
425}