ECW Dev. UI Animations

Ничего особенного, если не посмотреть в код...

Рис. 1. Ура, я сделал это!

В этом посте я поделился технической особенностью проекта. И вот, один из пунктиков: анимация элементов интерфейса.

Предыстория

Если не смотреть на уже готовые решения, то каким образом мы пишем анимацию? Примерно так:

// ... color rgba; rgba.a = ease( timer.progress() ); draw( rect, rgba, texture ); // ...

Где у нас всё строго фиксировано. И текстура, и таймер, и функция анимации, и свойства, которые анимируются.

Велосипед не нужен

Дайте автомобиль

В этом проекте реализуется собственный фреймворк UI. А потому и возможность анимаций тут приходится костылять вручную. Дополнительным усложнением является то, что элементы рисуются посредством другого класса. Это означает, что сам элемент UI знать не знает ничего ни про атрибуты (текстуры, цвета, шрифты), ни про тайминги и функции для анимаций.

Единственное, что может сделать сам элемент - обозначить временную метку, когда именно произошла смена состояния.

Каково же было это печальное озарение, что одним

virtual void SetVisible( bool ); // не отделаться

А базовый класс UI нуждается в жирной такой обёртке:

class UI::Animated{ // ... bool b_visible; // видимость элемента bool b_closing; // рендерить элемент, но блокировать ввод bool b_show; // желаемая видимость bool b_close; // закрыть элемент после closing bool b_destroy; // уничтожить элемент после closing // ... virtual void SetVisible( bool b )override; // * - не менять значение // b_visible = b; b_show = * virtual void Open(); // b_visible = 1; b_show = * // childs.Show() virtual void Close(); // b_visible = 0; b_show = * // when b_closing == false // childs.Hide() virtual void Show(); // b_visible = *; b_show = 1 virtual void Hide(); // b_visible = *; b_show = 0 // when b_closing == false // ... };

Аж целых 3 (5) функций, чтобы покрыть базовые потребности для анимаций.

SetVisible : чтобы иметь возможность просто "вырубить питание" для элемента и исключить его из вывода на экран.

Open/Close : то же самое, что и SetVisible, только с поддержкой анимации.

Show/Hide : то же самое, что Open/Close, только локального действия.

Но самым главным здесь является состояние b_closing, необходимое для блокировки ввода пользователя лишь с последующим переводом в состояние невидимости.

И всё?

1. Осталось только для каждого чиха прописать анимируемые атрибуты: offset, scale, color, rotation, image, text (анимация текста это вообще отдельное приключение...) и, возможно, что-то ещё

2. Для каждого анимируемого атрибута прописать свойства: timein{ delay, duration, function, target }, timeout: timein{}

3. Реализовать менеджер таймеров, который real-time будет щёлкать потенциальное количество таймеров в сотни-тысячи штук

Самое весёлое во всём этом, пожалуй, анимируемые атрибуты. Поскольку буквально для каждого спрайта нужно, например, анимировать цвет. По всем каналам отдельно. Итого, 1 color - 4 vertex - 20 float. Так это не только в памяти хранить нужно, а ещё производить постоянный расчёт lerp с использованием таймеров.

Так что это уже маленькая победа

Поскольку сейчас анимация, хоть на костылях, но честно работает из связки UI::Animated + TimersManager + Drawer

int ifadems = 0; float fade = 1.f; if( GetField( "#fade", ifadems ) ){ // Получить целевое время таймера fade = ifadems / 1000.f; if( !Search( "#fade", fade ) ){ // Получить текущий прогресс таймера fade = 1.f; } } // ... auto clr = m_img.GetColor(); if( m_flg.Is( this->FADING ) ){ // Свойство анимации a = clr; a.a *= fade; m_img.SetColor( a.GetHWColor() ); } m_img.Render( r ); m_img.SetColor( clr );
1