Создание класса анимации объектов на ActionScript 3. P.1
В этой статье я опишу создание простейшего класса, позволяющего выполнять элементарную анимацию объектов во Flash на ActionScript 3. Я знаю, что Adobe придумали такую клевую вещь как Motion Tween, которая позволяет легко выполнять перемещения и трансформации графических объектов, но так как я использую FlashDevelop для разработки Flash-приложений, сей чудный инструмент мне не доступен. Существуют, конечно, способы подключения необходимых классов к проекту, но танцевать с бубном мне не хотелось, к тому же были необходимы лишь простейщие эффекты анимации, как то: перемещенеи объекта по осям x и y и плавное изменение прозрачности для создания эффектов постепенного появления/исчезновения объектов. Поэтому, недолго думая, я приступил к создания собственного класса анимации, который удовлетворил бы моим нуждам. Заранее предупрежу, что я не претендую на звание профессионала по программированию на ActionScript, поэтому код наверняка будет не идеальным. Прошу отнестись с пониманием. Итак, приступим...
Первым делом, мы создадим новый проект:

В только что созданный проект добавим новый класс и назовем его Animate:


На этом с картинками заканчиваем, и переходим непосредственно к коду. Только что созданный класс будет выглядеть вот так:
package { /** * ... * @author twix */ public class Animate { public function Animate() { } } }
Первое, с чем нам надо определиться - с параметрами, которые будут передаваться в конструктор класса. Предположим, при создании, он будет получать ссылку на объект, с которым необходимо произвести некоторые манипуляции, их характер и силу, продолжительность и callback-функцию, выполняемую по завершении анимации. Для этого добавляем их в параметры конструктора и получаем следующее:
public function Animate(target:DisplayObject, animationType:String = "none", timeOut:Number = 100, callback:Function = null)
Перечислим их:
target:DisplayObject - мы не знаем о типе объекта, на который получаем ссылку, поэтому предполагаем, что это может быть вообще любой графический объект;
animationType:String = "none" - тип анимации. Ключевой параметр, который может принимать разные значения, например "fadeIn" или "x=100";
timeOut:Number = 100 - количество миллисекунд, за которое анимация должна быть выполнена;
callback:Function = null - функция обратного вызова, которая будет вызываться после завершения анимации, может быть не задана.
Естественно, нам потребуются переменные, для хранения этих параметров и использования их в других функциях класса. Их мы поместим в самое начало его описания:
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; // заданное время анимации в миллисекундах
Конструктор должен присвоить этим переменным необходимые значения, а затем запустить таймер, который будет вызывать основную функцию-обработчик каждую миллисекунду, поэтому добавляем в него следующие строки:
if (target != null) // анимация будет работать только в том случае, если передана ссылка на объект { _target = target; // запоминаем переданную ссылку на объект _animate = animationType; // то же делаем с типом анимации _callback = callback; // и с функцией обратного вызова _timeOut = timeOut; // и со временем действия анимации _start = flash.utils.getTimer(); // сохраняем время, в которое началась анимация _interval = flash.utils.setInterval(doAnimation, 1); // запускаем таймер, который будет вызывать функцию doAnimation каждую миллисекунду }
Теперь опишем главную функцию нашего класса - doAnimation. Из ее названия понятно, что она должна делать. На данный момент, для примера, разберем простейший вариант с плавным появлением и исчезновением обьекта - fadeIn и fadeOut.
private function doAnimation():void { var percent:Number = (getTimer() - _start) / _timeOut; // вычисляем количество уже затраченного времени switch(_animate) { case "fadeIn": // если тип анимации - плавное появление flash.display.DisplayObject(_target).alpha = percent; break; case "fadeOut": // если тип анимации - плавное исчезновение flash.display.DisplayObject(_target).alpha = 1 - percent; break; } if (percent >= 1) // если весь временной путь пройден { clearInterval(_interval); // отключаем таймер if (_callback != null) _callback(); // и вызываем функцию обратного вызова, если она задана } }
Теперь, чтобы проверить, что все идет по плану, мы вернемся к классу Main нашего проекта и добавим следующие строки в его функцию init():
var sprite:Sprite = new Sprite(); // создаем новый графический объект sprite.graphics.beginFill(0x0000FF); sprite.graphics.drawRect(0, 0, 50, 50); // рисуем на нем синий квадрат со стороной 50 пикселей sprite.graphics.endFill(); sprite.x = sprite.y = 100; // указываем его положение addChild(sprite); // и добавляем на сцену new Animate(sprite, "fadeIn", 500); // выполняем анимацию - плавное появление
Запустив проект на сборку и выполнение, мы должны будем увидеть плавно появляющийся синий квадрат:

Я думаю, вы могли заметить, что анимация выполняется не слишком плавно. Это связано с тормозным поведением самого Flash. Несмотря на то, что таймер класса Animate должен вызывать функцию doAnimate() раз в 1 миллисекунду, он делает это значительно реже. По наблюдениям на моем компьютере, это происходит раз в 15-20 миллисекунд. На комьютерах с другой конфигурацией эта цифра будет варьировать. Поэтому, при использовании большого количества графических объектов в проекте, их анимация на слабых компьютерах может выполняться не так плавно, как хотелось бы. Но, увы, с этим придется мириться.
На данном этапе наш класс может предоставить два способа анимации объектов: плавное появление и плавное исчезновение. Чтобы не перегружать статью, я оставлю другие способы анимации (перемещение объекта по осям x и y) для следующей статьи. А сейчас просто приведу исходные коды двух файлов, задействованных в нашем проекте.
Main.as
package { import flash.display.Sprite; import flash.events.Event; import Animate; /** * ... * @author twix */ public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point var sprite:Sprite = new Sprite(); // создаем новый графический объект sprite.graphics.beginFill(0x0000FF); sprite.graphics.drawRect(0, 0, 50, 50); // рисуем на нем синий квадрат со стороной 50 пикселей sprite.graphics.endFill(); sprite.x = sprite.y = 100; // указываем его положение addChild(sprite); // и добавляем на сцену new Animate(sprite, "fadeIn", 500); // выполняем анимацию - плавное появление } } }
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; // заданное время анимации в миллисекундах 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(); // сохраняем время, в которое началась анимация _interval = setInterval(doAnimation, 1); // запускаем таймер, который будет вызывать функцию doAnimation каждую миллисекунду } } private function doAnimation():void { var percent:Number = (getTimer() - _start) / _timeOut; // вычисляем количество уже затраченного времени switch(_animate) { case "fadeIn": // если тип анимации - плавное появление DisplayObject(_target).alpha = percent; break; case "fadeOut": // если тип анимации - плавное исчезновение DisplayObject(_target).alpha = 1 - percent; break; } if (percent >= 1) // если весь временной путь пройден { clearInterval(_interval); // отключаем таймер if (_callback != null) _callback(); // и вызываем функцию обратного вызова, если она задана } } } }
Нет обратных ссылок на эту запись.
