Skip to main content

wisp_storybook/stories/
s_graphics_ellipse.rs

1//! Story: animated click-ripple — outlined ellipse animated by `tick` (M0.13).
2
3use glam::Vec2;
4use wisp::application::Application;
5use wisp::{Color, Fill, Graphics, Stage, Stroke};
6
7use crate::story::Story;
8
9pub fn story() -> Story {
10    Story {
11        id: "graphics-ellipse",
12        category: "Graphics",
13        title: "Animated click ripple",
14        milestone: "M0.13",
15        writeup: include_str!("writeups/graphics_ellipse.md"),
16        build,
17        tick: Some(tick),
18    }
19}
20
21fn build(_app: &Application, stage: &mut Stage) {
22    let g = Graphics::new();
23    let _ = stage.add_child(stage.root(), g);
24}
25
26fn tick(stage: &mut Stage, t: f32) {
27    let root_id = stage.root();
28    let child_ids: Vec<_> = stage
29        .get(root_id)
30        .map(|n| n.container().children().collect())
31        .unwrap_or_default();
32    let Some(graphics_id) = child_ids.first().copied() else {
33        return;
34    };
35    let Some(node) = stage.get_mut(graphics_id) else {
36        return;
37    };
38    let wisp::Node::Graphics(g) = node else {
39        return;
40    };
41
42    *g = Graphics::new();
43
44    // Three ripples staggered in time — like multiple clicks.
45    for i in 0u8..3 {
46        let i_f = f32::from(i);
47        let offset = i_f * 0.6;
48        let phase = (t - offset).max(0.0) % 2.5;
49        let progress = (phase / 2.0).min(1.0);
50        let alpha = (1.0 - progress).max(0.0);
51        let radius = 0.15 + progress * 0.6;
52        let x = -0.6 + i_f * 0.6;
53
54        g.fill(Fill::Solid(Color::rgba(1.0, 1.0, 1.0, alpha * 0.25)));
55        g.stroke(Some(Stroke::new(
56            0.025,
57            Color::rgba(1.0, 1.0, 1.0, alpha * 0.9),
58        )));
59        g.draw_ellipse(Vec2::new(x, 0.0), Vec2::splat(radius));
60    }
61}