Class: Potty::Animator

Inherits:
Widgets::Base show all
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

Attributes inherited from Widgets::Base

#app, #focused, #parent, #rect

Instance Method Summary collapse

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

#centeredObject

Returns the value of attribute centered.



17
18
19
# File 'lib/potty/animator.rb', line 17

def centered
  @centered
end

#colorObject

Returns the value of attribute color.



17
18
19
# File 'lib/potty/animator.rb', line 17

def color
  @color
end

#currentObject (readonly)

Returns the value of attribute current.



16
17
18
# File 'lib/potty/animator.rb', line 16

def current
  @current
end

#frame_indexObject (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_completeObject

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

Returns:

  • (Boolean)


72
73
74
# File 'lib/potty/animator.rb', line 72

def playing?
  @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

#resumeObject



67
68
69
70
# File 'lib/potty/animator.rb', line 67

def resume
  @playing = true
  self
end

#spriteObject



39
40
41
# File 'lib/potty/animator.rb', line 39

def sprite
  @sprites[@current]
end

#sprite_namesObject



43
44
45
# File 'lib/potty/animator.rb', line 43

def sprite_names
  @sprites.keys
end

#stopObject



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