Создание класса анимации объектов на ActionScript 3. P.2
Продолжение статьи о создании класса-аниматора, реализующего простейшие функции анимации для графических объектов на ActionScript 3.
В прошлой статье мы остановились на создании рабочего класса-аниматора, способного выполнять два типа анимации: плавное появление и плавное исчезновение графических объектов. А сейчас мы попробуем добавить еще два вида: перемещение по осям x или y на указанное расстояние и перемещение по этим осям до указанного положения.
Для начала прикинем, как мы будем выполнять анимацию перемещения по одной из осей на заданное расстояние. Это простейший вариант - так как в основной функции класса Animate вычисляется процент пройденного времени (от 0 до 1), положение объекта получается простым умножением этого числа на указанное расстояние. Таким образом при каждом вызове функции doAnimation мы будем иметь возможность точно узнать, где должен находиться объект в данный момент времени. С перемещением по осям к указанному значению технология немного отличается - путь, который объект должен будет пройти, нам не известен - его придется вычислить, отталкиваясь от значений текущего положения объекта и его положения на конец анимации.
Теперь разберемся с переменными, которые нам нужно будет добавить в класс, чтобы реализовать желаемый функционал. Необходимыми будут хранение длины пути, который придется пройти объекту за время выполнения операции, и начальной позиции объекта. Следует так же учесть, что их значения могут быть как положительными, так и отрицательными:
private var _startPoint:Number; // начальное положение объекта на оси private var _delta:Number = 0; // длина пути, который необходимо пройти объекту
Кажется, этого достаточно. Но было бы неплохо определиться и с описанием видов анимации, которые будут реализовываться в нашем классе. Как мы помним, они передаются конструктору в виде строки вторым параметром (мы уже используем "fadeIn" и "fadeOut"). Но как же передать еще и цифровое значение, обозначающее путь объекта или его конечную точку? Очень просто:
"x-100" - перемещение объекта по оси x на -100 пикселей;
"y+50" - перемещение объекта по оси y на 50 пикселей;
"y=30" - перемещение объекта по оси y с текущей позиции до 30.
Просто, не правда ли? Нам останется лишь обработать эту строку, выделив из нее цифровое значение, и записать его в переменную _delta.
if ( (_animate.substr(0, 1) == "x") ) // если указана анимация перемещения по оси x _startPoint = DisplayObject(_target).x; // запоминаем стартовую позицию на оси x if ( (_animate.substr(0, 1) == "y") ) // если указана анимация перемещения по оси y _startPoint = DisplayObject(_target).y; // запоминаем стартовую позицию на оси y if ( !isNaN(_startPoint) ) // проверяем, выполняется ли перемещение по наличию значения стартовой позиции { switch(_animate.substr(1, 1)) //проверяем тип анимации перемещения { case "=": // перемещение в указанную точку по оси _delta = parseInt(_animate.substr(2)) - _startPoint; // вычисляем путь до указанной точки break; default: // перемещение на указанное значение по оси _delta = parseInt(_animate.substr(1)); // здесь просто преобразуем часть строки в число и сохраняем его } _animate = _animate.substr(0, 1); // запоминаем только тип анимации, обрезав числовое значение }
Поместив эти строки в конструктор перед стартом таймера, мы будем иметь информацию, необходимую для выполнения перемещений объекта по осям. Само перемещение выполняется так же просто, как и изменение прозрачности объекта для типов анимации fadeIn и fadeOut:
case "x": // если тип анимации - перемещение по оси x DisplayObject(_target).x = _startPoint + _delta * percent; break; case "y": // если тип анмиации - перемещение по оси y DisplayObject(_target).y = _startPoint + _delta * percent; break;
Теперь, если мы попробуем создать в модуле Main объект Animate с параметром анимации "x+100" или "y=200", то получим желаемый результат - перемещение синего квадрата по сцене. Однако, учитывая специфику работы таймера, может оказаться, что объект по завершении анимации оказывается чуть дальше нужной позиции. Происходит это потому, что к моменту последнего срабатывания прошло больше времени, чем мы отвели под анимацию, и переменная percent в функции doAnimation будет иметь значение больше единицы. Чтобы избежать этого, ограничим ее значение сверху, выполняя простую проверку сразу после вычисления значения переменной percent:
if (percent > 1) percent = 1; // чтобы не "промахнуться", ограничиваем значение сверху
Теперь наш класс умеет управлять не только прозрачностью графических объектов, но и их положением на сцене. Без сомнения, можно добавить удобства и, возможно, скорости работы нашего аниматора, но цели, поставленные в первой статье, достигнуты. Для полноты картины осталось лишь приложить полный код нашего класса:
Animate.as
package { import flash.display.DisplayObject; import flash.utils.clearInterval; import flash.utils.getTimer; import flash.utils.setInterval; /** * ... * @author twix */ public class Animate { private var _target:*; // ссылка на объект, который является "жертвой" анимации private var _animate:String; // тип анимации private var _callback:Function; // ссылка на функцию обратного вызова private var _start:uint = 0; // количество миллисекунд, прошедших с запуска Flash на момент старта анимации private var _interval:uint; // указатель на таймер, взводимый вызовом функции setInterval private var _timeOut:Number; // заданное время анимации в миллисекундах private var _startPoint:Number; // начальное положение объекта на оси private var _delta:Number = 0; // длина пути, который необходимо пройти объекту public function Animate(target:DisplayObject, animationType:String = "none", timeOut:Number = 100, callback:Function = null) { if (target != null) // анимация будет работать только в том случае, если передана ссылка на объект { _target = target; // запоминаем переданную ссылку на объект _animate = animationType; // то же делаем с типом анимации _callback = callback; // и с функцией обратного вызова _timeOut = timeOut; // и со временем действия анимации _start = getTimer(); // сохраняем время, в которое началась анимация if ( (_animate.substr(0, 1) == "x") ) // если указана анимация перемещения по оси x _startPoint = DisplayObject(_target).x; // запоминаем стартовую позицию на оси x if ( (_animate.substr(0, 1) == "y") ) // если указана анимация перемещения по оси y _startPoint = DisplayObject(_target).y; // запоминаем стартовую позицию на оси y if ( !isNaN(_startPoint) ) // проверяем, выполняется ли перемещение по наличию значения стартовой позиции { switch(_animate.substr(1, 1)) //проверяем тип анимации перемещения { case "=": // перемещение в указанную точку по оси _delta = parseInt(_animate.substr(2)) - _startPoint; // вычисляем путь до указанной точки break; default: // перемещение на указанное значение по оси _delta = parseInt(_animate.substr(1)); // здесь просто преобразуем часть строки в число и сохраняем его } _animate = _animate.substr(0, 1); // запоминаем только тип анимации, обрезав числовое значение } _interval = setInterval(doAnimation, 1); // запускаем таймер, который будет вызывать функцию doAnimation каждую миллисекунду } } private function doAnimation():void { var percent:Number = (getTimer() - _start) / _timeOut; // вычисляем количество уже затраченного времени if (percent > 1) percent = 1; // чтобы не "промахнуться", ограничиваем значение сверху switch(_animate) { case "fadeIn": // если тип анимации - плавное появление DisplayObject(_target).alpha = percent; break; case "fadeOut": // если тип анимации - плавное исчезновение DisplayObject(_target).alpha = 1 - percent; break; case "x": // если тип анимации - перемещение по оси x DisplayObject(_target).x = _startPoint + _delta * percent; break; case "y": // если тип анмиации - перемещение по оси y DisplayObject(_target).y = _startPoint + _delta * percent; break; } if (percent >= 1) // если весь временной путь пройден { clearInterval(_interval); // отключаем таймер if (_callback != null) _callback(); // и вызываем функцию обратного вызова, если она задана } } } }
Нет обратных ссылок на эту запись.
