aboutsummaryrefslogtreecommitdiff
path: root/src/animation
diff options
context:
space:
mode:
Diffstat (limited to 'src/animation')
-rw-r--r--src/animation/mod.rs20
-rw-r--r--src/animation/spring.rs31
2 files changed, 51 insertions, 0 deletions
diff --git a/src/animation/mod.rs b/src/animation/mod.rs
index 073b0ae3..a68b3342 100644
--- a/src/animation/mod.rs
+++ b/src/animation/mod.rs
@@ -16,6 +16,8 @@ pub struct Animation {
from: f64,
to: f64,
duration: Duration,
+ /// Time until the animation first reaches `to`.
+ clamped_duration: Duration,
start_time: Duration,
current_time: Duration,
kind: Kind,
@@ -106,6 +108,8 @@ impl Animation {
from,
to,
duration,
+ // Our current curves never overshoot.
+ clamped_duration: duration,
start_time: now,
current_time: now,
kind,
@@ -118,12 +122,14 @@ impl Animation {
let now = get_monotonic_time();
let duration = spring.duration();
+ let clamped_duration = spring.clamped_duration();
let kind = Kind::Spring(spring);
Self {
from: spring.from,
to: spring.to,
duration,
+ clamped_duration,
start_time: now,
current_time: now,
kind,
@@ -159,6 +165,7 @@ impl Animation {
from,
to,
duration,
+ clamped_duration: duration,
start_time: now,
current_time: now,
kind,
@@ -228,6 +235,10 @@ impl Animation {
self.current_time >= self.start_time + self.duration
}
+ pub fn is_clamped_done(&self) -> bool {
+ self.current_time >= self.start_time + self.clamped_duration
+ }
+
pub fn value(&self) -> f64 {
if self.is_done() {
return self.to;
@@ -254,6 +265,15 @@ impl Animation {
}
}
+ /// Returns a value that stops at the target value after first reaching it.
+ pub fn clamped_value(&self) -> f64 {
+ if self.is_clamped_done() {
+ return self.to;
+ }
+
+ self.value()
+ }
+
pub fn to(&self) -> f64 {
self.to
}
diff --git a/src/animation/spring.rs b/src/animation/spring.rs
index 6e100f05..506b01a3 100644
--- a/src/animation/spring.rs
+++ b/src/animation/spring.rs
@@ -96,6 +96,37 @@ impl Spring {
Duration::from_secs_f64(x1)
}
+ /// Computes and returns the duration until the spring reaches its target position.
+ pub fn clamped_duration(&self) -> Duration {
+ let beta = self.params.damping / (2. * self.params.mass);
+
+ if beta.abs() <= f64::EPSILON || beta < 0. {
+ return Duration::MAX;
+ }
+
+ if (self.to - self.from).abs() <= f64::EPSILON {
+ return Duration::ZERO;
+ }
+
+ // The first frame is not that important and we avoid finding the trivial 0 for in-place
+ // animations.
+ let mut i = 1u16;
+ let mut y = self.oscillate(f64::from(i) / 1000.);
+
+ while (self.to - self.from > f64::EPSILON && self.to - y > self.params.epsilon)
+ || (self.from - self.to > f64::EPSILON && y - self.to > self.params.epsilon)
+ {
+ if i > 1000 {
+ return Duration::ZERO;
+ }
+
+ i += 1;
+ y = self.oscillate(f64::from(i) / 1000.);
+ }
+
+ Duration::from_millis(u64::from(i))
+ }
+
/// Returns the spring position at a given time in seconds.
fn oscillate(&self, t: f64) -> f64 {
let b = self.params.damping;