Class: Potty::Animator
- Inherits:
-
Widgets::Base
- Object
- Widgets::Base
- Potty::Animator
- Defined in:
- lib/potty/animator.rb
Overview
Frame-based animation widget. Holds one or more named Sprites and advances the active one at its fps on each tick. Being a Widget, it composes into a View tree like anything else.
Playback is time-driven: tick(now) advances the frame only when enough wall-clock time has elapsed for the active sprite’s fps. The Application event loop supplies ‘now`; tests can supply it too, which makes the whole thing deterministic.
Instance Attribute Summary collapse
-
#centered ⇒ Object
Returns the value of attribute centered.
-
#color ⇒ Object
Returns the value of attribute color.
-
#current ⇒ Object
readonly
Returns the value of attribute current.
-
#frame_index ⇒ Object
readonly
Returns the value of attribute frame_index.
-
#on_complete ⇒ Object
Returns the value of attribute on_complete.
Attributes inherited from Widgets::Base
#app, #focused, #parent, #rect
Instance Method Summary collapse
-
#add_sprite(sprite) ⇒ Object
(also: #<<)
Register a sprite.
-
#initialize(app, color: :normal, centered: false) ⇒ Animator
constructor
A new instance of Animator.
-
#play(name, reset: true) ⇒ Object
Switch the active sprite and (re)start playback from frame 0.
- #playing? ⇒ Boolean
- #preferred_height(_width) ⇒ Object
- #render(window) ⇒ Object
- #resume ⇒ Object
- #sprite ⇒ Object
- #sprite_names ⇒ Object
- #stop ⇒ Object
- #tick(now) ⇒ Object
Methods inherited from Widgets::Base
#activate, #blur, #can_focus?, #deactivate, #focus, #handle_escape, #handle_key, #hide, #layout, #on_blur, #on_focus, #on_layout, #show, #theme, #visible=, #visible?
Methods included from Events
#emit, #listeners?, #off, #on
Constructor Details
#initialize(app, color: :normal, centered: false) ⇒ Animator
Returns a new instance of Animator.
19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/potty/animator.rb', line 19 def initialize(app, color: :normal, centered: false) super(app) @sprites = {} @current = nil # active sprite name (Symbol) @frame_index = 0 @last_advance = nil # Time of the last frame advance @playing = false @color = color @centered = centered @on_complete = nil # called with self when a :once sprite finishes end |
Instance Attribute Details
#centered ⇒ Object
Returns the value of attribute centered.
17 18 19 |
# File 'lib/potty/animator.rb', line 17 def centered @centered end |
#color ⇒ Object
Returns the value of attribute color.
17 18 19 |
# File 'lib/potty/animator.rb', line 17 def color @color end |
#current ⇒ Object (readonly)
Returns the value of attribute current.
16 17 18 |
# File 'lib/potty/animator.rb', line 16 def current @current end |
#frame_index ⇒ Object (readonly)
Returns the value of attribute frame_index.
16 17 18 |
# File 'lib/potty/animator.rb', line 16 def frame_index @frame_index end |
#on_complete ⇒ Object
Returns the value of attribute on_complete.
17 18 19 |
# File 'lib/potty/animator.rb', line 17 def on_complete @on_complete end |
Instance Method Details
#add_sprite(sprite) ⇒ Object Also known as: <<
Register a sprite. The first one added becomes active and starts playing.
32 33 34 35 36 |
# File 'lib/potty/animator.rb', line 32 def add_sprite(sprite) @sprites[sprite.name] = sprite play(sprite.name) if @current.nil? self end |
#play(name, reset: true) ⇒ Object
Switch the active sprite and (re)start playback from frame 0. Pass reset: false to keep the current frame index (e.g. crossfade).
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/potty/animator.rb', line 49 def play(name, reset: true) name = name.to_sym return self unless @sprites.key?(name) @current = name if reset @frame_index = 0 @last_advance = nil end @playing = true self end |
#playing? ⇒ Boolean
72 73 74 |
# File 'lib/potty/animator.rb', line 72 def @playing end |
#preferred_height(_width) ⇒ Object
76 77 78 |
# File 'lib/potty/animator.rb', line 76 def preferred_height(_width) sprite ? sprite.height : 0 end |
#render(window) ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/potty/animator.rb', line 94 def render(window) return unless @visible && @rect && sprite attr = theme[@color] sprite.frame_lines(@frame_index).each_with_index do |line, row| y = @rect.y + row break if y >= @rect.y + @rect.height x = @rect.x x += [(@rect.width - line.length) / 2, 0].max if @centered clipped = line[0, @rect.width] || '' window.setpos(y, x) window.attron(attr) { window.addstr(clipped) } end end |
#resume ⇒ Object
67 68 69 70 |
# File 'lib/potty/animator.rb', line 67 def resume @playing = true self end |
#sprite ⇒ Object
39 40 41 |
# File 'lib/potty/animator.rb', line 39 def sprite @sprites[@current] end |
#sprite_names ⇒ Object
43 44 45 |
# File 'lib/potty/animator.rb', line 43 def sprite_names @sprites.keys end |
#stop ⇒ Object
62 63 64 65 |
# File 'lib/potty/animator.rb', line 62 def stop @playing = false self end |
#tick(now) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/potty/animator.rb', line 80 def tick(now) return unless @playing && sprite @last_advance ||= now frame_duration = 1.0 / sprite.fps elapsed = now - @last_advance return if elapsed < frame_duration # Catch up if the loop ran slow, but never overshoot a :once endpoint. steps = (elapsed / frame_duration).floor @last_advance += steps * frame_duration advance(steps) end |