| Россия |
Мультимедиа. Анимация
Цель лекции: Научиться анимировать компоненты приложения
В наши дни всё больше требований предъявляется к интерактивности программ, т.е. к способности компонентов взаимодействовать друг с другом, а также изменяться в зависимости от действий пользователя. Особенностью современных приложений, особенно мультимедйных, является изменение их виджетами своего положения, размеров, цвета и прозрачности.
Библиотека 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, который позволяет анимировать как один компонент, так и целый набор посредством перемещения его / их на новые позиции и изменения уровня прозрачности.
Дополнительные материалы
Архив с исходными текстами примера Вы можете скачать
здесь.

