Class: Plushie::Animation
- Inherits:
-
Object
- Object
- Plushie::Animation
- Defined in:
- lib/plushie/animation.rb
Overview
Server-side animation interpolation and easing functions.
Pure functions operating on structs -- no threads, no state management beyond what lives in your app model. The host computes interpolated values on each animation frame tick.
== Easing functions
All easing functions take a +t+ value in 0.0..1.0 and return a curved +t+ value. Available easings:
- +linear+ -- identity
- +ease_in+ -- cubic ease in
- +ease_out+ -- cubic ease out
- +ease_in_out+ -- cubic ease in-out
- +ease_in_quad+ -- quadratic ease in
- +ease_out_quad+ -- quadratic ease out
- +ease_in_out_quad+ -- quadratic ease in-out
- +spring+ -- spring with overshoot
== Interpolation
+interpolate+ lerps between two numbers with an optional easing function applied to +t+.
== Animation struct
The Animation tracks a single animated value over time. Create one with +new+, start it with +start+, and advance it on each frame with +advance+.
Defined Under Namespace
Classes: State
Constant Summary collapse
- EASINGS =
-- Easing functions ---------------------------------------------------
{ linear: ->(t) { t }, ease_in: ->(t) { t * t * t }, ease_out: ->(t) { inv = 1.0 - t 1.0 - inv * inv * inv }, ease_in_out: ->(t) { if t < 0.5 4.0 * t * t * t else inv = -2.0 * t + 2.0 1.0 - inv * inv * inv / 2.0 end }, ease_in_quad: ->(t) { t * t }, ease_out_quad: ->(t) { 1.0 - (1.0 - t) * (1.0 - t) }, ease_in_out_quad: ->(t) { if t < 0.5 2.0 * t * t else 1.0 - (-2.0 * t + 2.0)**2 / 2.0 end }, spring: ->(t) { if t <= 0.0 0.0 elsif t >= 1.0 1.0 else c4 = 2.0 * Math::PI / 3.0 2.0**(-10.0 * t) * Math.sin((t * 10.0 - 0.75) * c4) + 1.0 end } }.freeze
Class Method Summary collapse
-
.advance(anim, timestamp) ⇒ Array(Numeric, State), Array(Numeric, Symbol)
Advance the animation to the given frame timestamp.
-
.ease_in(t) ⇒ Float
Cubic ease in.
-
.ease_in_out(t) ⇒ Float
Cubic ease in-out.
-
.ease_in_out_quad(t) ⇒ Float
Quadratic ease in-out.
-
.ease_in_quad(t) ⇒ Float
Quadratic ease in.
-
.ease_out(t) ⇒ Float
Cubic ease out.
-
.ease_out_quad(t) ⇒ Float
Quadratic ease out.
-
.finished?(anim) ⇒ Boolean
Returns true if the animation has run to completion.
-
.interpolate(from, to, t, easing = :linear) ⇒ Float
Linearly interpolate between +from+ and +to+ at progress +t+, with an optional easing function applied to +t+ first.
-
.linear(t) ⇒ Float
Linear easing (identity).
-
.new(from, to, duration_ms, easing: :linear) ⇒ State
Create a new animation.
-
.spring(t) ⇒ Float
Spring easing with overshoot.
-
.start(anim, timestamp) ⇒ State
Start (or restart) the animation at the given frame timestamp.
-
.value(anim) ⇒ Numeric
Return the current interpolated value.
Class Method Details
.advance(anim, timestamp) ⇒ Array(Numeric, State), Array(Numeric, Symbol)
Advance the animation to the given frame timestamp.
Returns +[current_value, updated_animation]+ while the animation is in progress, or +[final_value, :finished]+ when it completes.
If the animation has not been started yet, returns +[from, animation]+ unchanged.
194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/plushie/animation.rb', line 194 def self.advance(anim, ) return [anim.value, anim] if anim.started_at.nil? elapsed = - anim.started_at t = clamp(elapsed.to_f / anim.duration) current = interpolate(anim.from, anim.to, t, anim.easing) if t >= 1.0 [anim.to, :finished] else [current, anim.with(value: current)] end end |
.ease_in(t) ⇒ Float
Cubic ease in. Starts slow, accelerates.
99 |
# File 'lib/plushie/animation.rb', line 99 def self.ease_in(t) = EASINGS[:ease_in].call(t) |
.ease_in_out(t) ⇒ Float
Cubic ease in-out. Slow start, fast middle, slow end.
109 |
# File 'lib/plushie/animation.rb', line 109 def self.ease_in_out(t) = EASINGS[:ease_in_out].call(t) |
.ease_in_out_quad(t) ⇒ Float
Quadratic ease in-out. Slow start and end, fast middle.
124 |
# File 'lib/plushie/animation.rb', line 124 def self.ease_in_out_quad(t) = EASINGS[:ease_in_out_quad].call(t) |
.ease_in_quad(t) ⇒ Float
Quadratic ease in. Starts slow, accelerates.
114 |
# File 'lib/plushie/animation.rb', line 114 def self.ease_in_quad(t) = EASINGS[:ease_in_quad].call(t) |
.ease_out(t) ⇒ Float
Cubic ease out. Starts fast, decelerates.
104 |
# File 'lib/plushie/animation.rb', line 104 def self.ease_out(t) = EASINGS[:ease_out].call(t) |
.ease_out_quad(t) ⇒ Float
Quadratic ease out. Starts fast, decelerates.
119 |
# File 'lib/plushie/animation.rb', line 119 def self.ease_out_quad(t) = EASINGS[:ease_out_quad].call(t) |
.finished?(anim) ⇒ Boolean
Returns true if the animation has run to completion.
Note: once +advance+ returns +[value, :finished]+, the animation struct is no longer updated. Use the +:finished+ return value from +advance+ as the primary completion signal.
216 217 218 219 |
# File 'lib/plushie/animation.rb', line 216 def self.finished?(anim) return false if anim.started_at.nil? anim.value == anim.to end |
.interpolate(from, to, t, easing = :linear) ⇒ Float
Linearly interpolate between +from+ and +to+ at progress +t+, with an optional easing function applied to +t+ first.
+t+ is clamped to 0.0..1.0 before easing is applied.
144 145 146 147 148 149 |
# File 'lib/plushie/animation.rb', line 144 def self.interpolate(from, to, t, easing = :linear) easing_fn = resolve_easing(easing) clamped = clamp(t) eased = easing_fn.call(clamped) from + (to - from) * eased end |
.linear(t) ⇒ Float
Linear easing (identity). Returns +t+ unchanged.
94 |
# File 'lib/plushie/animation.rb', line 94 def self.linear(t) = EASINGS[:linear].call(t) |
.new(from, to, duration_ms, easing: :linear) ⇒ State
Create a new animation.
160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/plushie/animation.rb', line 160 def self.new(from, to, duration_ms, easing: :linear) raise ArgumentError, "duration_ms must be positive" unless duration_ms.is_a?(Integer) && duration_ms > 0 State.new( from: from, to: to, duration: duration_ms, started_at: nil, easing: easing, value: from ) end |
.spring(t) ⇒ Float
Spring easing with overshoot. Overshoots the target slightly before settling. Uses a single-period damped sine approximation.
130 |
# File 'lib/plushie/animation.rb', line 130 def self.spring(t) = EASINGS[:spring].call(t) |
.start(anim, timestamp) ⇒ State
Start (or restart) the animation at the given frame timestamp. Resets the current value to +from+.
179 180 181 |
# File 'lib/plushie/animation.rb', line 179 def self.start(anim, ) anim.with(started_at: , value: anim.from) end |
.value(anim) ⇒ Numeric
Return the current interpolated value.
224 |
# File 'lib/plushie/animation.rb', line 224 def self.value(anim) = anim.value |