#pragma once #include "Easing.h" class Tween { private: static inline Tween* head = nullptr; static inline Tween* last = nullptr; static inline uint32_t frameRateTimer; Tween* next; public: Tween(uint16_t fps) { setFps(fps); if (Tween::head == nullptr) { Tween::head = this; } if (last != nullptr) { last->next = this; } last = this; } static void tick() { Tween* current = Tween::head; while (current != nullptr) { current->update(); current = current->next; } } /////// private: uint16_t frameTime; float dt; uint32_t oldMillis; float from; float to; float duration; EasingFunc::eFunc easing = nullptr; float progress; bool isPlayingF; public: float current; void dtTick() { uint32_t loopStartTime = millis(); dt = (loopStartTime - oldMillis) / 1000.0; oldMillis = loopStartTime; } void update() { if (millis() - frameRateTimer > frameTime) { dtTick(); if (!isPlayingF || easing == nullptr) return; if (((uint16_t)current) == ((uint16_t)to) /* || progress > duration */) { stop(); return; } current = clamp(Tween::lerp(from, to, easing(progress / duration)), from, to); progress = progress + Tween::dt; frameRateTimer = millis(); } } void start(float from, float to, uint16_t duration, EasingFunc::eFunc easing) { current = from; if (from == to) { return; } this->from = from; this->to = to; this->duration = duration / 1000.0; this->easing = easing; resetToStart(); dtTick(); isPlayingF = true; } void resetToStart() { progress = 0; } void stop() { isPlayingF = false; } bool isPlaying() { return isPlayingF; } void setFps(uint16_t fps){ frameTime = 1000 / fps; } private: static float lerp(float a, float b, float t) { return a + (b - a) * t; } int clamp(int value, int a, int b) { if (a > b) { a ^= b; b ^= a; a ^= b; } if (value < a) return a; else if (value > b) return b; else return value; } };