Class: Plushie::Animation::Tween
- Inherits:
-
Object
- Object
- Plushie::Animation::Tween
- Defined in:
- lib/plushie/animation/tween.rb
Overview
SDK-side animation interpolation.
Pure functions operating on structs for manual, host-side animations. The host computes interpolated values on each animation frame tick.
For most use cases, prefer renderer-side descriptors (Transition, Spring, Sequence) which animate with zero wire traffic. Use Tween when you need host-side control over the interpolated value (e.g., driving non-visual state from an animation).
== 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
== Animation struct
The Tween 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
Instance Attribute Summary collapse
-
#auto_reverse [Boolean] swap from/to on each cycle([Boolean]) ⇒ Object
readonly
Immutable animation state.
-
#repeat [Integer, :forever, nil] remaining repeat count([Integer,: forever, nil]) ⇒ Object
readonly
Immutable animation state.
Class Method Summary collapse
-
.advance(anim, timestamp) ⇒ Array(Numeric, State), Array(Numeric, Symbol)
Advance the animation to the given frame timestamp.
-
.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.
-
.looping(from, to, duration_ms, **opts) ⇒ State
Create a looping tween that repeats forever with auto-reverse.
-
.new(from, to, duration_ms, easing: :linear, repeat: nil, auto_reverse: false) ⇒ State
Create a new animation.
-
.start(anim, timestamp) ⇒ State
Start (or restart) the animation at the given frame timestamp.
-
.value(anim) ⇒ Numeric
Return the current interpolated value.
Instance Attribute Details
#auto_reverse [Boolean] swap from/to on each cycle([Boolean]) ⇒ Object (readonly)
Immutable animation state.
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/plushie/animation/tween.rb', line 45 State = ::Data.define(:from, :to, :duration, :started_at, :easing, :value, :repeat, :auto_reverse) do include Plushie::Model::Extensions def initialize(from:, to:, duration:, started_at: nil, easing: :linear, value: nil, repeat: nil, auto_reverse: false) super(from: from, to: to, duration: duration, started_at: started_at, easing: easing, value: value.nil? ? from : value, repeat: repeat, auto_reverse: auto_reverse) end end |
#repeat [Integer, :forever, nil] remaining repeat count([Integer,: forever, nil]) ⇒ Object (readonly)
Immutable animation state.
45 46 47 48 49 50 51 52 53 54 |
# File 'lib/plushie/animation/tween.rb', line 45 State = ::Data.define(:from, :to, :duration, :started_at, :easing, :value, :repeat, :auto_reverse) do include Plushie::Model::Extensions def initialize(from:, to:, duration:, started_at: nil, easing: :linear, value: nil, repeat: nil, auto_reverse: false) super(from: from, to: to, duration: duration, started_at: started_at, easing: easing, value: value.nil? ? from : value, repeat: repeat, auto_reverse: auto_reverse) end end |
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.
179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/plushie/animation/tween.rb', line 179 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 handle_cycle_end(anim) else [current, anim.with(value: current)] end end |
.finished?(anim) ⇒ Boolean
Returns true if the animation has run to completion.
197 198 199 200 |
# File 'lib/plushie/animation/tween.rb', line 197 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.
116 117 118 119 120 121 |
# File 'lib/plushie/animation/tween.rb', line 116 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 |
.looping(from, to, duration_ms, **opts) ⇒ State
Create a looping tween that repeats forever with auto-reverse.
Convenience for common back-and-forth animations (pulsing, breathing, oscillating).
158 159 160 |
# File 'lib/plushie/animation/tween.rb', line 158 def self.looping(from, to, duration_ms, **opts) new(from, to, duration_ms, repeat: :forever, auto_reverse: true, **opts) end |
.new(from, to, duration_ms, easing: :linear, repeat: nil, auto_reverse: false) ⇒ State
Create a new animation.
132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/plushie/animation/tween.rb', line 132 def self.new(from, to, duration_ms, easing: :linear, repeat: nil, auto_reverse: false) 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, easing: easing, repeat: repeat, auto_reverse: auto_reverse ) end |
.start(anim, timestamp) ⇒ State
Start (or restart) the animation at the given frame timestamp.
167 168 169 |
# File 'lib/plushie/animation/tween.rb', line 167 def self.start(anim, ) anim.with(started_at: , value: anim.from) end |
.value(anim) ⇒ Numeric
Return the current interpolated value.
205 |
# File 'lib/plushie/animation/tween.rb', line 205 def self.value(anim) = anim.value |