Опубликован: 13.07.2012 | Доступ: свободный | Студентов: 461 / 9 | Оценка: 5.00 / 5.00 | Длительность: 18:06:00
Специальности: Программист
Лекция 18:

Мультимедиа. Анимация

< Лекция 17 || Лекция 18 || Лекция 19 >
Аннотация: В этой лекции вы познакомитесь с компонентом для анимации других компонентов (класс ComponentAnimator) и создадим с его помощью имитацию летящего в облаках самолёта.

Цель лекции: Научиться анимировать компоненты приложения

В наши дни всё больше требований предъявляется к интерактивности программ, т.е. к способности компонентов взаимодействовать друг с другом, а также изменяться в зависимости от действий пользователя. Особенностью современных приложений, особенно мультимедйных, является изменение их виджетами своего положения, размеров, цвета и прозрачности.

Библиотека Juce включает класс ComponentAnimator, который позволяет анимировать набор компонентов, включённых в программу, за счёт изменения их позиции, а кроме того, менять их прозрачность.

Рассмотрим использование этого класса на примере создания простой анимации — движение объекта (самолёт) на фоне изображения ( рис. 18.1). Изображения фона и самого самолёта будут загружаться из ресурсов программы.

Программа, демонстрирующая анимацию компонентов с помощью  ComponentAnimator

Рис. 18.1. Программа, демонстрирующая анимацию компонентов с помощью ComponentAnimator

В качестве виджета, который будет перемещаться по окну нашей программы, будет выступать объект класса 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

Рис. 18.2. Анимация кнопок в демонстрационном приложении Juce

Теперь вам будет легче разобраться с этим примером, который находится в папке <каталог Juce>/juce/extras/JuceDemo/Source/demos, файл WidgetsDemo.cpp.

Краткие итоги

Нами был рассмотрен класс ComponentAnimator, который позволяет анимировать как один компонент, так и целый набор посредством перемещения его / их на новые позиции и изменения уровня прозрачности.

Дополнительные материалы

Архив с исходными текстами примера Вы можете скачать здесь.

< Лекция 17 || Лекция 18 || Лекция 19 >