tween_scroll/
tween_scroll.rs

1// Example: adapter-driven tween scrolling (retarget on interruption).
2use virtualizer::{Align, Virtualizer, VirtualizerOptions};
3
4#[derive(Clone, Copy, Debug)]
5struct Tween {
6    from: u64,
7    to: u64,
8    start_ms: u64,
9    duration_ms: u64,
10}
11
12impl Tween {
13    fn new(from: u64, to: u64, start_ms: u64, duration_ms: u64) -> Self {
14        Self {
15            from,
16            to,
17            start_ms,
18            duration_ms: duration_ms.max(1),
19        }
20    }
21
22    fn is_done(&self, now_ms: u64) -> bool {
23        now_ms.saturating_sub(self.start_ms) >= self.duration_ms
24    }
25
26    fn sample(&self, now_ms: u64) -> u64 {
27        let elapsed = now_ms.saturating_sub(self.start_ms);
28        let t = (elapsed as f32 / self.duration_ms as f32).clamp(0.0, 1.0);
29        let eased = smoothstep(t);
30
31        let from = self.from as f32;
32        let to = self.to as f32;
33        let v = from + (to - from) * eased;
34        v.max(0.0) as u64
35    }
36
37    fn retarget(&mut self, now_ms: u64, new_to: u64, duration_ms: u64) {
38        let cur = self.sample(now_ms);
39        *self = Self::new(cur, new_to, now_ms, duration_ms);
40    }
41}
42
43fn smoothstep(t: f32) -> f32 {
44    t * t * (3.0 - 2.0 * t)
45}
46
47fn main() {
48    // Simulate an adapter that drives scroll_offset with a tween.
49    let mut v = Virtualizer::new(VirtualizerOptions::new(10_000, |_| 1));
50    v.set_viewport_and_scroll_clamped(20, 0);
51
52    let to = v.scroll_to_index_offset(2_000, Align::Center);
53    let mut tween = Tween::new(v.scroll_offset(), to, 0, 240);
54
55    let mut now_ms = 0u64;
56    let mut frame = 0u64;
57
58    loop {
59        // Simulate a 60fps "tick".
60        now_ms = now_ms.saturating_add(16);
61        frame += 1;
62
63        // Adapter samples tween and writes it into the virtualizer.
64        let off = tween.sample(now_ms);
65        v.apply_scroll_offset_event_clamped(off, now_ms);
66
67        // Render: a UI would iterate items and draw them.
68        let mut first: Option<usize> = None;
69        let mut last: Option<usize> = None;
70        v.for_each_virtual_item(|it| {
71            first.get_or_insert(it.index);
72            last = Some(it.index);
73        });
74
75        if frame.is_multiple_of(5) {
76            println!(
77                "t={now_ms}ms off={} visible={:?} first={:?} last={:?}",
78                v.scroll_offset(),
79                v.visible_range(),
80                first,
81                last
82            );
83        }
84
85        // Simulate user input interrupting the animation:
86        // at ~120ms, retarget to a new index.
87        if (120..120 + 16).contains(&now_ms) {
88            let new_to = v.scroll_to_index_offset(7_500, Align::Start);
89            tween.retarget(now_ms, new_to, 300);
90        }
91
92        if tween.is_done(now_ms) {
93            break;
94        }
95    }
96
97    // Mark scroll as finished for adapters that use this state.
98    v.set_is_scrolling(false);
99
100    println!(
101        "done: off={} range={:?}",
102        v.scroll_offset(),
103        v.virtual_range()
104    );
105}