///|
pub(all) struct Frame {
time_ms : Double
delta_ms : Double
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Clock {
mut last_time_ms : Double?
} derive(Eq, Debug, ToJson)
///|
pub(all) enum AnimationStatus {
Idle
Running
Completed
} derive(Eq, Debug, ToJson)
///|
pub(all) enum Easing {
Linear
EaseIn
EaseOut
EaseInOut
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Tween {
from : Double
to : Double
easing : Easing
} derive(Eq, Debug, ToJson)
///|
pub(all) struct AnimatedDouble {
from : Double
to : Double
duration_ms : Double
easing : Easing
} derive(Eq, Debug, ToJson)
///|
pub(all) struct AnimatedPoint {
from : Point
to : Point
duration_ms : Double
easing : Easing
} derive(Eq, Debug, ToJson)
///|
pub(all) struct AnimatedColor {
from : Color
to : Color
duration_ms : Double
easing : Easing
} derive(Eq, Debug, ToJson)
///|
pub(all) struct AnimationController {
duration_ms : Double
mut elapsed_ms : Double
mut status : AnimationStatus
} derive(Eq, Debug, ToJson)
///|
pub fn Frame::new(time_ms~ : Double, delta_ms~ : Double) -> Frame {
{ time_ms, delta_ms }
}
///|
pub fn Clock::new() -> Clock {
{ last_time_ms: None }
}
///|
pub fn Clock::frame(self : Clock, time_ms : Double) -> Frame {
let delta = match self.last_time_ms {
Some(last) => max_double(0.0, time_ms - last)
None => 0.0
}
self.last_time_ms = Some(time_ms)
Frame::new(time_ms~, delta_ms=delta)
}
///|
pub fn Easing::sample(self : Easing, progress : Double) -> Double {
let t = clamp_double(progress, 0.0, 1.0)
match self {
Linear => t
EaseIn => t * t
EaseOut => 1.0 - (1.0 - t) * (1.0 - t)
EaseInOut =>
if t < 0.5 {
2.0 * t * t
} else {
1.0 - (-2.0 * t + 2.0) * (-2.0 * t + 2.0) / 2.0
}
}
}
///|
pub fn Tween::new(
from~ : Double,
to~ : Double,
easing? : Easing = Easing::Linear,
) -> Tween {
{ from, to, easing }
}
///|
pub fn Tween::value(self : Tween, progress : Double) -> Double {
self.from + (self.to - self.from) * self.easing.sample(progress)
}
///|
pub fn AnimatedDouble::new(
from~ : Double,
to~ : Double,
duration_ms? : Double = 180.0,
easing? : Easing = Easing::EaseInOut,
) -> AnimatedDouble {
{ from, to, duration_ms: max_double(0.0, duration_ms), easing }
}
///|
pub fn AnimatedDouble::value(
self : AnimatedDouble,
elapsed_ms : Double,
) -> Double {
Tween::new(from=self.from, to=self.to, easing=self.easing).value(
animation_progress(elapsed_ms, self.duration_ms),
)
}
///|
pub fn AnimatedPoint::new(
from~ : Point,
to~ : Point,
duration_ms? : Double = 180.0,
easing? : Easing = Easing::EaseInOut,
) -> AnimatedPoint {
{ from, to, duration_ms: max_double(0.0, duration_ms), easing }
}
///|
pub fn AnimatedPoint::value(self : AnimatedPoint, elapsed_ms : Double) -> Point {
let progress = self.easing.sample(
animation_progress(elapsed_ms, self.duration_ms),
)
Point::new(
x=self.from.x + (self.to.x - self.from.x) * progress,
y=self.from.y + (self.to.y - self.from.y) * progress,
)
}
///|
pub fn AnimatedColor::new(
from~ : Color,
to~ : Color,
duration_ms? : Double = 180.0,
easing? : Easing = Easing::EaseInOut,
) -> AnimatedColor {
{ from, to, duration_ms: max_double(0.0, duration_ms), easing }
}
///|
pub fn AnimatedColor::value(self : AnimatedColor, elapsed_ms : Double) -> Color {
let progress = self.easing.sample(
animation_progress(elapsed_ms, self.duration_ms),
)
Color::rgba(
r=self.from.r + (self.to.r - self.from.r) * progress,
g=self.from.g + (self.to.g - self.from.g) * progress,
b=self.from.b + (self.to.b - self.from.b) * progress,
a=self.from.a + (self.to.a - self.from.a) * progress,
)
}
///|
fn animation_progress(elapsed_ms : Double, duration_ms : Double) -> Double {
if duration_ms <= 0.0 {
1.0
} else {
clamp_double(elapsed_ms / duration_ms, 0.0, 1.0)
}
}
///|
pub fn AnimationController::new(duration_ms~ : Double) -> AnimationController {
{ duration_ms: max_double(0.0, duration_ms), elapsed_ms: 0.0, status: Idle }
}
///|
pub fn AnimationController::start(self : AnimationController) -> Unit {
self.elapsed_ms = 0.0
self.status = if self.duration_ms <= 0.0 { Completed } else { Running }
}
///|
pub fn AnimationController::stop(self : AnimationController) -> Unit {
self.status = Idle
}
///|
pub fn AnimationController::reset(self : AnimationController) -> Unit {
self.elapsed_ms = 0.0
self.status = Idle
}
///|
pub fn AnimationController::tick(
self : AnimationController,
frame : Frame,
) -> Bool {
if self.status != Running {
false
} else {
self.elapsed_ms = min_double(
self.duration_ms,
self.elapsed_ms + max_double(0.0, frame.delta_ms),
)
if self.elapsed_ms >= self.duration_ms {
self.status = Completed
}
true
}
}
///|
pub fn AnimationController::progress(self : AnimationController) -> Double {
if self.duration_ms <= 0.0 {
1.0
} else {
clamp_double(self.elapsed_ms / self.duration_ms, 0.0, 1.0)
}
}
///|
pub fn AnimationController::is_active(self : AnimationController) -> Bool {
self.status == Running
}