QmlMaterial 0.1.0
Loading...
Searching...
No Matches
scroll_motion.hpp
1#pragma once
2
3#include <algorithm>
4#include <cmath>
5
6namespace qml_material
7{
8
9class ScrollMotion {
10public:
11 enum class Mode
12 {
13 Idle,
14 Fling,
15 Target
16 };
17 struct Sample {
18 double position;
19 double velocity;
20 bool finished;
21 };
22
23 Mode mode() const { return m_mode; }
24 bool active() const { return m_mode != Mode::Idle; }
25 double target() const { return m_target; }
26 unsigned long long revision() const { return m_revision; }
27
28 void stop() {
29 ++m_revision;
30 m_mode = Mode::Idle;
31 }
32
33 void fling(double position, double velocity, double deceleration, double now) {
34 ++m_revision;
35 m_mode = Mode::Fling;
36 m_start = position;
37 m_velocity = velocity;
38 m_rate = std::max(1.0, deceleration);
39 m_time = now;
40 m_target = position + velocity * std::abs(velocity) / (2 * m_rate);
41 }
42
43 void smooth(double position, double velocity, double target, double now) {
44 ++m_revision;
45 m_mode = Mode::Target;
46 m_start = position;
47 m_velocity = velocity;
48 m_target = target;
49 m_time = now;
50 m_rate = 18;
51 }
52
53 void translate(double delta) {
54 ++m_revision;
55 m_start += delta;
56 m_target += delta;
57 }
58
59 Sample sample(double now) const {
60 const double elapsed = std::max(0.0, now - m_time);
61 if (m_mode == Mode::Idle) return { m_start, 0, true };
62 if (m_mode == Mode::Fling) {
63 const double duration = std::abs(m_velocity) / m_rate;
64 if (elapsed >= duration) return { m_target, 0, true };
65 const double acceleration = -std::copysign(m_rate, m_velocity);
66 return { m_start + m_velocity * elapsed + acceleration * elapsed * elapsed / 2,
67 m_velocity + acceleration * elapsed,
68 false };
69 }
70 const double distance = m_start - m_target;
71 const double b = m_velocity + m_rate * distance;
72 const double decay = std::exp(-m_rate * elapsed);
73 const double offset = (distance + b * elapsed) * decay;
74 const double velocity = (b - m_rate * (distance + b * elapsed)) * decay;
75 if (std::abs(offset) < 0.01 && std::abs(velocity) < 0.1) return { m_target, 0, true };
76 return { m_target + offset, velocity, false };
77 }
78
79private:
80 unsigned long long m_revision { 0 };
81 Mode m_mode { Mode::Idle };
82 double m_start { 0 };
83 double m_velocity { 0 };
84 double m_target { 0 };
85 double m_time { 0 };
86 double m_rate { 1 };
87};
88
89} // namespace qml_material