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