Animations¶
Run a frame callback at a fixed rate, use the four built-in effects, and draw button feedback with ButtonUI.
Snippets on this page are compiled from tests/docs/graphics.cpp.
The animator¶
Animator(gfx, frame_fn, user_data, fps) calls frame_fn(gfx, frame, user_data) each time a frame is due. It
keeps no clock of its own: you tell it how much time has passed.
void run_plasma(Display& display, uint32_t (*millis)()) {
auto gfx = display.CreateGraphicsContext();
isf15acp4::Animator anim(gfx, isf15acp4::animations::Plasma, nullptr, 25); // 25 FPS: 40 ms per frame
uint32_t last = millis();
for (;;) {
const uint32_t now = millis();
if (anim.Tick(now - last)) { // renders when a frame is due
display.Present(gfx);
}
last = now;
}
}
| Member | Does |
|---|---|
Tick(elapsed_ms) |
Adds the time; when at least one frame period has built up, renders one frame and returns true |
RenderNow() |
Renders the next frame at once |
SetFps(fps) |
Frame period 1000 / fps ms (33 ms when fps is 0) |
Frame(), Reset() |
The frame counter; Reset() sets it and the time to zero |
Tick() renders at most one frame per call and keeps any extra time, so after a late call the next calls catch
up one frame each; no frame number is skipped. The animator only draws: you call Present() when it returns
true.
Built-in effects¶
| Function | Draws |
|---|---|
ColorWheel |
A rotating colour wheel, with a label |
Plasma |
A moving sine-interference pattern |
ScrollingText |
A title and a line of text moving left |
PulseButton |
A frame and a growing bar, green when pressed |
They are in isf15acp4::animations. ColorWheel and Plasma ignore user_data. ScrollingText takes an
animations::ScrollTextState* (its text; null shows "ISF15ACP4"), PulseButton an
animations::PulseButtonState* (its pressed; null is not pressed).
ColorWheel and Plasma compute every pixel with floating-point sine functions: on an MCU without an FPU that
can take longer than the frame period.
Your own effect¶
A frame function is a plain noexcept function. Pass your state through user_data:
struct Bar {
uint8_t level; // 0..95, updated by your code
};
void DrawBar(isf15acp4::GraphicsContext& gfx, uint32_t frame, void* user_data) noexcept {
const auto* bar = static_cast<const Bar*>(user_data);
gfx.Clear(colors::Black);
gfx.FillRect({0, 28, bar->level, 36}, (frame / 15) % 2 ? colors::Green : colors::Yellow);
}
void start_bar(isf15acp4::GraphicsContext& gfx, Bar& bar) {
isf15acp4::Animator anim(gfx, &DrawBar, &bar, 30);
anim.RenderNow(); // first frame without waiting
}
The animator keeps a reference to the context, so the context must outlive it.
Button feedback¶
ButtonUI debounces the switch (API guide) and draws two kinds of feedback:
isf15acp4::ButtonUI ui;
ui.DrawStatus(gfx, "START", display.IsPressed()); // title bar, state text, frame
isf15acp4::ButtonUI::DrawGauge(gfx, 48, 40, 20, load, // half-circle gauge, load in 0..1
colors::Green, colors::Gray);
display.Present(gfx);
| Call | Draws |
|---|---|
DrawStatus() |
A full status screen: title bar with the label, "PRESSED" or "READY", a border |
DrawGauge() |
A half disc below its centre, the first value (0 to 1) of it in fg |
DrawStatus() clears the frame to dark green when active, black otherwise. DrawGauge() is static.
The examples combine these: isf15acp4_animation_demo cycles the four effects on each press, and
isf15acp4_button_dashboard draws a gauge driven by the switch.