Мультимедиа. Анимация
Цель лекции: Научиться анимировать компоненты приложения
В наши дни всё больше требований предъявляется к интерактивности программ, т.е. к способности компонентов взаимодействовать друг с другом, а также изменяться в зависимости от действий пользователя. Особенностью современных приложений, особенно мультимедйных, является изменение их виджетами своего положения, размеров, цвета и прозрачности.
Библиотека Juce включает класс ComponentAnimator, который позволяет анимировать набор компонентов, включённых в программу, за счёт изменения их позиции, а кроме того, менять их прозрачность.
Рассмотрим использование этого класса на примере создания простой анимации — движение объекта (самолёт) на фоне изображения ( рис. 18.1). Изображения фона и самого самолёта будут загружаться из ресурсов программы.
В качестве виджета, который будет перемещаться по окну нашей программы, будет выступать объект класса DrawableImage (последний, как вы должны помнить, наследует класс Component, что нам и нужно).
Для запуска анимации будем использовать сигнал таймера, от которого мы и унаследуем класс компонента содержимого ( пример 18.1).
#ifndef _TCentralComponent_h_ #define _TCentralComponent_h_ //---------------------------------------------------- #include "../JuceLibraryCode/JuceHeader.h" //----------------------------------------------------- // Класс компонента содержимого. // Наследует класс таймера class TCentralComponent : public Component, public Timer { public: TCentralComponent(); ~TCentralComponent(); void paint(Graphics&); void resized(); void timerCallback(); // Бинарные ресурсы: static const char* psClouds_jpg; static const int iClouds_jpgSize; static const char* psAirplane_png; static const int iAirplane_pngSize; private: // Переменная для хранения картинки фона (облака) Image ChachedBackground; // Наш самолёт DrawableImage* pAirplane; // Компонент для управления анимацией самолёта ComponentAnimator Animator; // Прямоугольник - новая позиция самолёта Rectangle<int> PlanePosition; // Предотвращает создание копии конструктора и оператора = TCentralComponent(const TCentralComponent&); const TCentralComponent& operator= (const TCentralComponent&); }; //--------------------------------------------------- #endifЛистинг 18.1. Объявление класса компонента содержимого TCentralComponent (файл TCentralComponent.h)
Картинку неба с облаками мы будем загружать из бинарных ресурсов программы (psClouds_jpg) в объект класса Image, который и будет отвечать за её прорисовку на поверхности компонента содержимого. В свою очередь, картинка самолёта будет загружаться из ресурса psAirplane_png в виджет pAirplane.
Для хранения позиции самолёта, куда он будет перемещаться, в качестве члена класса объявлен прямоугольник Rectangle<int> PlanePosition. По срабатыванию таймера будет меняться координата x этой позиции.
Вначале нам необходимо инициировать приложение: нарисовать фон и самолёт, задать его положение и размеры, а также запустить таймер ( пример 18.2).
#include "TCentralComponent.h" //------------------------------------------------- TCentralComponent::TCentralComponent() : Component ("Central Component"), ChachedBackground(0), pAirplane(0) { // Загружаем изображение фона из памяти ChachedBackground = ImageCache::getFromMemory(psClouds_jpg, iClouds_jpgSize); // Создаём самолёт... pAirplane = new DrawableImage(); // и показываем его, загружая в него картинку из ресурсов pAirplane->setImage(ImageCache::getFromMemory(psAirplane_png, iAirplane_pngSize)); addAndMakeVisible(pAirplane); setSize (800, 600); // Задаём начальную позицию самолёта... PlanePosition.setPosition(Point<int>(0, 200)); // и его размеры (равны таковым загруженной картинки) PlanePosition.setSize(256, 256); // Стартуем таймер с интервалом в 1 секунду this->startTimer(1000); } //---------------------------------------------------- TCentralComponent::~TCentralComponent() { // Останавливаем таймер перед завершением работы this->stopTimer(); // Удаляем дочерние виджеты // и обнуляем их указатели deleteAllChildren(); } //--------------------------------------------------- void TCentralComponent::paint(Graphics& Canvas) { // Заливка - на всякий случай и для заголовка окна Canvas.fillAll(Colours::azure); // Отображаем фон, растягивая его по размерам окна программы Canvas.drawImage(ChachedBackground, 0, 0, proportionOfWidth(1.0000f), proportionOfHeight(1.0000f), 0, 0, ChachedBackground.getWidth(), ChachedBackground.getHeight()); } //---------------------------------------------------- void TCentralComponent::resized() { pAirplane->setBounds(0, 200, 256, 256); } //-----------------------------------------------------Листинг 18.2. Часть реализации класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Логика работы программы реализована в обработчике события срабатывания таймера ( пример 18.3).
void TCentralComponent::timerCallback() { // Если самолёт "улетел" за пределы окна // (его левая граница больше ширины окна)... if(pAirplane->getX() >= getWidth()) { // перерисовываем изображение pAirplane->setImage(ImageCache::getFromMemory(psAirplane_png, iAirplane_pngSize)); // и меняем позицию самолёта на начальную PlanePosition.setX(0); } // Задаём новую позицию самолёта для перемещения PlanePosition.setX(getWidth()); // Двигаем самолёт Animator.animateComponent( pAirplane, PlanePosition, pAirplane->getAlpha(), 800, alse, 1.0, 1.0 ); }Листинг 18.3. Реализация метода timerCallback класса компонента содержимого TCentralComponent (файл TCentralComponent.cpp)
Для перемещения нашего самолёта мы воспользовались методом void ComponentAnimator::animateComponent(Component* component, const Rectangle< int >& finalBounds, float finalAlpha, int animationDurationMilliseconds, bool useProxyComponent, double startSpeed, double endSpeed). Рассмотрим передаваемые в него параметры. В качестве первого мы передаём указатель на анимируемый компонент component (в нашем случае это DrawableImage*).
Вторым параметром служит область, в которую мы хотим переместить компонент, finalBounds. Это объект класса Rectangle, служащий для хранения информации о каком-либо прямоугольнике и предоставляющий методы для выполнения над последним каких-либо геометрических операций. Основными свойствами класса являются координаты его верхнелевого угла (x и y), а также длина и высота прямоугольника. Изменяя эти свойства у объекта класса Rectangle, переданного в метод animateComponent, мы можем не только перемещать анимированный компонент в произвольном направлении, но и изменять (увеличивать или уменьшать) его линейные размеры. В нашем случае (простейшем), изменяется лишь координата x самолёта, что приводит к его перемещению по горизонтали слева направо.
Если в качестве второго параметра в animateComponent передать значение, возвращаемое методом getBounds() компонента component, то, как понятно, он не будет анимирован (останется на прежнем месте и с прежними размерами).
При анимации компонент может менять свою прозрачность на значение finalAlpha. Выглядит это довольно красиво, но в нашем случае нам нет необходимости делать это, поэтому в качестве этого параметра мы передали прежнее значение альфы изображения самолёта.
Продолжительность анимации компонента в миллисекундах задаётся переменной int animationDurationMilliseconds.
В том случае, если параметр bool useProxyComponent принимает значение true, то анимируемый компонент может замещаться своей временной копией, что ускоряет перерисовку во время выполнения анимации.
Переменные double startSpeed и double endSpeed задают относительные скорости начала и завершения анимации, соответственно. В том случае, если мы ходим, чтобы объект двигался с одинаковой скоростью в начале и в конце анимации, обе переменные должны принимать значение 1.0. В том случае, если первый параметр принимает значение 0, это значит, что первоначально объект неподвижен и разгоняется от старта. Если значение начальной скорости больше 1.0, то объект начнёт её сбрасывать к середине маршрута анимации. В том случае, если параметр endSpeed принимает значение 0, то его скорость сбрасывается к моменту окончания анимации, в противном случае в это время он продолжает движение.
Кроме рассмотренного, класс ComponentAnimator включает ещё несколько полезных методов. Вот некоторые из них:
- void ComponentAnimator::cancelAnimation(Component* component, bool moveComponentToItsFinalPosition) — останавливает анимацию компонента component. В том случае, если параметр moveComponentToItsFinalPosition принимает значение true, компонент немедленно устанавливается в финальную позицию с заданными окончательными размерами; в противном случае он останавливается в текущей позиции;
- void ComponentAnimator::cancelAllAnimations(bool moveComponentsToTheirFinalPositions) — останавливает анимацию всех компонентов. В том случае, если параметр moveComponentsToItsFinalPosition принимает значение true, все компоненты немедленно устанавливается в их финальные позиции с заданными окончательными размерами; в противном случае они останавливаются в текущей позиции;
- bool ComponentAnimator::isAnimating(Component* component) const — проверяет, является ли компонент component анимированным.
В демонстрационном приложении Juce используется более сложный пример, расплывающихся кнопок, которые во время движения меняют свою прозрачность и размеры случайным образом после нажатия на кнопку "click to animate..." ( рис. 18.2).
Теперь вам будет легче разобраться с этим примером, который находится в папке <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp.
Краткие итоги
Нами был рассмотрен класс ComponentAnimator, который позволяет анимировать как один компонент, так и целый набор посредством перемещения его / их на новые позиции и изменения уровня прозрачности.
Дополнительные материалы
Архив с исходными текстами примера Вы можете скачать здесь.