1.10
animation.h
1/*
2 * Copyright (C) 2018 Microchip Technology Inc. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6#ifndef EGT_ANIMATION_H
7#define EGT_ANIMATION_H
8
14#include <chrono>
15#include <egt/detail/meta.h>
16#include <egt/easing.h>
17#include <egt/geometry.h>
18#include <egt/timer.h>
19#include <functional>
20#include <memory>
21#include <vector>
22
23namespace egt
24{
25inline namespace v1
26{
30using EasingScalar = float;
31
38using AnimationCallback = std::function<void (EasingScalar value)>;
39
41using EasingFunc = std::function<EasingScalar(EasingScalar percent)>;
42
43namespace detail
44{
45
55template<class Functor, class T>
56T interpolate(Functor&& easing, T percent, T start, T end, bool reverse = false)
57{
58 if (percent < 0.0f)
59 return start;
60 else if (percent > 1.0f)
61 return end;
62
63 if (reverse)
64 percent = 1.0f - easing(1.0f - percent);
65 else
66 percent = easing(percent);
67
68 return start * (1.0f - percent) + end * percent;
69}
70
74class EGT_API AnimationBase
75{
76public:
77
78 AnimationBase() = default;
79 AnimationBase(const AnimationBase&) = delete;
81 AnimationBase(AnimationBase&&) noexcept = default;
82 AnimationBase& operator=(AnimationBase&&) noexcept = default;
83
87 virtual void start() = 0;
88
95 virtual bool next() = 0;
96
100 virtual void stop() = 0;
101
105 virtual void resume() = 0;
106
110 EGT_NODISCARD virtual bool running() const { return m_running; }
111
123 {
124 if (callback)
125 m_callbacks.emplace_back(std::move(callback));
126 }
127
132 {
133 m_callbacks.clear();
134 }
135
136 virtual ~AnimationBase() noexcept = default;
137
138protected:
139
141 bool m_running{false};
142
144 std::vector<AnimationCallback> m_callbacks;
145};
146}
147
167class EGT_API Animation : public detail::AnimationBase
168{
169public:
170
179 EasingScalar end,
180 const AnimationCallback& callback,
181 std::chrono::milliseconds duration,
182 EasingFunc func = easing_linear);
183
187 EGT_NODISCARD EasingScalar starting() const { return m_start; }
188
192 void starting(EasingScalar start) { m_start = start; }
193
197 EGT_NODISCARD EasingScalar ending() const { return m_end; }
198
202 void ending(EasingScalar end) { m_end = end; }
203
207 void duration(std::chrono::milliseconds dur) { m_duration = dur; }
208
213
217 void reverse(bool rev) { m_reverse = rev; }
218
220 void start() override;
221
228 bool next() override;
229
231 void stop() override;
232
234 void resume() override;
235
237 EGT_NODISCARD EasingScalar current() const { return m_current; }
238
247 void rounding(bool enable) { m_round = enable; }
248
249protected:
250
252 EasingScalar m_start{0};
253
255 EasingScalar m_end{0};
256
258 EasingScalar m_elapsed{0};
259
262
264 EasingScalar m_current{0};
265
267 std::chrono::milliseconds m_duration{};
268
270 std::chrono::time_point<std::chrono::steady_clock> m_intermediate_time;
271
273 bool m_reverse{false};
274
276 bool m_round{false};
277};
278
292class EGT_API AnimationSequence : public detail::AnimationBase
293{
294public:
295
299 explicit AnimationSequence(bool loop = false) noexcept
300 : m_loop(loop)
301 {}
302
308 virtual void add(detail::AnimationBase& animation)
309 {
310 // Nasty, but it gets the job done. If a widget is passed in as a
311 // reference, we don't own it, so create a "pointless" shared_ptr that
312 // will not delete it.
313 add(std::shared_ptr<detail::AnimationBase>(&animation, [](detail::AnimationBase*) {}));
314 }
315
324 template<class T>
325 void add(const std::shared_ptr<T>& animation)
326 {
327 add(std::dynamic_pointer_cast<detail::AnimationBase>(animation));
328 }
329
335 virtual void add(const std::shared_ptr<detail::AnimationBase>& animation)
336 {
337 if (!animation)
338 return;
339
340 m_animations.push_back(animation);
341
342 auto weak_animation = std::weak_ptr<detail::AnimationBase>(animation);
343 animation->add_callback([this, weak_animation](EasingScalar)
344 {
345 auto animation = weak_animation.lock();
346 if (animation)
347 {
348 // when the animation ever completes, we go next
349 if (!animation->running())
350 next();
351 }
352 });
353 }
354
359 {
360 if (!m_animations.empty())
361 m_animations.pop_back();
362 }
363
369 void removeAt(size_t pos)
370 {
371 if (pos < m_animations.size())
372 m_animations.erase(m_animations.begin() + pos);
373 }
374
381 {
382 if (!animation)
383 return;
384
385 const auto i = std::find_if(m_animations.begin(), m_animations.end(),
386 [animation](const auto & ptr)
387 {
388 return ptr.get() == animation;
389 });
390 if (i != m_animations.end())
391 {
392 m_animations.erase(i);
393 }
394 }
395
399 bool skip()
400 {
401 if (m_current >= m_animations.size())
402 return false;
403
404 m_animations[m_current]->stop();
405
406 if (++m_current >= m_animations.size())
407 return false;
408
409 m_animations[m_current]->start();
410
411 return true;
412 }
413
414 void start() override
415 {
416 // Prevent case when sequence is started without any animation added.
417 if (m_current >= m_animations.size())
418 return;
419
420 if (running())
421 stop();
422
423 m_current = 0;
424
425 m_animations[m_current]->start();
426
427 m_running = true;
428 }
429
430 bool next() override
431 {
432 if (!running())
433 return true;
434
435 if (m_animations[m_current]->running())
436 return false;
437
438 if (++m_current >= m_animations.size())
439 {
440 if (m_loop)
441 {
442 m_current = 0;
443 }
444 else
445 {
446 m_current = 0;
447 m_running = false;
448 return m_running;
449 }
450 }
451
452 m_animations[m_current]->start();
453
454 return m_running;
455 }
456
457 void stop() override
458 {
459 if (m_current >= m_animations.size())
460 return;
461
462 m_animations[m_current]->stop();
463
464 m_running = false;
465 }
466
467 void resume() override
468 {
469 if (m_current >= m_animations.size())
470 return;
471
472 if (running())
473 return;
474
475 m_animations[m_current]->resume();
476
477 m_running = true;
478 }
479
480protected:
481
483 using AnimationArray = std::vector<std::shared_ptr<detail::AnimationBase>>;
484
487
489 size_t m_current{0};
490
492 bool m_loop{false};
493};
494
504class EGT_API AutoAnimation : public Animation
505{
506public:
507
516 EasingScalar end,
517 std::chrono::milliseconds duration,
518 const EasingFunc& func = easing_linear,
519 const AnimationCallback& callback = nullptr);
520
526 explicit AutoAnimation(std::chrono::milliseconds duration,
527 const EasingFunc& func = easing_linear,
528 const AnimationCallback& callback = nullptr);
529
530 void start() override;
531 void stop() override;
532 void resume() override;
533
541 void interval(std::chrono::milliseconds duration);
542
543protected:
544
549};
550
560template <class T>
562{
563public:
564
568 using Value = T;
569
576 explicit PropertyAnimatorType(T start = T(),
577 T end = T(),
578 std::chrono::milliseconds duration = std::chrono::milliseconds(),
579 const EasingFunc& func = easing_linear)
580 : AutoAnimation(start, end, duration, func,
581 [this](T value) { invoke_handlers(value); })
582 {}
583
588 explicit PropertyAnimatorType(std::chrono::milliseconds duration,
589 const EasingFunc& func = easing_linear)
590 : AutoAnimation(duration, func,
591 [this](T value)
592 {
593 invoke_handlers(value);
594 })
595 {}
596
598 using PropertyCallback = std::function<void (T v)>;
599
606 {
607 m_callbacks.emplace_back(std::move(callback));
608 }
609
612 {
613 m_callbacks.clear();
614 }
615
616protected:
617
619 void invoke_handlers(T value)
620 {
621 for (const auto& callback : m_callbacks)
622 callback(value);
623 }
624
626 using CallbackArray = std::vector<PropertyCallback>;
627
630};
631
638
645
654class EGT_API AnimationDelay : public detail::AnimationBase
655{
656public:
657
658 AnimationDelay() = delete;
659
663 explicit AnimationDelay(std::chrono::milliseconds duration) noexcept
664 : m_timer(duration)
665 {
666 m_timer.on_timeout([this]()
667 {
668 if (!next())
669 {
670 stop();
671 }
672 });
673 }
674
675 void start() override
676 {
677 m_timer.start();
678 }
679
680 bool next() override
681 {
682 for (const auto& c : m_callbacks)
683 c(0);
684
685 return false;
686 }
687
688 void stop() override
689 {
690 m_timer.cancel();
691 }
692
693 void resume() override
694 {
695 m_timer.start();
696 }
697
698protected:
699
702};
703
704}
705}
706
707#endif
Simple delay, useful to insert a delay in an AnimationSequence.
Definition animation.h:655
Timer m_timer
One-shot timer.
Definition animation.h:701
void resume() override
Resume the animation.
Definition animation.h:693
void start() override
Start the animation.
Definition animation.h:675
void stop() override
Stop the animation.
Definition animation.h:688
AnimationDelay(std::chrono::milliseconds duration) noexcept
Definition animation.h:663
bool next() override
Periodic call to the animation.
Definition animation.h:680
Series of animations as a single animation.
Definition animation.h:293
AnimationSequence(bool loop=false) noexcept
Definition animation.h:299
std::vector< std::shared_ptr< detail::AnimationBase > > AnimationArray
Helper type for an array of animations.
Definition animation.h:483
void resume() override
Resume the animation.
Definition animation.h:467
void start() override
Start the animation.
Definition animation.h:414
virtual void add(detail::AnimationBase &animation)
Add an animation to the animation sequence.
Definition animation.h:308
void removeAt(size_t pos)
Remove the sub animation at the given position from the sequence.
Definition animation.h:369
void stop() override
Stop the animation.
Definition animation.h:457
void remove(detail::AnimationBase *animation)
Remove the first occurence of a sub animation from the sequence.
Definition animation.h:380
virtual void add(const std::shared_ptr< detail::AnimationBase > &animation)
Add a sub animation to the sequence.
Definition animation.h:335
void add(const std::shared_ptr< T > &animation)
Add an animation to the animation sequence.
Definition animation.h:325
bool skip()
Skip to the next sub animation in the sequence.
Definition animation.h:399
bool next() override
Periodic call to the animation.
Definition animation.h:430
AnimationArray m_animations
The animations of the sequence.
Definition animation.h:486
void removeLast()
Remove the last sub animation from the sequence.
Definition animation.h:358
Animation class with configurable easing function.
Definition animation.h:168
void reverse(bool rev)
Definition animation.h:217
EGT_NODISCARD EasingScalar current() const
Get the current value.
Definition animation.h:237
void resume() override
Resume the animation from its current value.
void ending(EasingScalar end)
Definition animation.h:202
void starting(EasingScalar start)
Definition animation.h:192
void start() override
Start the animation from its start value.
void easing_func(EasingFunc func)
EGT_NODISCARD EasingScalar ending() const
Get the ending value.
Definition animation.h:197
void rounding(bool enable)
Should the value be rounded?
Definition animation.h:247
EGT_NODISCARD EasingScalar starting() const
Get the starting value.
Definition animation.h:187
void stop() override
Stop the animation.
Animation(EasingScalar start, EasingScalar end, const AnimationCallback &callback, std::chrono::milliseconds duration, EasingFunc func=easing_linear)
void duration(std::chrono::milliseconds dur)
Definition animation.h:207
std::chrono::time_point< std::chrono::steady_clock > m_intermediate_time
Absolute time used to track the animation time elapsed.
Definition animation.h:270
bool next() override
Periodic call to the animation.
Animation object with built in timer.
Definition animation.h:505
void resume() override
Resume the animation from its current value.
void start() override
Start the animation from its start value.
void interval(std::chrono::milliseconds duration)
Change the interval of the internal timer.
void stop() override
Stop the animation.
AutoAnimation(EasingScalar start, EasingScalar end, std::chrono::milliseconds duration, const EasingFunc &func=easing_linear, const AnimationCallback &callback=nullptr)
PeriodicTimer m_timer
Periodic timer used to run the animation.
Definition animation.h:548
AutoAnimation(std::chrono::milliseconds duration, const EasingFunc &func=easing_linear, const AnimationCallback &callback=nullptr)
Periodic timer.
Definition timer.h:281
Animates a property of a widget.
Definition animation.h:562
void invoke_handlers(T value)
Invoke handlers with the specified value.
Definition animation.h:619
PropertyAnimatorType(T start=T(), T end=T(), std::chrono::milliseconds duration=std::chrono::milliseconds(), const EasingFunc &func=easing_linear)
Definition animation.h:576
void clear_change_callbacks()
Clear all callbacks.
Definition animation.h:611
CallbackArray m_callbacks
Registered callbacks for the animation.
Definition animation.h:629
std::vector< PropertyCallback > CallbackArray
Property callback array type.
Definition animation.h:626
PropertyAnimatorType(std::chrono::milliseconds duration, const EasingFunc &func=easing_linear)
Definition animation.h:588
void on_change(PropertyCallback callback)
Register a callback handler for when the value changes.
Definition animation.h:605
std::function< void(T v)> PropertyCallback
Property callback type.
Definition animation.h:598
T Value
Alias for the value type of the animator.
Definition animation.h:568
Basic one shot timer.
Definition timer.h:70
Base class for an animation.
Definition animation.h:75
AnimationBase & operator=(const AnimationBase &)=delete
AnimationBase(const AnimationBase &)=delete
void clear_callbacks()
Clear all callbacks.
Definition animation.h:131
virtual ~AnimationBase() noexcept=default
AnimationBase(AnimationBase &&) noexcept=default
std::vector< AnimationCallback > m_callbacks
Registered callbacks for the animation.
Definition animation.h:144
void add_callback(AnimationCallback callback)
Register a callback for the animation.
Definition animation.h:122
EGT_API EasingScalar easing_linear(EasingScalar p)
<style>div.image img[src="linear.png"]{width:320px;}</style>
T interpolate(Functor &&easing, T percent, T start, T end, bool reverse=false)
Interpolate function used internally.
Definition animation.h:56
std::function< EasingScalar(EasingScalar percent)> EasingFunc
Easing function type.
Definition animation.h:41
std::function< void(EasingScalar value)> AnimationCallback
Animation callback type.
Definition animation.h:38
float EasingScalar
Easing function value type.
Definition animation.h:30
EGT framework namespace.
Definition animation.h:24