В этом уроке мы сделаем многоразовый таймер обратного отсчета с динамической целевой датой, которую можно установить с помощью XML. Мы будем анимировать номера в стиле старого аэропорта или табло статуса железнодорожного вокзала. Мы рассмотрим код, создание графики и анимацию.
Каждые несколько недель мы пересматриваем некоторые из любимых постов нашего читателя на протяжении всей истории сайта. Этот учебник был впервые опубликован в мае 2010 года.
Шаг 1: Настройте файл Flash
Создайте новый файл Flash (Actionscript 3) со следующими настройками: 500×300, черный фон и 30 кадров в секунду.
Шаг 2. Создайте мувиклип digit_bottom
Создайте новый мувиклип с именем digit_bottom и нарисуйте внутри него скругленный прямоугольник шириной около 36 пикселей и высотой 50 пикселей. (Быстрый способ нарисовать прямоугольник с точными размерами — выбрать инструмент «Прямоугольник» и щелкнуть «Alt» на сцене.)
Дайте прямоугольнику градиентную заливку от # 111111 (вверху) до # 333333 (внизу) и цветной контур 2 пикселя # 333333.
Шаг 3: поместите прямоугольник
Расположите прямоугольник так, чтобы точка регистрации мувиклипа (маленький знак «+») находилась точно посередине между верхом и низом и левым краем. Если вы сделали прямоугольник высотой 50px, то значение y должно быть -25.
Шаг 4: Добавьте номер
Создайте новый слой и добавьте динамическое текстовое поле с именем «t_num». Выберите шрифт, который имеет отношение к аэропорту или вокзалу (например, Helvetica , DIN или Interstate ). Я использую Helvetica Bold.
Установите формат абзаца по центру и не забудьте встроить шрифты для чисел 0–9.
Расположите текстовое поле так, чтобы оно находилось по центру прямоугольника фона.
Мы собираемся использовать этот мувиклип в качестве основы для другой графики, поэтому потратьте немного времени, чтобы убедиться, что он выглядит хорошо.
Шаг 5: добавь маску
Создайте новый слой на временной шкале MovieClip digit_bottom и назовите его «mask». Скопируйте прямоугольник со скругленными углами и вставьте их на слой маски («Правка»> «Вставить на месте» или «Command-Shift-V»).
Выберите верхнюю половину маски прямоугольника и удалите ее.
Щелкните правой кнопкой мыши слой маски , выберите «Маска» и убедитесь, что он маскирует все слои под ним.
Шаг 6: Создайте мувиклип digit_top
Зайдите в библиотеку, продублируйте мувиклип digit_bottom и назовите новую копию digit_top.
Этот мувиклип будет практически идентичен клипу digit_bottom , за исключением того, что маска будет отображать верхнюю половину графики вместо нижней.
Удалите графику, которая в данный момент находится на слое маски . Скопируйте скругленный прямоугольник и снова вставьте его на слой маски . На этот раз выберите нижнюю половину и удалите ее.
Единственная другая настройка, которую вы можете сделать здесь, это настроить цвет текста и затенение прямоугольника с закругленными углами фона. Я сделал графику в своем клипе digit_top немного темнее, чтобы имитировать свет, идущий сверху.
Шаг 7: Создайте Digit MovieClip
Создайте новый мувиклип с именем «Digit». Перетащите MovieClips digit_top и digit_bottom и поместите их в 0,0. Дайте им имена экземпляров «top1» и «bottom1».
Теперь скопируйте оба фрагмента ролика ( digit_top и digit_bottom ), создайте новый слой и вставьте копию каждого из них. Назовите новые копии «top2» и «bottom2».
Теперь у вас должно быть 4 MovieClips внутри вашего Digit MovieClip: 2 копии digit_top и 2 копии digit_bottom . Я объясню, почему мы настраиваем это следующим образом.
Шаг 8: Стратегия анимации
Нам нужно немного поработать над анимацией, чтобы получить эффект переворачивания чисел, который мы хотим. Взгляните на диаграмму ниже нашего Digit MovieClip (я рендерил его в 3D только для того, чтобы вы могли легче видеть слои):
Шаг анимации 1:
Мы начинаем с клипа bottom2, перевернутого вверх дном (используя свойство scaleY ) и расположенного за клипом top2 . На данный момент 2 видимых клипа это top2 и bottom1 . Числа на этих двух клипах соответствуют друг другу, поэтому они образуют полную цифру.
Шаг анимации 2:
Теперь мы перевернем клип top2 к центру цифры. В этой точке шкала Y будет равна нулю, поэтому клип не будет виден. В то же время, мы также переворачиваем клип bottom2 , но этот перевернем до самого конца. Так как он находится за top2 , он не будет отображаться, пока не переместится за полпути. Теперь 2 видимых клипа — это top1 и bottom1 . Числа на этих двух клипах не совпадают, но это нормально, потому что этот шаг длится недолго.
Шаг 3 анимации:
Зажим top2 остается в центре, а bottom2 продолжает падать до самого дна. Как только это на месте, числа на видимых клипах ( top1 и bottom2 ) снова совпадают, чтобы сформировать полную цифру.
Шаг анимации 4:
На этом этапе мы будем ретранслировать и сбрасывать позиции 2 скрытых клипов, чтобы подготовиться к следующему броску. Обратите внимание, что клипы находятся в тех же позициях, что и Шаг 1, только в обратном порядке.
Шаг 9: создай муви клип
Теперь, когда мы настроили отдельный Digit MovieClip, давайте создадим часы.
Создайте новый мувиклип на сцене «Часы» с именем экземпляра «часы». Внутри нового MovieClip поместите 9 копий вашего Digit MovieClip; 2 для секунд, 2 для минут, 2 для часов и 3 для дней. Дайте каждой цифре имя экземпляра. Слева направо назовите их «digit0», «digit1», «digit2» и так далее.
Добавьте несколько двоеточий для разделения фрагментов ролика и надписей для каждого раздела. Дизайн зависит от вас. Я добавил темный скругленный прямоугольник в качестве фона для моих часов.
Наконец, добавьте динамическое текстовое поле с именем «t_date». Здесь мы покажем целевую дату, до которой отсчитываются часы. Не забудьте вставить шрифт для этого текстового поля, если вы не используете системный шрифт.
Шаг 10: Создайте цифровой класс
Создайте новый файл Actionscript с именем «Digit.as» и добавьте этот код, чтобы создать пустую оболочку для класса:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
package {
import flash.display.MovieClip;
public class Digit extends MovieClip {
private const TOP:int = 0;
private const BOTTOM:int = 1;
private var _currentDigit:Array;
private var _nextDigit:Array;
private var _number:String = «0»;
// CONSTRUCTOR
public function Digit() {
_currentDigit = new Array( top1, bottom1 );
_nextDigit = new Array( top2, bottom2 );
}
}
}
|
Это пока мало что делает. У нас есть пара массивов для хранения двух наборов мувиклипов digit_top и digit_bottom . Я установил 2 константы, TOP и BOTTOM, чтобы отслеживать верхний и нижний клипы в этих массивах. Переменная _number будет содержать цифру, отображаемую в любой момент времени.
(Примечание: я использую подчеркивание в именах переменных для обозначения приватных переменных.)
Найдите свой Digit MovieClip в библиотеке и назначьте ему этот класс в настройках Linkage.
Шаг 11: Импортируйте библиотеку TweenLite
Мы собираемся использовать библиотеку TweenLite для анимации нашего Digit MovieClip.
Загрузите версию библиотеки TweenLite для AS3 здесь .
Поместите папку ‘com’ в тот же каталог, что и ваш основной файл Flash (или в исходный путь, если вы установили другой путь к классу ).
Добавьте эти две строки вверху вашего класса Digit , чуть ниже импорта MovieClip:
1
2
|
import com.greensock.*
import com.greensock.easing.*
|
Мы едва ли расскажем о том, что TweenLite может сделать в этом уроке. Для получения дополнительной информации ознакомьтесь с документацией TweenLite .
Шаг 12: Запрограммируйте анимацию flipTo
Добавьте эту функцию в ваш класс Digit :
01
02
03
04
05
06
07
08
09
10
|
public function flipTo(num:String):void {
_number = num;
_nextDigit[TOP].t_num.text = num;
_nextDigit[BOTTOM].t_num.text = num;
// flip down the top of the digit to the halfway point
TweenLite.to(_currentDigit[TOP], .15, {scaleY: 0, ease: Linear.easeNone});
// flip the next digit bottom down
TweenLite.to(_nextDigit[BOTTOM], .3, {scaleY:1, onComplete: flipComplete, ease: Bounce.easeOut});
}
|
Вот что происходит, строка за строкой:
- Эта функция принимает строку, которая будет содержать цифру, к которой мы будем перелистывать. Первая строка просто устанавливает переменную _number для хранения этой цифры.
- Затем мы устанавливаем текстовые поля в TOP и BOTTOM MovieClips в нашем массиве _nextDigit для отображения той же цифры.
- Затем мы используем TweenLite для анимации свойства scaleY TOP MovieClip _currentDigit до 0. Это дает эффект, что он «падает» к центру цифры.
- Последняя строка — это другая анимация, на этот раз анимирующая нижний клип _nextDigit от верха цифры вниз до низа. Опять же, мы используем свойство scaleY для имитации этого эффекта, но на этот раз от -1 до 1. Поскольку его анимация удваивается вдвое больше, чем клип TOP, мы даем ему удвоенное количество времени (0,3 секунды вместо 0,15). , Когда эта анимация заканчивается, она вызывает функцию flipComplete. Мы напишем эту функцию на следующем шаге.
Посмотрите еще раз на диаграмму в шаге 8, если вы не уверены в анимации здесь.
Шаг 13: Добавьте функцию flipComplete ()
Добавьте эту функцию в класс Digit чуть ниже функции flipTo :
1
2
3
4
5
6
7
8
9
|
private function flipComplete():void {
// swap digits
var next:Array = _currentDigit;
_currentDigit = _nextDigit;
_nextDigit = next;
// reset layering
reset();
}
|
После завершения анимации мы запустим эту функцию. Он заменяет массивы _currentDigit и _nextDigit . После того, как это сделано, он вызывает функцию с именем «reset», чтобы сбросить слои клипа и позиции для следующего броска. Давайте напишем эту функцию сейчас.
Шаг 14: Добавьте функцию сброса ()
Добавьте эту функцию в класс Digit :
1
2
3
4
5
6
7
8
|
private function reset():void {
addChild(_nextDigit[BOTTOM]);
addChild(_currentDigit[TOP]);
// flip up the next bottom to be behind the current top
_nextDigit[BOTTOM].scaleY = -1;
_nextDigit[TOP].scaleY = 1;
}
|
Первые две строки в этой функции выводят нижнюю _nextDigit, а затем верхнюю часть _currentDigit в верхнюю часть списка отображения. Я обычно просто использую addChild (), чтобы сделать это, потому что он требует меньше набирать, чем setChildIndex () .
После того, как клипы перенастроены, мы устанавливаем свойства scaleY, чтобы они были готовы к следующему флипу. Это означает изменение _nextDigit [BOTTOM] с 1 на -1 и _nextDigit [TOP] с 0 на 1.
Опять же, проверьте схему в шаге 8, если вы заблудились.
Шаг 15: добавь в конструктор
Одна вещь, которую мы забыли сделать, это правильно расположить клипы для первой анимации сальто. Это легко сделать, добавив вызов функции reset прямо в конструкторе класса Digit:
1
2
3
4
5
6
7
|
// CONSTRUCTOR
public function Digit() {
_currentDigit = new Array( top1, bottom1 );
_nextDigit = new Array ( top2, bottom2 );
reset();
}
|
Шаг 16: Добавьте функцию number ()
Последнее, что нам понадобится в нашем классе Digit, — это способ доступа к закрытой переменной _number извне класса. Мы добавим простую функцию доступа:
1
2
3
|
public function get number():String {
return _number;
}
|
Шаг 17: создайте класс часов
Создайте новый файл ActionScript с именем «Clock.as». Вставьте в этот код:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
package {
import flash.display.MovieClip;
import flash.events.TimerEvent;
import flash.media.Sound;
import flash.utils.Timer;
public class Clock extends MovieClip {
private var _clockTimer:Timer;
private var _targetDate:Date;
// CONSTRUCTOR
public function Clock() {
}
}
}
|
Еще немного здесь. Просто импортируем некоторые классы, которые нам понадобятся. У меня также есть несколько личных переменных. _clockTimer будет отсчитывать секунды для нас, а _targetDate будет хранить дату, до которой мы ведем обратный отсчет.
Шаг 18: Добавьте функцию set ()
Добавьте эту функцию в класс Clock чуть ниже конструктора:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
// set the target date and start the countdown timer
public function set(date:Date):void {
_targetDate = date;
_clockTimer = new Timer(1000) // tick every second (1000 milliseconds)
_clockTimer.addEventListener(TimerEvent.TIMER, update);
_clockTimer.start();
// display the target date above the clock
t_date.text = _targetDate.toLocaleString().toUpperCase();
// update the clock once here so it starts with the correct time
update();
}
|
Это функция, которую мы будем использовать для установки целевой даты для часов. Он принимает дату (конечно) и назначает ее переменной _targetDate . Затем он создает наш _clockTimer . _ClockTimer будет вызывать функцию обновления один раз в секунду для обновления цифр.
После запуска таймера функция устанавливает текст t_date с целевой датой. Функция toLocaleString () обеспечивает отображение даты в местном часовом поясе пользователя.
Последняя строка этой функции вызывает update один раз, чтобы установить часы на нужное время. В противном случае он будет отображать «000 00:00:00» в течение одной секунды до первого события таймера.
Шаг 19: Добавьте функцию update ()
Эта функция немного длинная, потому что именно там выполняется большая часть работы. Добавьте его в свой класс часов:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
private function update(e:TimerEvent = null):void {
var now:Date = new Date();
// find the difference (in ms) between the target and now
var diff:Number = _targetDate.valueOf() — now.valueOf();
if(diff <=0){
// TIME’S UP!
// do something cool here
_clockTimer.stop();
_clockTimer.removeEventListener(TimerEvent.TIMER, update);
diff = 0;
}
// convert to seconds
diff = Math.round(diff/1000);
// number of days
var days:int = Math.floor(diff/ (24 * 60 * 60));
diff -= days*(24 * 60 * 60 );
// number of hours
var hours:int = Math.floor(diff / (60 * 60))
diff -= hours*60 * 60;
// number of minutes
var min:int = Math.floor(diff/ 60);
diff -= min*60;
// seconds are all that remain
var sec:int = diff;
// create an array of strings to hold the number for each value
var diffArr:Array = new Array(String(days), String(hours), String(min), String(sec));
var diffString:String = «»
var len:int = 3;
for each(var s:String in diffArr){
// pad the string with a leading zero if needed
while(s.length < len){
s = «0»+s;
}
len = 2;
diffString += s;
}
// go through each character in the diffString and set the corresponding digit
for(var i:int = 0; i< diffString.length; i++){
if(diffString.substr(i, 1) != this[«digit»+i].number){
this[«digit»+i].flipTo(diffString.substr(i, 1));
}
}
}
|
Эта функция принимает TimerEvent в качестве своего параметра. Значением по умолчанию для этого параметра является ноль . Это позволяет нам вызывать функцию без отправки параметра, как мы делаем в функции set .
Первая строка этой функции получает текущую дату и время как объект Date. Далее мы находим разницу между текущей датой и целевой датой (строка 37). Если разница равна 0 или меньше, то она превышает целевую дату, поэтому мы останавливаем _clockTimer (строки 38-44).
Поскольку разница во времени между текущим и целевым значением рассчитывается в миллисекундах, нам необходимо преобразовать ее в удобное для чтения отображение дней, часов, минут и секунд (строки 46–62). Математика здесь довольно проста, если вы знаете, что в секунду 1000 миллисекунд, 60 секунд в минуту, 60 минут в час и 24 часа в день.
В строке 65 мы храним все эти значения как элементы в массиве. Начиная со строки 68, мы перебираем каждый элемент и добавляем его в строку символов с именем diffString. При этом мы также добавляем ведущие нули, где это необходимо (строка 71). Поэтому, если бы наши значения для часов были 30 дней, 5 часов, 56 минут и 6 секунд, diffString выглядела бы так: «030055606».
Последнее, что делает эта функция, это перебирает символы в diffString (используя метод charAt () ). Для каждого символа в строке мы проверяем, отличается ли он от числа, отображаемого в данный момент на соответствующей цифре. Это легко из-за того, как мы назвали наши экземпляры цифр. Если число не совпадает с отображаемым в данный момент, мы сообщаем, что эта цифра переходит на число в diffString .
Шаг 20: добавь звук
Найдите (или создайте) хороший тикающий звук, который будет звучать при каждом обновлении часов. Импортируйте его в библиотеку вашего Flash-файла и установите имя класса «TickSound» в настройках Linkage.
Добавьте переменную _tickSound в начало класса Clock чуть ниже двух других переменных:
1
2
3
|
private var _clockTimer:Timer;
private var _targetDate:Date;
private var _tickSound:Sound = new TickSound();
|
И воспроизвести звук внутри функции обновления :
1
|
_tickSound.play();
|
Шаг 21: Добавьте основной класс документа
Наш таймер обратного отсчета завершен, нам просто нужен какой-то способ установить целевую дату. Создайте новый файл Actionscript с именем «Main.as» с этим кодом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
// set the target date for the clock
var targetDate:Date = new Date();
targetDate.setTime( Date.UTC(2010, 4, 28, 20, 00) );
clock.set(targetDate);
}
}
}
|
Все, что это делает, это устанавливает целевую дату для экземпляра Clock на сцене. Я использую setTime () и Date.UTC () для преобразования даты в универсальный тайм-код. Таким образом, дата будет правильной, когда она будет преобразована обратно в местное время на компьютере пользователя. Также помните, что месяцы начинаются с нуля. Итак, месяц 4 на самом деле май, а не апрель.
В вашем Flash-файле установите класс Document в значение «Main».
Если вам нужно освежить в использовании класса документов, ознакомьтесь с этим кратким советом .
Шаг 22: Тест
Проверьте свой фильм сейчас, и все должно работать. Попробуйте изменить целевую дату в классе Main и посмотрите, как меняется обратный отсчет.
Один потенциальный недостаток того, как мы это настроили, заключается в том, что целевая дата жестко запрограммирована в нашем SWF. Это хорошо, но было бы здорово, если бы мы могли динамически загружать дату, чтобы мы могли использовать обратный отсчет для разных вещей.
Давайте посмотрим, что мы можем сделать с этим …
Шаг 23. Создайте файл XML
Создайте новый XML-файл в той же папке, что и ваш Flash-файл, с именем «targetDate.xml» (XML-файл — это просто текстовый файл). Добавьте это в файл XML:
1
2
3
4
5
6
7
|
<targetDate>
<year>2011</year>
<month>3</month>
<day>25</day>
<hour>20</hour>
<minute>21</minute>
</targetDate>
|
Использование этого формата для нашей целевой даты довольно раздуто (разметки больше, чем фактических данных), но это будет очень понятно для целей этого урока.
Шаг 24: Загрузите XML
Теперь давайте внесем некоторые изменения в наш класс основного документа. Замените все в этом файле этим кодом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package {
import flash.display.MovieClip;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
public class Main extends MovieClip {
// CONSTRUCTOR
public function Main() {
// load the XML
var xmlLoader:URLLoader = new URLLoader();
xmlLoader.addEventListener(Event.COMPLETE, onDataLoaded);
xmlLoader.load( new URLRequest(«targetDate.xml») );
}
}
}
|
Вы заметите, что мы импортировали некоторые дополнительные классы, чтобы помочь нам загрузить файл XML. В функции конструктора мы создаем новый экземпляр URLLoader для загрузки файла для нас. Мы прикрепляем прослушиватель событий, который будет вызывать функцию с именем onDataLoaded после завершения загрузки файла.
Шаг 25: Добавьте функцию onDataLoaded ()
Добавьте эту функцию в основной класс:
1
2
3
4
5
6
7
8
|
private function onDataLoaded(e:Event):void {
var xml:XML = new XML(e.target.data);
var targetDate:Date = new Date();
targetDate.setTime(Date.UTC(int(xml.year), int(xml.month), int(xml.day), int(xml.hour), int(xml.minute) ));
clock.set(targetDate);
}
|
Эта функция создает новый объект XML из файла, который мы загрузили. Затем мы создаем новый объект Date из значений в XML. Мы снова используем setTime () и Date.UTC () для преобразования даты в универсальный тайм-код. Последняя строка такая же, как и раньше, она просто отправляет целевую дату нашему экземпляру Clock.
Шаг 26: Завершение
Это довольно много для этого. Есть несколько улучшений, которые вы могли бы сделать, хотя:
- В зависимости от того, для чего вы используете обратный отсчет, вы можете захотеть сделать что-то особенное для пользователя, когда обратный отсчет достигнет нуля. Вы добавили бы это к классу Clock в части функции обновления, которая проверяет, находится ли таймер на нуле.
- Как я уже упоминал, формат нашего XML довольно расточительный. Вы можете просто передать дату в виде строки через FlashVars , использовать другой формат данных (например, JSON ) или просто переформатировать XML, чтобы сделать его более компактным.
Удачи! Как всегда, оставьте комментарий и дайте мне знать, что вы думаете.