ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
animation.h
Go to the documentation of this file.
1#pragma once
3
4#ifdef USE_LVGL_ANIMATION
5#include "lvgl_esphome.h"
6#include "esphome/core/hal.h"
8
9namespace esphome::lvgl {
10
11enum class AnimationState {
12 STOPPED,
13 STARTED,
14 RUNNING,
15};
16
18 public:
19 // Map progress in the range [0, 1]
20 virtual float map_progress(float value) = 0;
21};
22
24 public:
25 // moving_length_ is the fraction of progress spent moving in each direction, in (0, 0.5].
26 // Callers must pass pause in [0, 1) -- pause == 1.0 would make moving_length_ zero and divide by zero below.
27 LvAnimationTimingRoundTrip(float pause) : moving_length_((1.0f - pause) / 2.0f) {}
28 float map_progress(float value) override {
29 if (value < this->moving_length_) {
30 return value / this->moving_length_;
31 }
32 if (value > 1.0f - this->moving_length_) {
33 return (1.0f - value) / this->moving_length_;
34 }
35 // pause in the middle
36 return 1.0f;
37 }
38
39 protected:
41};
42
44 public:
45 LvAnimationTimingGravity(float acceleration, float bounce) : acceleration_(acceleration), bounce_(bounce) {}
46 float map_progress(float value) override {
47 if (value == 0.0f) {
48 this->initial_position_ = 0.0f;
49 this->initial_speed_ = 0.0f;
50 this->initial_time_ = 0.0f;
51 }
52 auto position = this->calc_pos_(value);
53 if (position > 1.0f) {
54 auto initial_time = this->calc_end_time_();
55 this->initial_speed_ = -this->calc_speed_(initial_time) * this->bounce_;
56 this->initial_position_ = 1.0f;
57 this->initial_time_ = initial_time;
58 position = calc_pos_(value);
59 if (position > 1.0f) {
60 position = 1.0f;
61 }
62 }
63 return position;
64 }
65
66 protected:
67 float calc_pos_(float value) const {
68 value -= this->initial_time_;
69 return (0.5 * value * this->acceleration_ + this->initial_speed_) * value + this->initial_position_;
70 }
71
72 float calc_speed_(float value) const {
73 value -= this->initial_time_;
74 return this->acceleration_ * value + this->initial_speed_;
75 }
76
77 float calc_end_time_() const {
78 return (-this->initial_speed_ + std::sqrt(this->initial_speed_ * this->initial_speed_ -
79 4.0f * this->acceleration_ / 2.0 * (this->initial_position_ - 1.0f))) /
80 this->acceleration_ +
81 this->initial_time_;
82 }
83
85 float bounce_;
86 float initial_position_{0.0f};
87 float initial_time_{0.0f};
88 float initial_speed_{0.0f};
89};
90
92 public:
93 LvAnimationTimingEaseInOut(float slope) : slope_(slope) {}
94 float map_progress(float value) override {
95 float sqr = value * value;
96 sqr = sqr / (2.0f * (sqr - value) + 1.0f);
97 return this->slope_ * sqr + (1.0 - this->slope_) * value;
98 }
99
100 protected:
101 float slope_;
102};
103
105 public:
106 LvAnimationTimingEaseIn(float slope) : slope_(slope) {}
107 float map_progress(float value) override {
108 const float sqr = value * value;
109 return this->slope_ * sqr + (1.0f - this->slope_) * value;
110 }
111
112 protected:
113 float slope_;
114};
115
117 public:
118 LvAnimationTimingEaseOut(float slope) : slope_(slope) {}
119 float map_progress(float value) override {
120 const float inv = 1.0f - value;
121 const float sqr = 1.0f - inv * inv;
122 return this->slope_ * sqr + (1.0f - this->slope_) * value;
123 }
124
125 protected:
126 float slope_;
127};
128
129template<size_t DATA_SIZE, bool AUTO_START = false> class LvAnimation : public Component {
130 public:
131 LvAnimation(void (*update_callback)(const lv_coord_t *data), std::vector<TemplatableValue<lv_coord_t>> from,
132 std::vector<TemplatableValue<lv_coord_t>> to)
133 : update_callback_(update_callback) {
134 std::copy(from.begin(), from.end(), this->from_);
135 std::copy(to.begin(), to.end(), this->to_);
136 }
137
138 void start() {
139 if (!this->prepare_())
140 return;
141 this->loop();
142 this->start_callback_.call();
143 }
144
145 void stop() {
146 // Only fire the stop callback on a genuine running -> stopped transition, so that
147 // repeated stop() calls (e.g. start() pre-clearing a stopped animation) don't re-fire it.
148 if (this->state_ == AnimationState::STOPPED)
149 return;
151 this->stop_callback_.call();
152 }
153
154 void setup() override {
155 if constexpr (AUTO_START)
156 this->start();
157 }
158
159 void loop() override {
160 if (this->state_ == AnimationState::STOPPED)
161 return;
162 uint32_t elapsed = millis() - this->start_time_;
163 float progress = clamp_at_most(elapsed / static_cast<float>(this->duration_), 1.0f);
164 switch (this->state_) {
166 if (elapsed < this->start_delay_)
167 return;
169 this->start_time_ = millis();
170 elapsed = 0;
171 progress = 0.0f;
172 break;
174 break;
175 default:
176 return;
177 }
178
179 // state here is RUNNING.
180 for (auto *timing : this->timings_) {
181 // avoid overshooting
182 progress = clamp_at_most(timing->map_progress(progress), 1.0f);
183 }
184 lv_coord_t data[DATA_SIZE];
185 for (size_t i = 0; i != DATA_SIZE; i++) {
186 data[i] = static_cast<lv_coord_t>(
187 roundf(this->data_from_[i] + static_cast<lv_coord_t>(this->data_to_[i] - this->data_from_[i]) * progress));
188 }
189 this->update_callback_(data);
190 if (elapsed >= this->duration_) {
191 this->stop();
192 // Restart without drawing the first frame now, so the final frame is not overwritten before it is shown
193 if (this->loop_ && this->prepare_())
194 this->start_callback_.call();
195 }
196 }
197
198 float get_setup_priority() const override { return setup_priority::PROCESSOR - 20.0; }
200 void set_start_delay(uint32_t start_delay) { this->start_delay_ = start_delay; }
201 void add_timing(LvAnimationTiming *timing) { this->timings_.push_back(timing); }
202 void set_loop(bool loop) { this->loop_ = loop; }
203
204 template<typename F> void add_on_start_callback(F &&callback) {
205 this->start_callback_.add(std::forward<F>(callback));
206 }
207 template<typename F> void add_on_stop_callback(F &&callback) { this->stop_callback_.add(std::forward<F>(callback)); }
208
209 protected:
210 // Arms a new run. Returns false if there is nothing to run.
211 bool prepare_() {
213 this->stop();
214 if (this->duration_ == 0)
215 return false;
216 // evaluate any lambdas
217 for (size_t i = 0; i != DATA_SIZE; i++) {
218 this->data_from_[i] = this->from_[i].value();
219 this->data_to_[i] = this->to_[i].value();
220 }
221 this->start_time_ = millis();
223 return true;
224 }
225
226 void (*const update_callback_)(const lv_coord_t *data);
234 lv_coord_t data_from_[DATA_SIZE]{0};
235 lv_coord_t data_to_[DATA_SIZE]{0};
237 std::vector<LvAnimationTiming *> timings_{};
238 bool loop_{false};
239};
240
241} // namespace esphome::lvgl
242
243#endif // USE_LVGL_ANIMATION
Primary TemplatableValue: stores either a constant value or a function pointer.
Definition automation.h:160
T value(X... x) const
Definition automation.h:241
void add_timing(LvAnimationTiming *timing)
Definition animation.h:201
lv_coord_t data_to_[DATA_SIZE]
Definition animation.h:235
void(*const update_callback_)(const lv_coord_t *data)
Definition animation.h:226
void set_loop(bool loop)
Definition animation.h:202
LazyCallbackManager< void()> start_callback_
Definition animation.h:227
LazyCallbackManager< void()> stop_callback_
Definition animation.h:228
std::vector< LvAnimationTiming * > timings_
Definition animation.h:237
void set_duration(uint32_t duration)
Definition animation.h:199
void setup() override
Definition animation.h:154
TemplatableValue< lv_coord_t > to_[DATA_SIZE]
Definition animation.h:230
LvAnimation(void(*update_callback)(const lv_coord_t *data), std::vector< TemplatableValue< lv_coord_t > > from, std::vector< TemplatableValue< lv_coord_t > > to)
Definition animation.h:131
lv_coord_t data_from_[DATA_SIZE]
Definition animation.h:234
void set_start_delay(uint32_t start_delay)
Definition animation.h:200
float get_setup_priority() const override
Definition animation.h:198
void add_on_start_callback(F &&callback)
Definition animation.h:204
void add_on_stop_callback(F &&callback)
Definition animation.h:207
TemplatableValue< lv_coord_t > from_[DATA_SIZE]
Definition animation.h:229
float map_progress(float value) override
Definition animation.h:107
float map_progress(float value) override
Definition animation.h:94
float map_progress(float value) override
Definition animation.h:119
LvAnimationTimingGravity(float acceleration, float bounce)
Definition animation.h:45
float map_progress(float value) override
Definition animation.h:46
float calc_pos_(float value) const
Definition animation.h:67
float calc_speed_(float value) const
Definition animation.h:72
virtual float map_progress(float value)=0
float map_progress(float value) override
Definition animation.h:28
float position
Definition cover.h:0
uint8_t duration
Definition msa3xx.h:0
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:47
T clamp_at_most(T value, U max)
Definition helpers.h:2402
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
static void uint32_t