Статьи

Рисование красивых компонентов с помощью кода — Made Easy

Большинство программистов никогда не касались дизайна. Если вы всегда хотели красивую графику в своих проектах Flash, но никогда не знали, как использовать Adobe Photoshop или Illustrator, этот учебник для вас. Приятная графика может быть полностью создана с помощью кода. Рисование графики для компонентов с кодом даже имеет несколько преимуществ.

Это руководство требует, чтобы вы знали, как выполнять базовую анимацию с помощью Greensock TweenLite или TweenMax. Вы можете скачать последнюю версию TweenLite с веб-сайта Greensock . Я буду использовать Flash CS5 Professional для завершения этого проекта. Вы можете использовать любую Flash IDE, например, FlashDevelop или FDT . Это также помогло бы иметь справедливое понимание наследования .

Прежде чем мы начнем, давайте посмотрим, что мы будем изучать и почему это важно. При разработке Flash-приложений существует множество способов создания графики. Вы можете использовать внешний инструмент, такой как Adobe Illustrator, или рисовать сложную графику с помощью Adobe Photoshop. Вы даже можете использовать 3D-утилиты, такие как Cinema 4D, для создания потрясающих 3D-растровых изображений для импорта во Flash. Эти методы отлично подходят для создания хорошо спроектированного статического приложения — статическое значение не очень динамично или, в большинстве случаев, вообще не динамично .

Если вы используете один из вышеперечисленных методов для создания графики для Flash, вам может быть немного трудно изменить размер графики без некоторой потери качества или, что еще хуже, искажений. С солнечной стороны, вы можете импортировать векторный файл, созданный с помощью Adobe Illustrator, в ваше приложение Flash и масштабировать изображение бесконечно, не искажая изображение — но когда вы захотите изменить общее соотношение изображения, вы заметите, что ваше изображение становится немного деформированным.

Что мы собираемся сделать сегодня, так это узнать, насколько эффективно можно рисовать графику нашего приложения с помощью кода. Мы собираемся создать красивую анимированную кнопку, которая полностью генерируется с помощью ActionScript.

Прежде чем мы начнем, я хотел бы обсудить важность планирования вашего приложения и ответить на простые вопросы, чтобы решить, рисовать ли графику с помощью кода или вручную (например, с помощью Adobe Photoshop). Чтобы помочь нам ответить на вопросы ниже, давайте сначала рассмотрим плюсы и минусы рисования с помощью кода.

  • Переносимость: графические объекты , написанные в коде, легко импортировать в другие проекты, поскольку они существуют в виде файлов AS. Нет необходимости копировать и вставлять изображение в новый проект Flash или импортировать изображение в библиотеку. Просто импортируйте соответствующие классы.
  • Простота: графика, нарисованная с помощью кода, выполняется с использованием векторов, поэтому они имеют тонкий и упрощенный вид, который приятно смотреть и на процессор.
  • Согласованность: когда вы хотите изменить размеры вашей графики, они не будут искажаться (если только это не тот эффект, которого вы добились), и они будут поддерживать качество при масштабировании. Например, растровые изображения становятся пикселированными по мере увеличения или увеличения. Векторные изображения не теряют качества и не становятся пиксельными. Вы можете бесконечно увеличивать векторное изображение, и оно никогда не потеряет качество. Вы также можете быть уверены, что ваша графика по-прежнему выглядит потрясающе на экранах с более высоким разрешением. По мере появления технологических достижений и новых разрешений экрана ваша графика, скорее всего, сможет обеспечивать хорошее качество, в отличие от растровых изображений, которые могут выглядеть не так хорошо.
  • Динамика: Наряду со способностью изменять размеры и масштабировать графику, вы также можете перекрашивать сегменты изображения или компонента с очень небольшим количеством строк кода. Изменить свойства графики еще никогда не было так просто. Что ты говоришь? Вы больше не хотите, чтобы рамка вашего текстового поля имела закругленные края? Здесь нет проблем. Я просто поменяю одну строку кода … и мы готовы идти!
  • Точность. Хотя многие графические приложения имеют функции, которые упрощают позиционирование графических сегментов, вам будет проще вносить изменения и позиционировать содержимое графики с помощью кода. Вы можете легко центрировать один сегмент внутри другого, когда размер родительского сегмента меняет размер, или написать алгоритм для обработки отображения сегмента при его изменении размера. Подсказка Подсказка.
  • Детали: векторная графика, нарисованная с помощью кода, может быть очень подробной, но слишком большая детализация может существенно повлиять на производительность наших приложений. Хотя это может быть возможно, вы не обнаружите, что я пытаюсь нарисовать подробные квадратные штаны Спанч Боба с кодом. Нечто подобное должно быть сделано с помощью Adobe Illustrator. Также векторы могут быть не такими подробными, как сложная графика, нарисованная в Adobe Photoshop. Векторы, как правило, содержат файл меньшего размера, чем обычное растровое изображение. Векторы являются связанными точками, а растровые изображения представлены попиксельно. Но если вы преобразуете растровое изображение в векторное изображение, размер файла нового вектора окажется намного больше, чем исходное растровое изображение. Тем не менее, вы не сможете нарисовать сложный портрет с кодом. Вместо этого используйте Adobe Photoshop.
  • Ограничения: могут быть случаи, когда вы хотите использовать графику на разных носителях. Извините, что говорю, но если вы не сможете поместить свою динамическую графику в ту Java-игру, над которой вы работали, или в приложение Objective-C, которое вы только что начали. Существуют способы визуализации изображений с использованием Flash, но вы определенно потеряете все динамические возможности, предоставляемые кодом.
  • Сложность: Нельзя сказать, что вам будет смешно рисовать графику с помощью кода, но в зависимости от того, что вы рисуете, вы, скорее всего, обнаружите, что весь процесс немного сложнее, чем использование программного обеспечения, предназначенного для этой цели. Не ожидайте, что рисование с кодом будет легкой задачей. Это действительно все зависит от ожидаемого результата.

Теперь вы должны легко найти ответы на следующие вопросы. Перед началом проекта вам нужно задать себе три вопроса:

  1. Требуется ли, чтобы ваше приложение было статичным или динамичным?

    1. статический
    2. динамический
  2. Графика или компоненты в вашем приложении должны быть эксклюзивными для Flash или согласованными на нескольких платформах?

    1. Только Flash Platform
    2. Несколько платформ
  3. Графический дизайн вашего приложения более детальный или достаточно простой?

    1. Относительно простой (основные геометрические фигуры)
    2. Умеренный (креативный дизайн с простыми формами)
    3. Сложные (чрезмерно сложные шедевры)

Теперь проверьте ваши результаты теста. Если результаты вашего теста очень похожи на приведенные ниже, то какие методы, которые мы готовимся изучать, для вас?

  1. # 2
  2. # 1
  3. № 1 или № 2

Если ваши результаты не похожи на результаты выше, вы можете рассмотреть другие методы. Но для остальных из нас, давайте продолжим.

Итак, вот сценарий. Клиент только что попросил нас создать анимированную кнопку для своего сайта Flash и приложения AIR. Кнопка должна соответствовать теме научной фантастики на сайте клиента. Высота кнопки может быть фиксированного размера, но кнопка должна быть приспособлена для различной ширины. Кнопка должна иметь состояние переключения, которое имеет явную разницу между ее активным и неактивным состояниями. Наконец, кнопка должна иметь API, с которым легко работать.

Теперь, когда мы знаем, что у нас есть этот тематический компонент для создания, нам нужно подумать о том, как мы собираемся его создавать. Что ж, мы знаем, что будем рисовать много графики с помощью кода, чтобы компонент был легко и эффективно редактироваться. Почему бы не начать с базового класса, который добавит высокоуровневую функциональность в топ API рисования Flash?

Мы не хотим рисовать графику снова и снова путем написания одних и тех же строк кода, поэтому у нас будет класс с определенными свойствами и методами, специально предназначенными для повторного использования кода для нас. Мы расширим этот класс, чтобы создать основные фигуры, которые нам понадобятся для нашей кнопки.

Теперь пришло время добраться до него! Создайте новый класс с именем EditableShape и добавьте следующие классы в путь к классам.

1
2
3
4
5
6
7
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.Event;
import flash.geom.Matrix;
import flash.display.BitmapData;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

Теперь создайте объявление класса. Класс должен расширять класс flash.display.Sprite .

1
public class EditableShape extends Sprite {

Создайте следующие переменные (свойства) перед конструктором класса.

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
private var _fillGradientType:String;
private var _fillSpreadMethod:String;
private var _fillColors:Array;
private var _fillAlphas:Array;
private var _fillRatios:Array;
private var _fillGradientWidth:Number;
private var _fillGradientHeight:Number;
private var _fillGradientRotation:Number;
private var _fillTx:Number;
private var _fillTy:Number;
private var _lineGradientType:String;
private var _lineSpreadMethod:String;
private var _lineThickness:Number;
private var _lineColors:Array;
private var _lineAlphas:Array;
private var _lineRatios:Array;
private var _lineGradientWidth:Number;
private var _lineGradientHeight:Number;
private var _lineGradientRotation:Number;
private var _lineTx:Number;
private var _lineTy:Number;
 
private var _width:Number;
private var _height:Number;
private var _matchGradientSize:Boolean;
private var _bitmapData:BitmapData;
private var _useBitmapFill:Boolean;
private var _pixelHinting:Boolean;
 
protected var fillGradientBox:Matrix;
protected var lineGradientBox:Matrix;

В конструкторе класса добавьте следующий код, и я объясню, что мы только что сделали.

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
super();
_width = 100;
_height = 100;
_matchGradientSize = true;
_fillGradientType = «linear»;
_fillSpreadMethod = «pad»;
_fillColors = [ 0xFFFFFF, 0x000000 ];
_fillAlphas = [ 1, 1 ];
_fillRatios = [ 1, 255 ];
_fillGradientWidth = _width;
_fillGradientHeight = _height;
_fillGradientRotation = 0;
_fillTx = 0;
_fillTy = 0;
_lineGradientType = «linear»;
_lineSpreadMethod = «pad»;
_lineThickness = .1;
_lineColors = [ 0xFFFFFF, 0x000000 ];
_lineAlphas = [ 1, 1 ];
_lineRatios = [ 1, 255 ];
_lineGradientWidth = _width;
_lineGradientHeight = _height;
_lineGradientRotation = 0;
_lineTx = 0;
_lineTy = 0;
_useBitmapFill = false;
_pixelHinting = true;
fillGradientBox = new Matrix();
lineGradientBox = new Matrix();
init();

Основной функцией конструктора является создание экземпляров всех свойств, которые мы определили. Мы также дали каждому свойству значение по умолчанию. Вы сможете увидеть результат этих значений позже. Каждое свойство, начинающееся с подчеркивания, получит доступ на чтение / запись к внешнему коду. Эти свойства не являются общедоступными, потому что мы хотим реагировать на изменение стоимости каждого конкретного свойства. Мы сделаем это в методах установки, которые мы создадим позже.

Каждое свойство основано на определенном значении, которое должно быть передано в любой из методов graphics объекта. Одним из примеров является свойство _fillGradientColors . Это массив uints которые будут переданы в метод graphics.beginGradientFill . Другим примером является свойство _pixelHinting . Он будет передан в параметр pixelHinting метода pixelHinting .

Последняя строка конструктора вызывает метод init . Метод init очень прост. Он вызывает метод update .

Метод update вероятно, является наиболее важным методом в классе EditableShape . Основная цель этого метода — очистить любую существующую графику и перерисовать ее на основе последних данных. Мы будем вызывать этот метод каждый раз, когда свойство будет изменено, чтобы изменения могли быть сразу же видны пользователю, если это необходимо. Создайте метод update .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
protected function update():void {
     
    if ( _matchGradientSize ) {
         
        _lineGradientWidth = _width;
        _lineGradientHeight = _height;
        _fillGradientWidth = _width;
        _fillGradientHeight = _height;
    }
     
    lineGradientBox.createGradientBox( lineGradientWidth, lineGradientHeight, toRadians( lineGradientRotation ), lineTx, lineTy );
    fillGradientBox.createGradientBox( fillGradientWidth, fillGradientHeight, toRadians( fillGradientRotation ), fillTx, fillTy );
    super.graphics.clear();
    super.graphics.lineStyle( lineThickness, 0, 0, pixelHinting );
    super.graphics.lineGradientStyle( lineGradientType, lineColors, lineAlphas, lineRatios, lineGradientBox, lineSpreadMethod );
    if ( !_bitmapData || !_useBitmapFill ) super.graphics.beginGradientFill( fillGradientType, fillColors, fillAlphas, fillRatios, fillGradientBox, fillSpreadMethod )
    else super.graphics.beginBitmapFill( _bitmapData );
     
    draw();
}

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

Мы будем переопределять метод get graphics чтобы мы могли запретить доступ к этому свойству для внешнего кода. Мы вообще не хотим, чтобы какой-либо внешний код вмешивался в нашу графику. Поскольку мы собираемся переопределить метод get graphics , нам потребуется доступ к методу из подкласса. Это достигается через super объект. Мы также создадим защищенный метод с именем getGraphics позже. Этот метод необходим для предоставления подклассам доступа к graphics объекту без предоставления доступа к объекту внешнему коду.

В заключение, метод обновления повторно инициализирует графику и делает это на основе текущих значений свойств в экземпляре EditableShape . Последняя строка кода вызывает метод draw который является абстрактным методом в классе EditableShape . Метод должен быть переопределен подклассом и предназначен для рисования конкретной фигуры с использованием graphics объекта в его реализации.

Создать метод рисования.

1
2
3
4
protected function draw():void {
     
    // Abstract
}

Опять же, метод draw — это простой абстрактный метод, который мы можем пока игнорировать.

Нам нужно будет скопировать свойства одной фигуры на другую. Также могут быть случаи, когда вам нужен полный клон определенной формы. Мы создадим два метода, чтобы выполнить это для нас. Это сэкономит нам много времени в долгосрочной перспективе. Вместо того, чтобы всегда переписывать код, мы просто вызываем соответствующий метод, который копирует или клонирует фигуру. Создайте методы copy и clone .

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
public function copy( shape:EditableShape ):void {
     
    _width = shape.width;
    _height = shape.height;
    _matchGradientSize = shape.matchGradientSize;
    _fillGradientType = shape.fillGradientType;
    _fillSpreadMethod = shape.fillSpreadMethod;
    _fillColors = shape.fillColors;
    _fillAlphas = shape.fillAlphas;
    _fillRatios = shape.fillRatios;
    _fillGradientWidth = shape.fillGradientWidth;
    _fillGradientHeight = shape.fillGradientHeight;
    _fillGradientRotation = shape.fillGradientRotation;
    _fillTx = shape.fillTx;
    _fillTy = shape.fillTy;
    _lineGradientType = shape.lineGradientType;
    _lineSpreadMethod = shape.lineSpreadMethod;
    _lineThickness = shape.lineThickness;
    _lineColors = shape.lineColors;
    _lineAlphas = shape.lineAlphas;
    _lineRatios = shape.lineRatios;
    _lineGradientWidth = shape.lineGradientWidth;
    _lineGradientHeight = shape.lineGradientHeight;
    _lineGradientRotation = shape.lineGradientRotation;
    _lineTx = shape.lineTx;
    _lineTy = shape.lineTy;
    _useBitmapFill = shape.useBitmapFill;
    if ( _bitmapData ) _bitmapData = shape.bitmapData.clone();
    if ( filters ) filters = shape.filters;
    alpha = shape.alpha;
    update();
}
 
public function clone():EditableShape {
     
    var c:Class = Class( getDefinitionByName( getQualifiedClassName( this ) ) );
    var shape:EditableShape = new c();
    shape.width = _width;
    shape.height = _height;
    shape.matchGradientSize = _matchGradientSize;
    shape.fillGradientType = _fillGradientType;
    shape.fillSpreadMethod = _fillSpreadMethod;
    shape.fillColors = _fillColors;
    shape.fillAlphas = _fillAlphas;
    shape.fillRatios = _fillRatios;
    shape.fillGradientWidth = _fillGradientWidth;
    shape.fillGradientHeight = _fillGradientHeight;
    shape.fillGradientRotation = _fillGradientRotation;
    shape.fillTx = _fillTx;
    shape.fillTy = _fillTy;
    shape.lineGradientType = _lineGradientType;
    shape.lineSpreadMethod = _lineSpreadMethod;
    shape.lineThickness = _lineThickness;
    shape.lineColors = _lineColors;
    shape.lineAlphas = _lineAlphas;
    shape.lineRatios = _lineRatios;
    shape.lineGradientWidth = _lineGradientWidth;
    shape.lineGradientHeight = _lineGradientHeight;
    shape.lineGradientRotation = _lineGradientRotation;
    shape.lineTx = _lineTx;
    shape.lineTy = _lineTy;
    shape.useBitmapFill = _useBitmapFill;
    if ( _bitmapData ) shape.bitmapData = _bitmapData.clone();
    shape.filters = filters;
    shape.alpha = alpha;
    return shape;
}

Метод copy принимает параметр shape и устанавливает все свойства, равные свойствам текущего EditableShape .

Мы не будем использовать метод clone в этом уроке, но я просто добавил его в качестве дополнительного бонуса. Метод возвращает клонированную копию текущего класса EditableShape .

Последнее, что нам нужно сделать, это предоставить доступ на чтение / запись ко всем основным свойствам фигуры. Напишите следующие методы получения и установки в классе EditableShape .

001
002
003
004
005
006
007
008
009
010
011
012
013
014
+015
016
+017
018
019
020
021
022
023
024
025
026
027
028
029
+030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
+055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
public override function set width(value:Number):void {
     
    _width = value;
    update();
}
 
public override function get width():Number {
     
    return _width;
}
 
public override function set height(value:Number):void {
     
    _height = value;
    update();
}
 
public override function get height():Number {
     
    return _height;
}
 
public function set fillGradientType( value:String ):void {
     
    switch ( value.toLowerCase() ) {
         
        case «linear» :
        case «radial» :
            _fillGradientType = value.toLowerCase();
            update();
            break;
        default :
            //Do nothing
    }
}
 
public function get fillGradientType():String {
     
    return _fillGradientType;
}
 
public function set fillSpreadMethod(value:String):void {
     
    switch ( value.toLowerCase() ) {
         
        case «pad» :
        case «reflect» :
        case «repeat» :
            _fillSpreadMethod = value.toLowerCase();
            update();
        default :
            //Do nothing
    }
}
 
public function get fillSpreadMethod():String {
     
    return _fillSpreadMethod;
}
 
public function set fillColors(array:Array):void {
     
    var a:Array = [];
     
    for each( var color:uint in array ) {
         
        if ( color is uint ) {
             
            a.push( color );
        }
    }
     
    _fillColors = a;
    update();
}
 
public function get fillColors():Array {
     
    return _fillColors;
}
 
public function set fillAlphas( array:Array ):void {
     
    var a:Array = [];
     
    for each( var nAlpha:Number in array ) {
         
        if ( nAlpha is Number) {
             
            a.push( nAlpha );
        }
    }
     
    _fillAlphas = a;
    update();
}
 
public function get fillAlphas():Array {
     
    return _fillAlphas;
}
 
public function set fillRatios(array:Array):void {
     
    var a:Array = [];
     
    for each(var ratio:Number in array) {
         
        if (ratio is int) {
             
            a.push(ratio);
        }
    }
     
    _fillRatios = a;
    update();
}
 
public function get fillRatios():Array {
     
    return _fillRatios;
}
 
public function set fillGradientWidth(value:Number):void {
     
    _fillGradientWidth = value;
    update();
}
 
public function get fillGradientWidth():Number {
     
    return _fillGradientWidth;
}
 
public function set fillGradientHeight(value:Number):void {
     
    _fillGradientHeight = value;
    update();
}
 
public function get fillGradientHeight():Number {
     
    return _fillGradientHeight;
}
 
public function set fillGradientRotation(degrees:Number):void {
     
    _fillGradientRotation = degrees;
    update();
}
 
public function get fillGradientRotation():Number {
     
    return _fillGradientRotation;
}
 
public function set fillTx(value:Number):void {
     
    _fillTx = value;
    update();
}
 
public function get fillTx():Number {
     
    return _fillTx;
}
 
public function set fillTy(value:Number):void {
     
    _fillTy = value;
    update();
}
 
public function get fillTy():Number {
     
    return _fillTy;
}
 
public function set lineGradientType(value:String):void {
     
    switch (value.toLowerCase()) {
         
        case «linear» :
        case «radial» :
            _lineGradientType = value.toLowerCase();
            update();
            break;
        default :
            //Do nothing
    }
}
 
public function get lineGradientType():String {
     
    return _lineGradientType;
}
 
public function set lineSpreadMethod(value:String):void {
     
    switch (value.toLowerCase()) {
         
        case «pad» :
        case «reflect» :
        case «repeat» :
            _lineSpreadMethod = value.toLowerCase();
            update();
        default :
            //Do nothing
    }
}
 
public function get lineSpreadMethod():String {
     
    return _lineSpreadMethod;
}
 
public function set lineThickness(value:Number):void {
     
    _lineThickness = value;
    update();
}
 
public function get lineThickness():Number {
     
    return _lineThickness;
}
 
public function set lineColors(array:Array):void {
     
    var a:Array = [];
     
    for each(var color:uint in array) {
         
        if (color is uint) {
             
            a.push(color);
        }
    }
     
    _lineColors = array;
    update();
}
 
public function get lineColors():Array {
     
    return _lineColors;
}
 
public function set lineAlphas(array:Array):void {
     
    var a:Array = [];
     
    for each(var nAlpha:Number in array) {
         
        if (nAlpha is Number) {
             
            a.push(nAlpha);
        }
    }
     
    _lineAlphas = a;
    update();
}
 
public function get lineAlphas():Array {
     
    return _lineAlphas;
}
 
public function set lineRatios(array:Array):void {
     
    var a:Array = [];
     
    for each(var ratio:Number in array) {
         
        if (ratio is int) {
             
            a.push(ratio);
        }
    }
     
    _lineRatios = a;
    update();
}
 
public function get lineRatios():Array {
     
    return _lineRatios;
}
 
public function set lineGradientWidth(value:Number):void {
     
    _lineGradientWidth = value;
    update();
}
 
public function get lineGradientWidth():Number {
     
    return _lineGradientWidth;
}
 
public function set lineGradientHeight(value:Number):void {
     
    _lineGradientHeight = value;
    update();
}
 
public function get lineGradientHeight():Number {
     
    return _lineGradientHeight;
}
 
public function set lineGradientRotation(degrees:Number):void {
     
    _lineGradientRotation = degrees;
    update();
}
 
public function get lineGradientRotation():Number {
     
    return _lineGradientRotation;
}
 
public function set lineTx(value:Number):void {
     
    _lineTx = value;
    update();
}
 
public function get lineTx():Number {
     
    return _lineTx;
}
 
public function set lineTy(value:Number):void {
     
    _lineTy = value;
    update();
}
 
public function get lineTy():Number {
     
    return _lineTy;
}
 
public function set matchGradientSize(value:Boolean):void {
     
    _matchGradientSize = value;
    update();
}
 
public function get matchGradientSize():Boolean {
     
    return _matchGradientSize;
}
 
public function set bitmapData( value:BitmapData ):void {
     
    _bitmapData = value;
    update();
}
 
public function get bitmapData():BitmapData {
     
    return _bitmapData;
}
 
public function set useBitmapFill( value:Boolean ):void {
     
    _useBitmapFill = value;
    update();
}
 
public function get useBitmapFill():Boolean {
     
    return _useBitmapFill;
}
 
public function set firstFillColor( value:uint ):void {
     
    _fillColors[ 0 ] = value;
    update();
}
 
public function get firstFillColor():uint {
     
    if ( _fillColors.length > 0 ) {
         
        return _fillColors[ 0 ];
    }
    else {
         
        return 0;
    }
}
 
public function set pixelHinting( value:Boolean ):void {
     
    _pixelHinting = value;
    update();
}
 
public function get pixelHinting():Boolean {
     
    return _pixelHinting;
}
 
public override function get graphics():Graphics {
     
    return null;
}
 
protected function getGraphics():Graphics {
     
    return super.graphics;
}

Все методы получения просто возвращают соответствующее значение свойства. Но для методов установки мы исправляем или отфильтровываем любой нежелательный ввод. Мы также вызываем метод update для немедленной реакции на новое значение или значения.

Большая часть нашей кнопки будет состоять из прямоугольных фигур. Если мы не хотим утомлять пользователей нашего клиента кучей хромых блочных фигур, мы захотим иметь возможность скруглить края наших прямоугольников. Класс EditableShape предназначен для расширения, что означает, что он должен иметь подклассы, которые завершают основную функцию класса. Класс EditableShape берет на себя всю тяжелую работу за нас. Все, что нам нужно сделать сейчас, это нарисовать фигуру в подклассе. Создайте новый класс с именем RectangleShape который расширяет класс EditableShape . Объявление класса должно выглядеть следующим образом.

1
public class RectangleShape extends EditableShape {

Добавьте следующие частные свойства к классу непосредственно после объявления класса.

1
2
private var _ellipseWidth:Number;
private var _ellipseHeight:Number;

Я упоминал ранее, что мы будем округлять края наших прямоугольников. Как вы, наверное, уже догадались, мы будем предоставлять доступ на чтение / запись к этим свойствам так же, как и для всех других унаследованных свойств. Это позволит нам реагировать на любые изменения свойств ellipseWidth и ellipseHeight сразу после их изменения.

Напоминание: это достигается путем вызова метода update .

Создайте конструктор класса. Задайте для _ellipseWidth и _ellipseHeight значение по умолчанию, равное 0 . Мы не хотим, чтобы края были округлены по умолчанию.

1
2
3
4
5
6
public function RectangleShape() {
     
    _ellipseWidth = 0;
    _ellipseHeight = 0;
    super();
}

Вы должны помнить, что метод draw из класса EditableShape был абстрактным методом. Это метод с пустой реализацией. Мы собираемся переопределить этот метод, чтобы дать ему надлежащую функциональность. Название метода говорит само за себя. Метод выполнит фактическое рисование фигуры.

Важное замечание: Мы вызываем конструктор базового класса super() после того, как мы присвоили значение свойствам _ellipseWidth и _ellipseHeight . Помните, что конструктор инициализирует форму, вызывая метод init который затем вызывает метод update который, наконец, вызывает метод draw . Через минуту вы увидите, что метод draw требует этих свойств. Если вы в последний раз вызовете конструктор последним, это приведет к ошибке аргумента, drawRoundRect методом drawRoundRect graphics объекта.

1
2
3
4
protected override function draw():void {
             
    getGraphics().drawRoundRect( 0, 0, width, height, _ellipseWidth, _ellipseHeight );
}

Я должен извиниться перед всеми вами, кто ожидал десять или более строк кода в методе draw . Это не тот случай, здесь. Помните, что базовый класс ( EditableShape ) выполняет большую часть работы за нас. Он обрабатывает цвета, градиенты, альфы, свойства линий и так далее. Все, что нам нужно было сделать, это нарисовать форму, и это то, что мы сделали. graphics объект больше не доступен из внешнего кода, поскольку свойство было переопределено, поэтому нам пришлось обращаться к нему через защищенный метод getGraphics . Наконец, мы вызываем метод drawRoundRect для graphics объекта и передаем соответствующие параметры.

Есть еще два метода, которые мы должны переопределить, прежде чем мы сможем завершить этот класс. Первый метод — это метод copy . Это метод из класса EditableShape который принимает EditableShape в качестве параметра и вызывает свойства родительской формы для формы параметра. Нам нужно добавить некоторые функциональные возможности для этого метода. Мы будем переопределять этот метод, но это не значит, что мы должны забыть все, что базовый класс делает с этим методом. Мы будем использовать super для вызова метода из базового класса, чтобы он наследовал свои первоначальные функциональные возможности при добавлении новых функциональных возможностей.

01
02
03
04
05
06
07
08
09
10
11
public override function copy( shape:EditableShape ):void {
     
    super.copy( shape );
     
    if ( shape is RectangleShape ) {
         
        var rect:RectangleShape = shape as RectangleShape;
        ellipseWidth = rect.ellipseWidth;
        ellipseHeight = rect.ellipseHeight;
    }
}

Можете ли вы сказать, что только что произошло? Если параметр shape является RectangleShape мы также ellipseHeight свойства ellipseWidth и ellipseHeight . Если у вас есть коллекция RectangleShapes , которую вы используете для рисования графики, и вы хотите, чтобы у них всех были одинаковые свойства ellipseWidth и ellipseHeight , это не позволит вам писать слишком много лишних строк кода.

Второй метод, который мы должны переопределить, это метод clone .

1
2
3
4
5
6
7
public override function clone():EditableShape {
     
    var shape:RectangleShape = super.clone() as RectangleShape;
    shape.ellipseWidth = _ellipseWidth;
    shape.ellipseHeight = _ellipseHeight;
    return shape;
}

Метод clone также наследует реализацию базового класса. Но с этой реализацией мы добавили свойства ellipseWidth и ellipseHeight .

Напишите следующие методы получения и установки:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function set ellipseWidth( value:Number ):void {
     
    if ( value < 0 ) value = 0;
    _ellipseWidth = value;
    update();
}
 
public function get ellipseWidth():Number {
             
    return _ellipseWidth;
}
 
public function set ellipseHeight( value:Number ):void {
     
    if ( value < 0 ) value = 0;
    _ellipseHeight = value;
    update();
}
 
public function get ellipseHeight():Number {
     
    return _ellipseHeight;
}

Мы не только предоставили доступ для чтения / записи, который необходим внешнему коду, но также мы убедились, что ellipseWidth и ellipseHeight никогда не бывают ниже нуля. Существует также немедленная реакция на эти свойства, которые устанавливаются или изменяются.

Примечание. Если вы не хотите корректировать какие-либо значения для свойств ellipseWidth или ellispeHeight , это не требуется. Может быть время, когда вы захотите, чтобы значения ellipseWidth или ellipseHeight были меньше нуля.

Теперь, когда у нас завершена первая форма. Давайте кратко рассмотрим, как выглядит наша форма при ее создании. Вам нужно будет создать класс документа и создать в нем новый экземпляр объекта RectangleShape . Добавьте новый RectangleShape на stage . Вот мой код:

1
stage.addChild( new RectangleShape() );

Давайте посмотрим на результат.

Так выглядит наша фигура по умолчанию. Не стесняйтесь поиграть с этим немного, чтобы вы почувствовали это. Попробуйте изменить цвета или изменить размер.

Мы собираемся создать своего рода пузырящийся эффект для состояния over нашей кнопки. Это значит, что нам понадобятся пузыри. Создайте класс EclipseShape . Убедитесь, что он расширяет класс EditableShape .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package {
 
    public class EllipseShape extends EditableShape {
         
        public function EllipseShape() {
             
            super();
        }
         
        protected override function draw():void {
             
            getGraphics().drawEllipse( 0, 0, width, height );
        }
    }
}

Еще раз сила наследования позволила нам перерабатывать старый код вместо того, чтобы писать один и тот же код снова и снова. Этот класс содержит только унаследованные свойства и методы. Нам пришлось переопределить метод draw , чтобы создать реальную форму затмения. Метод drawEclipse использует унаследованные свойства width и height для рисования нашей фигуры. И снова мы получаем доступ к graphics объекту, используя защищенный метод getGraphics .

Теперь у нас есть все формы, которые нам нужны, чтобы нарисовать нашу кнопку.

Создайте новый класс, который расширяет flash.display.Sprite . Вы можете позвонить в класс как угодно. Я собираюсь использовать имя MyButton . Импортируйте следующие классы.

01
02
03
04
05
06
07
08
09
10
11
12
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.filters.BlurFilter;
import flash.filters.GlowFilter;
import flash.events.MouseEvent;
import flash.filters.DropShadowFilter;
import flash.system.System;
import flash.filters.BevelFilter;
import com.greensock.TweenMax;
 
public class MyButton extends Sprite {

Определите следующие свойства.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
private var _width:Number;
private var _active:Boolean;
private var base:RectangleShape;
private var toggleBase:RectangleShape;
private var txt:TextField;
private var effectTxt:TextField;
private var toggleTxt:TextField;
private var toggleGlass:RectangleShape;
private var toggleGlassAlpha:Number;
private var ref:RectangleShape;
private var light:RectangleShape;
private var lightAlpha:Number;
private var glow:RectangleShape;
private var effectMask:RectangleShape;
private var txtMask:RectangleShape;
private var effectTxtMask:RectangleShape;
private var toggleTxtMask:RectangleShape;
private var effectContainer:Sprite;
private var circles:Array;
private var tweens:Array;
private var circleBlur:BlurFilter;
private var _toggle:Boolean;

Как и раньше, ожидается, что любое свойство, начинающееся с подчеркивания, вызовет немедленную реакцию, если оно установлено, или свойство будет доступно только для чтения.

Свойство _width будет использоваться для переопределения свойства width базового класса, и мы перерисовываем фигуру каждый раз, когда устанавливается свойство width .

Свойство active будет использоваться для смены состояний переключения кнопки позже.

Вы заметите несколько свойств RectangleShapes , несколько свойств TextFields и Sprite . Мы увидим роль каждого из этих свойств позже.

Мы будем хранить все наши пузыри в Array circles . Для повышения производительности нам нужно будет приостановить различные анимации. Для этого нам нужно будет хранить эти анимации в массиве, чтобы к ним можно было обращаться в любое время, когда они нам нужны.

Наконец, у нас есть свойство circleBlur которое представляет собой BlurFilter который должен применяться к каждому пузырю, и у нас есть свойство _toggle которое влияет на поведение кнопки.

Создайте следующие публичные константы.

1
2
public static const BUTTON_HEIGHT:Number = 40;
public static const NUM_OF_CIRCLES:uint = 12;

Высота нашей кнопки не изменится, поэтому целесообразно сохранить значение высоты кнопки в константе класса. Размер эллипса кнопки зависит от высоты кнопки. Если кнопка становится слишком большой или слишком маленькой, наша кнопка может потерять вид кнопки « Пилюля», к которому мы стремимся. Также количество пузырьков предопределено, поэтому мы сохраняем это значение также в константе класса.

Когда я рисую графику для компонента, такого как текстовое поле или кнопка, я всегда, всегда, всегда … начинаю с базовой фигуры. Это форма, которая выступает в качестве основы формы. Это основа компонента. То, как графика структурирована, полностью зависит от того, как я строю базу.

Вы заметите свойство, которое мы определили, под названием toggleBase . Я не говорю об этой собственности. Я говорю о самой base собственности. Эта форма будет определять контур кнопки. Давайте создадим это свойство и инициализируем его значения сейчас. Добавьте следующий код в конструктор класса, который вы определили как кнопку.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public function MyButton() {
     
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    addChild( base );
}

По умолчанию fillGradientRotation равно 0. Это значение fillGradientRotation в градусах, но преобразуется в радианы при передаче в поле градиента ( Matrix ). Наряду с инициализацией размера кнопки, мы должны установить вращение градиента. В этом случае мы хотим, чтобы это значение было 90 градусов. Мы также устанавливаем fillColors (как Array ) и lineColors .

Важно помнить, что значением по умолчанию для этих свойств является Array , имеющий length два. Все свойства Array ( lineColor , lineAlpha и lineRatios ) должны иметь одинаковую length . Если это не так, графика не будет рисовать. Вот почему мы устанавливаем свойства lineAlphas и lineRatios .

Если вы тестируете Flash-ролик, вы должны получить это.

Это та самая универсальная форма, к которой мы стремимся, «Таблетка геля». Давайте сделаем так, чтобы это немного сияло.

У нас уже есть основная структура кнопки, но теперь нам нужно добавить небольшую вещь, о которой я люблю проповедовать. Нам нужно много и много глубины . Мы можем начать с добавления отражения. Когда свет попадает на глянцевую поверхность, он отражается от поверхности. Создайте и инициализируйте свойство ref (сокращение от отражения ).

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
super();
buttonMode = true;
mouseChildren = false;
_width = 100;
 
base = new RectangleShape();
base.height = BUTTON_HEIGHT;
base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
base.fillGradientRotation = 90;
base.fillColors = [ 0xAAAAAA, 0x444444 ];
base.lineColors = [ 0x222222 ];
base.lineAlphas = [ .5 ];
base.lineRatios = [ 1 ];
 
ref = new RectangleShape();
ref.fillGradientRotation = 90;
ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
ref.fillAlphas = [ 1, 0 ];
ref.lineColors = [ 0 ];
ref.lineAlphas = [ 0 ];
ref.lineRatios = [ 0 ];
ref.alpha = .5;
ref.x = ref.ellipseHeight / 2;
ref.width = _width — ref.height;
 
addChild( base );
addChild( ref );

Мы немного уменьшили свойство alpha чтобы отражение не было таким интенсивным, но мы в основном выполнили те же шаги, чтобы нарисовать форму ref как мы делали с base формой. И, конечно же, мы измерили и расположили отражение относительно размера height кнопки. Посмотрим, что у нас сейчас.

Не так уж плохо. Все еще нужно намного больше глубины, хотя. Мы добавим еще немного позже.

Иногда, когда вы создаете искусство, вы замечаете, что именно эти крошечные детали создают большую разницу во внешнем виде вашей работы. Свойство light добавит к нашей кнопке крошечные детали, которые будут иметь значение. Это свойство также будет использоваться немного по-разному в состояниях включения и выключения нашей кнопки, но на данный момент она будет иметь полупрозрачную поверхность.

Добавьте следующий код в метод конструктора вашей кнопки.

01
02
03
04
05
06
07
08
09
10
light = new RectangleShape();
light.copy( ref );
light.x = ref.x;
light.y = BUTTON_HEIGHT — light.height;
light.width = _width — light.height;
light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
light.alpha = lightAlpha = .3;
 
addChild( light );

Конструктор теперь должен выглядеть так:

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
public function MyButton() {
     
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    ref = new RectangleShape();
    ref.fillGradientRotation = 90;
    ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
    ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
    ref.fillAlphas = [ 1, 0 ];
    ref.lineColors = [ 0 ];
    ref.lineAlphas = [ 0 ];
    ref.lineRatios = [ 0 ];
    ref.alpha = .5;
    ref.x = ref.ellipseHeight / 2;
    ref.width = _width — ref.height;
     
    light = new RectangleShape();
    light.copy( ref );
    light.x = ref.x;
    light.y = BUTTON_HEIGHT — light.height;
    light.width = _width — light.height;
    light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
    light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
    light.alpha = lightAlpha = .3;
     
    addChild( base );
    addChild( light );
    addChild( ref );
}

Мы добавили GlowFilter и BlurFilter к этому объекту, чтобы мы могли добиться определенного вида. Мы не хотим, чтобы фигура вообще выглядела как фигура, кроме пустого пространства внутри кнопки. Обратите внимание, что мы использовали метод copy для имитации свойств объекта ref . Нет необходимости писать одинаковые строки кода для light объекта. Просто имитируйте свойство ref и сделайте всего несколько модификаций, и вы получите новую форму.

Видите, как мы начинаем видеть больше глубины, когда строится наша форма? Таблетка не выглядит полностью непрозрачной и не очень прозрачной. Мы хотим, чтобы внешний вид «Из этого мира» был идеальным.

Теперь, когда у нас есть хорошее основание для таблицы, уместно начать добавлять первый TextField к кнопке. Инициализируйте свойство txt . И создайте маску такого же размера и эллипса, что и base чтобы текст не появлялся за пределами кнопки.

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
54
55
56
57
58
59
public function TutButton() {
             
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    ref = new RectangleShape();
    ref.fillGradientRotation = 90;
    ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
    ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
    ref.fillAlphas = [ 1, 0 ];
    ref.lineColors = [ 0 ];
    ref.lineAlphas = [ 0 ];
    ref.lineRatios = [ 0 ];
    ref.alpha = .5;
    ref.x = ref.ellipseHeight / 2;
    ref.width = _width — ref.height;
     
    light = new RectangleShape();
    light.copy( ref );
    light.x = ref.x;
    light.y = BUTTON_HEIGHT — light.height;
    light.width = _width — light.height;
    light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
    light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
    light.alpha = lightAlpha = .3;
     
    txt = new TextField();
    txt.selectable = false;
    txt.type = «dynamic»;
    txt.wordWrap = false;
    txt.multiline = false;
    txt.autoSize = «left»;
    txt.defaultTextFormat = new TextFormat( null, 27 );
    txt.textColor = 0x333333;
    txt.text = «label»;
    txt.height = BUTTON_HEIGHT;
     
    txtMask = new RectangleShape();
    txtMask.copy( base );
    txtMask.filters = [];
    txt.mask = txtMask;
     
    addChild( base );
    addChild( light );
    addChild( ref );
    addChild( txt );
    addChild( txtMask );
}

We have once again utilized the copy method by creating a copy of the base object. We could have very well have used the clone method also. The result would be the same. Also note that we have set the filters array of the txtMask object to a blank array. This is because when the copy method used the filters property is also inherited. We have just simply overriden this property. Let’s see where we’re at.

We will position all of the TextFields later on. For now our txt will just have to hang out on the left side of our button.

Our button will illuminate and display a sort of science fiction like animation sequence with bubbles when the mouse hovers over the button. Of course the entire sequence will be accomplished 100% with code.

But before we can do this we need to create the elements of the sequence. The first is the background object, or the glow property, of the over state. Let’s add the glow object now. Add the following code to the constructor method:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
glow = new RectangleShape();
glow.copy( base );
glow.fillColors = [ 0xFFFFFF ];
glow.fillAlphas = [ 1 ];
glow.fillRatios = [ 0 ];
glow.lineColors = [ 0 ];
glow.lineAlphas = [ 0 ];
glow.lineRatios = [ 0 ];
glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ];
 
addChild( base );
addChild( light );
addChild( ref );
addChild( txt );
addChild( txtMask );
addChild( glow );

We will also need a container for the bubbles to be displayed in and a mask for that container so that the bubbles don’t float out of the button.

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public function MyButton() {
     
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    ref = new RectangleShape();
    ref.fillGradientRotation = 90;
    ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
    ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
    ref.fillAlphas = [ 1, 0 ];
    ref.lineColors = [ 0 ];
    ref.lineAlphas = [ 0 ];
    ref.lineRatios = [ 0 ];
    ref.alpha = .5;
    ref.x = ref.ellipseHeight / 2;
    ref.width = _width - ref.height;
     
    light = new RectangleShape();
    light.copy( ref );
    light.x = ref.x;
    light.y = BUTTON_HEIGHT - light.height;
    light.width = _width - light.height;
    light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
    light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
    light.alpha = lightAlpha = .3;
     
    txt = new TextField();
    txt.selectable = false;
    txt.type = "dynamic";
    txt.wordWrap = false;
    txt.multiline = false;
    txt.autoSize = "left";
    txt.defaultTextFormat = new TextFormat( null, 27 );
    txt.textColor = 0x333333;
    txt.text = "label";
    txt.height = BUTTON_HEIGHT;
     
    txtMask = new RectangleShape();
    txtMask.copy( base );
    txtMask.filters = [];
    txt.mask = txtMask;
     
    glow = new RectangleShape();
    glow.copy( base );
    glow.fillColors = [ 0xFFFFFF ];
    glow.fillAlphas = [ 1 ];
    glow.fillRatios = [ 0 ];
    glow.lineColors = [ 0 ];
    glow.lineAlphas = [ 0 ];
    glow.lineRatios = [ 0 ];
    glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ];
     
    effectContainer = new Sprite();
    effectContainer.cacheAsBitmap = true;
     
    effectMask = new RectangleShape();
    effectMask.copy( base );
    effectMask.filters = [];
    effectContainer.mask = effectMask;
     
    addChild( base );
    addChild( light );
    addChild( ref );
    addChild( txt );
    addChild( txtMask );
    addChild( glow );
    addChild( effectContainer );
    addChild( effectMask );
}

Now we are going to blow a few bubbles. Add the following lines of code to the constructor method:

1
2
3
circleBlur = new BlurFilter( 5, 5, 3 );
circles = [];
tweens = [];

The circleBlur is a BlurFilter that will be applied to all bubbles. This will give each bubble the specific science like look that we’re going for. We also need to create the arrays that will store all of the bubbles and the tweens that will animate each bubble.

We need a method that will generate all of the bubbles we need.

01
02
03
04
05
06
07
08
09
10
private function createEffect():void {
     
    for ( var i:int = 0; i < NUM_OF_CIRCLES; i++ ) {
         
        var circ:EllipseShape = new EllipseShape();
        resetCircle( circ );
    }
     
    stopEffect();
}

Use a loop to iterate a block of code that creates a new EllipseShape object, or in this case a bubble, and run the resetCircle method on the bubble. We haven’t created this method yet but it re-initializes the parameter bubble. In the loop, the method simply initializes each bubble since they haven’t been initialized to begin with. Finally the stopEffect method is called. This method pauses the bubble’s animation sequence. We’ll write this method in a bit.

For now, create the following methods. (Also now is a good time to import com.greensock.TweenMax ).

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
private static function randomNumber( min:int = 0, max:int = 10 ):int {
     
    return Math.round( Math.random() * ( max - min ) + min );
}
 
private function resetCircle( circle:EllipseShape ):void {
     
    circle.width = circle.height = randomNumber( 10, 20 );
    effectContainer.addChild( circle );
    circle.cacheAsBitmap = true;
    circle.x = randomNumber( 0, _width );
    circle.y = BUTTON_HEIGHT;
    circle.filters = [ circleBlur ];
     
    if ( circles.indexOf( circle ) == -1 ) {
     
        circles.push( circle );
        circle.fillColors = [ 0x00BCE9 ];
        circle.fillAlphas = [ 1 ];
        circle.fillRatios = [ 1 ];
        circle.lineAlphas = [ 0 ];
        circle.lineRatios = [ 0 ];
        circle.lineColors = [ 0 ];
        circle.alpha = .6;
    }
     
    var tween:TweenMax = TweenMax.to( circle, randomNumber( 1, 6 ) * .5, { y:-BUTTON_HEIGHT, onComplete:doComplete } );
    tweens.push( tween );
     
    function doComplete():void {
         
        resetCircle( circle );
        removeTween( tween );
    }
}
 
private function removeTween( tween:TweenMax ):void {
     
    var a:Array = [];
     
    for each( var t:TweenMax in tweens ) {
         
        if ( t != tween ) a.push( t );
    }
     
    tweens = null;
    System.gc();
    tweens = a;
}

The randomNumber method returns a random number based on the parameters passed into the method. The first parameter specifies a the minimum value that can be generated and the second parameter specifies the maximum value that can be generated. We need this method to generate a random location and size for a bubble when we reset a bubble.

This brings me to the resetCircle method. I said before that this method re-initializes the specified bubble. To be more specific, the method recycles a bubble and makes it appear like a brand new bubble that has just been created when in reality it is just another old bubble.

The to method from the TweenMax class returns the tween that the bubble contains. We push this bubble into the tweens array. The bubble starts below the button and rises above the button. When the tween is finished it is no longer needed. The tween needs to be removed (using the removeTween method) and the bubble needs to be reset or recycled. The anonymous function doComplete does just that.

Before we can see what our bubbles look like we need to create two methods that will control playback of the bubbles’ animation sequence. Besides if we added a call to the createEffect method in the constructor and tested our movie now, the bubbles would be all there but we wouldn’t see them because they would be beneath the button. Remember that each bubble is added to the effectContainer in the resetCircle method and that the effectContainer is masked so that the bubbles aren’t seen outside of the button.

(Plus, if you tested the movie now you’d just get a compile-time error for not implementing the stopEffect method that is called when we create our bubbles.)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private function playEffect():void {
     
    for each( var tween:TweenMax in tweens ) {
         
        tween.play();
    }
}
 
private function stopEffect():void {
     
    for each( var tween:TweenMax in tweens ) {
         
        tween.pause();
    }
}

Loop through the tweens array to play each tween and to pause each tween when needed. Очень просто.

Now add a call to the createEffect method and the playEffect method within the constructor. Проверь свой фильм. You should now have a beautiful display of bubbles. When you are finished remove the playEffect method from the constructor that you just added but keep the createEffect method.

Let’s add the glowing TextField to the button. We’re using a seperate TextField , effectTxt instead of txt , so that we can do a fading transition into the next state. We need an additional TextField for this. We also need another mask for our TextField just as we did with the first one.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
public function MyButton() {
     
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    ref = new RectangleShape();
    ref.fillGradientRotation = 90;
    ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
    ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
    ref.fillAlphas = [ 1, 0 ];
    ref.lineColors = [ 0 ];
    ref.lineAlphas = [ 0 ];
    ref.lineRatios = [ 0 ];
    ref.alpha = .5;
    ref.x = ref.ellipseHeight / 2;
    ref.width = _width - ref.height;
     
    light = new RectangleShape();
    light.copy( ref );
    light.x = ref.x;
    light.y = BUTTON_HEIGHT - light.height;
    light.width = _width - light.height;
    light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
    light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
    light.alpha = lightAlpha = .3;
     
    txt = new TextField();
    txt.selectable = false;
    txt.type = "dynamic";
    txt.wordWrap = false;
    txt.multiline = false;
    txt.autoSize = "left";
    txt.defaultTextFormat = new TextFormat( null, 27 );
    txt.textColor = 0x333333;
    txt.text = "label";
    txt.height = BUTTON_HEIGHT;
     
    txtMask = new RectangleShape();
    txtMask.copy( base );
    txtMask.filters = [];
    txt.mask = txtMask;
     
    glow = new RectangleShape();
    glow.copy( base );
    glow.fillColors = [ 0xFFFFFF ];
    glow.fillAlphas = [ 1 ];
    glow.fillRatios = [ 0 ];
    glow.lineColors = [ 0 ];
    glow.lineAlphas = [ 0 ];
    glow.lineRatios = [ 0 ];
    glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ];
     
    effectContainer = new Sprite();
    effectContainer.cacheAsBitmap = true;
     
    effectMask = new RectangleShape();
    effectMask.copy( base );
    effectMask.filters = [];
    effectContainer.mask = effectMask;
     
    effectTxt = new TextField();
    effectTxt.selectable = false;
    effectTxt.type = "dynamic";
    effectTxt.wordWrap = false;
    effectTxt.multiline = false;
    effectTxt.autoSize = "left";
    effectTxt.defaultTextFormat = new TextFormat( null, 27 );
    effectTxt.textColor = 0x00BCE9;
    effectTxt.text = "label";
    effectTxt.filters = [ new GlowFilter( 0x00BCE9, 1, 16, 16, 2, 3 ) ];
     
    effectTxtMask = new RectangleShape();
    effectTxtMask.copy( base );
    effectTxtMask.filters = [];
    effectTxt.mask = effectTxtMask;
     
    circleBlur = new BlurFilter( 5, 5, 3 );
    circles = [];
    tweens = [];
     
    addChild( base );
    addChild( light );
    addChild( ref );
    addChild( txt );
    addChild( txtMask );
    addChild( glow );
    addChild( effectContainer );
    addChild( effectMask );
    addChild( effectTxt );
    addChild( effectTxtMask );
     
    createEffect();
}

Just another TextField with a glow filter. Again we will position the text fields later when we create the update method. Now you should have something that looks like this.

Now that all of the elements for the over state have been generated, we only have one more task to complete. Next we position the contents of the button.

Encapsulated in our button class is a function that positions the button’s contents based on the current value of the _width property. The method I am referring to is the update method. Call the update method on the last line of code of the constructor method. Then create the update method.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
    addChild( effectTxt );
    addChild( effectTxtMask );
     
    createEffect();
    update();
}
 
protected function update():void {
     
    base.width = _width;
    glow.width = _width;
    ref.width = _width - ref.height;
    light.width = _width - light.height;
    txt.x = ( _width - txt.width ) / 2;
    txt.y = ( BUTTON_HEIGHT - txt.height ) / 2;
    effectTxt.x = txt.x;
    effectTxt.y = txt.y;
    effectMask.width = _width;
    txtMask.width = _width;
    effectTxtMask.width = _width;
}

Resize particalar objects and reposition others. Test the movie:

We now have centered text. Set the alpha property of all of the effect base objects( effectContainer , effectTxt and glow ) to zero. Re-test the movie and now you should see the original state of the button.

At last we finally get to the ever-so-exciting «over» state. Okay let’s go! Add the following lines of code at the end of the constructor method.

1
2
addEventListener( "rollOver", overState );
addEventListener( "rollOut", outState );

Now create the overState and outState event handler methods.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private function overState( e:MouseEvent ):void {
     
    playEffect();
    TweenMax.to( light, 3, { alpha:1 } );
    TweenMax.to( glow, 1, { alpha:1 } );
    TweenMax.to( effectTxt, 1, { alpha:1 } );
    TweenMax.to( effectContainer, 1, { alpha:1 } );
}
 
private function outState( e:MouseEvent ):void {
     
    TweenMax.to( light, 3, { alpha:lightAlpha } );
    TweenMax.to( glow, 1, { alpha:0 } );
    TweenMax.to( effectTxt, 1, { alpha:0 } );
    TweenMax.to( effectContainer, 1, { alpha:0, onComplete:stopEffect } );
    //upState();
}

The overState method plays the bubbles animation and performs a fade in transition of the effect whenever the mouse hovers over the button.

The outState method does the complete opposite: it returns the button back to its original state when the mouse hovers off of the button, lets the light property linger for a second to give the button a sense of losing power or losing illumination.

Comment out the upState method so that you can test the movie without any errors. We’ll create this method soon.

Important: Remember to uncomment out the call to the upState method within the outState method after we have created that method.

Add the following code at the end of the constructor method.

1
2
addEventListener( "mouseDown", downState );
addEventListener( "mouseUp", upState );

Create the following methods.

1
2
3
4
5
6
7
8
9
private function downState( e:MouseEvent ):void {
     
    filters = [ new GlowFilter( 0 ) ];
}
 
private function upState( e:MouseEvent = null ):void {
     
    filters = [];
}

The downState method displays a background glow around the button when the mouse button is down and the upState button removes that glow return the button back to it’s initial state. The upState method’s e parameter is set to null by default. This allows the method to be called manually and not require the dispatching of a MouseEvent . We’ll test to see where were at after the next step.

Reminder: Uncomment the call to the upState method from the outState method.

Add the missing code to the constructor method.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
public function MyButton() {
     
    super();
    buttonMode = true;
    mouseChildren = false;
    _width = 100;
     
    base = new RectangleShape();
    base.height = BUTTON_HEIGHT;
    base.ellipseHeight = base.ellipseWidth = BUTTON_HEIGHT;
    base.fillGradientRotation = 90;
    base.fillColors = [ 0xAAAAAA, 0x444444 ];
    base.lineColors = [ 0x222222 ];
    base.lineAlphas = [ .5 ];
    base.lineRatios = [ 1 ];
     
    ref = new RectangleShape();
    ref.fillGradientRotation = 90;
    ref.ellipseHeight = ref.ellipseWidth = ref.height = BUTTON_HEIGHT / 2;
    ref.fillColors = [ 0xFFFFFF, 0xFFFFFF ];
    ref.fillAlphas = [ 1, 0 ];
    ref.lineColors = [ 0 ];
    ref.lineAlphas = [ 0 ];
    ref.lineRatios = [ 0 ];
    ref.alpha = .5;
    ref.x = ref.ellipseHeight / 2;
    ref.width = _width - ref.height;
     
    light = new RectangleShape();
    light.copy( ref );
    light.x = ref.x;
    light.y = BUTTON_HEIGHT - light.height;
    light.width = _width - light.height;
    light.fillColors = [ 0xFFFFFF, 0xDDDDDD ];
    light.filters = [ new GlowFilter( 0xFFFFFF, .8, 10, 10, 2, 3 ), new BlurFilter( 6, 6, 3 ) ];
    light.alpha = lightAlpha = .3;
     
    txt = new TextField();
    txt.selectable = false;
    txt.type = "dynamic";
    txt.wordWrap = false;
    txt.multiline = false;
    txt.autoSize = "left";
    txt.defaultTextFormat = new TextFormat( null, 27 );
    txt.textColor = 0x333333;
    txt.text = "label";
    txt.height = BUTTON_HEIGHT;
     
    txtMask = new RectangleShape();
    txtMask.copy( base );
    txtMask.filters = [];
    txt.mask = txtMask;
     
    glow = new RectangleShape();
    glow.copy( base );
    glow.fillColors = [ 0xFFFFFF ];
    glow.fillAlphas = [ 1 ];
    glow.fillRatios = [ 0 ];
    glow.lineColors = [ 0 ];
    glow.lineAlphas = [ 0 ];
    glow.lineRatios = [ 0 ];
    glow.filters = [ new GlowFilter( 0x00BCE9, 1, 10, 10, 2, 3, false, false ), new GlowFilter( 0x00BCE9, 1, 20, 20, 2, 3, true ) ];
    glow.alpha = 0;
     
    effectContainer = new Sprite();
    effectContainer.cacheAsBitmap = true;
    effectContainer.alpha = 0;
     
    effectMask = new RectangleShape();
    effectMask.copy( base );
    effectMask.filters = [];
    effectContainer.mask = effectMask;
     
    effectTxt = new TextField();
    effectTxt.selectable = false;
    effectTxt.type = "dynamic";
    effectTxt.wordWrap = false;
    effectTxt.multiline = false;
    effectTxt.autoSize = "left";
    effectTxt.defaultTextFormat = new TextFormat( null, 27 );
    effectTxt.textColor = 0x00BCE9;
    effectTxt.text = "label";
    effectTxt.filters = [ new GlowFilter( 0x00BCE9, 1, 16, 16, 2, 3 ) ];
    effectTxt.alpha = 0;
     
    effectTxtMask = new RectangleShape();
    effectTxtMask.copy( base );
    effectTxtMask.filters = [];
    effectTxt.mask = effectTxtMask;
     
    toggleBase = new RectangleShape();
    toggleBase.height = BUTTON_HEIGHT;
    toggleBase.ellipseHeight = toggleBase.ellipseWidth = BUTTON_HEIGHT;
    toggleBase.fillGradientRotation = 90;
    toggleBase.fillColors = [ 0x00CDFF, 0x00A9D2 ];
    toggleBase.lineColors = [ 0x00A9D2 ];
    toggleBase.lineAlphas = [ .5 ];
    toggleBase.lineRatios = [ 1 ];
    toggleBase.visible = false;
    toggleBase.alpha = 0;
    toggleBase.filters = [ new BevelFilter( 5, 45, 0xFFFFFF, 1, 0, .6, 18, 18, 1, 3, "inner" ), new GlowFilter( 0x00BCE9, .8, 6, 6, 1, 3 ) ];
     
    toggleGlass = new RectangleShape();
    toggleGlass.copy( toggleBase );
    toggleGlass.fillColors = [ 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF ];
    toggleGlass.fillAlphas = [ 0, .3, .2, .3 ];
    toggleGlass.fillRatios = [ 1, 127, 127, 255 ];
    toggleGlass.filters = [ new DropShadowFilter( 0, 45, 0xFFFFFF, .8, 10, 10, 2, 3, true ) ];
    toggleGlass.visible = false;
     
    toggleTxt = new TextField();
    toggleTxt.selectable = false;
    toggleTxt.type = "dynamic";
    toggleTxt.wordWrap = false;
    toggleTxt.multiline = false;
    toggleTxt.autoSize = "left";
    toggleTxt.defaultTextFormat = new TextFormat( null, 27 );
    toggleTxt.textColor = 0xFFFFFF;
    toggleTxt.text = "label";
    toggleTxt.filters = [ new BevelFilter( 1, 45, 0, .4, 0xFFFFFF, 1, 2, 2, 2, 3, "inner" ), new GlowFilter( 0xFFFFFF, 1, 16, 16, 2, 3 ) ];
    toggleTxt.alpha = 0;
    toggleTxt.visible = false;
     
    toggleTxtMask = new RectangleShape();
    toggleTxtMask.copy( base );
    toggleTxtMask.filters = [];
    toggleTxt.mask = toggleTxtMask;
     
    circleBlur = new BlurFilter( 5, 5, 3 );
    circles = [];
    tweens = [];
     
    addChild( base );
    addChild( light );
    addChild( ref );
    addChild( txt );
    addChild( txtMask );
    addChild( glow );
    addChild( effectContainer );
    addChild( effectMask );
    addChild( effectTxt );
    addChild( effectTxtMask );
    addChild( toggleBase );
    addChild( toggleGlass );
    addChild( toggleTxt );
    addChild( toggleTxtMask );
     
    createEffect();
    update();
     
    addEventListener( "rollOver", overState );
    addEventListener( "rollOut", outState );
    addEventListener( "mouseDown", downState );
    addEventListener( "mouseUp", upState );
}

We have just created the all of the objects that we need for what I call the active state . When the button’s _toggle property is set to true , the button behaves differently. We’ll see how in a moment. First add the following lines of code to the update method.

1
2
3
4
5
toggleBase.width = _width;
toggleTxtMask.width = _width;
toggleGlass.width = _width;
toggleTxt.x = txt.x;
toggleTxt.y = txt.y;

Now create two new methods. The goActive method and the goInactive method.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private function goActive():void {
     
    if ( !_active ) {
         
        _active = true;
        TweenMax.allTo( [ toggleBase, toggleGlass, toggleTxt ], .4, { autoAlpha:1 } );
        TweenMax.to( glow, 1, { alpha:1 } );
    }
}
 
private function goInactive():void {
     
    if ( _active ) {
         
        _active = false;
        TweenMax.allTo( [ toggleBase, toggleGlass, toggleTxt ], .4, { autoAlpha:0 } );
    }
}

Let’s take a quick look at each method. The goActive method animates to the active state only if the button is inactive. It then set’s the _active property to true . The goInactive method does the complete opposite. If the button is active, it returns to an inactive state and sets the _active property to false .

Now for the final step in creating toggle behavior for our button. Add this code to the upState method.

01
02
03
04
05
06
07
08
09
10
11
if ( e && _toggle ) {
     
    if ( _active ) {
             
        goInactive();
    }
    else {
         
        goActive();
    }
}

The code above checks to see if the method has been called do to an event being dispatched (we don’t want this block of code to execute otherwise) and it checks to see if the button’s toggle property has been enabled. Then the button travels to the appropriate state based on its current state. In other words, it toggles.

Set the _toggle property to true somewhere in the constructor and test the movie.

We now have a beautiful button. There’s just one last problem. Our button isn’t editable — well, not from outside of itself anyway. So, next were going to create the class’s getters and setters .

First let’s override the width property to give access to our custom _width property.

01
02
03
04
05
06
07
08
09
10
11
public override function set width( value:Number ):void {
     
    if ( value < 0 ) value = 0;
    _width = value;
    update();
}
 
public override function get width():Number {
     
    return _width;
}

Don’t allow the value to be lower than zero. Set the _width property and call the update method.

Now override the height property.

1
2
3
4
5
6
7
8
9
public override function set height( value:Number ):void {
     
    throw new Error( "Property 'height' has been overriden and is now read-only." );
}
 
public override function get height():Number {
     
    return BUTTON_HEIGHT;
}

Trying to set the height property just throws an error because this property now has a fixed value which is the BUTTON_HEIGHT constant.

Create the following getter and setter methods.

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
public function set label( value:String ):void {
     
    txt.text = value;
    effectTxt.text = value;
    toggleTxt.text = value;
    update();
}
 
public function get label():String {
     
    return txt.text;
}
 
public function set active( value:Boolean ):void {
     
    if ( value ) {
         
        goActive();
    }
    else {
         
        goInactive();
    }
}
 
public function get active():Boolean {
     
    return _active;
}
 
public function set toggle( value:Boolean ):void {
     
    _toggle = value;
     
    if ( !value && active ) goInactive();
}
 
public function get toggle():Boolean {
     
    return _toggle;
}

When the label is set we immediatly react to the change by setting the text values of all of the TextField objects and we position them by calling the update method. The active property changes states based on the new value. When we set the toggle property, if it’s new value is false we check to see if the button is active and if it is we switch it to its original state.

Trying setting the width property to 300 and the label to «Push Me», and making the button an active toggle button.

I said earlier that we would add more depth to the button. I also said that the tiny details make a large difference. Well this can be accomplished very quickly and easily by applying a couple of filters. Add these two lines of code anywhere in the constructor method. Then test the movie.

1
2
base.filters = [ new DropShadowFilter( 5, 45, 0, .6, 6, 6, 1, 3 ), new BevelFilter( 0, 0, 0xFFFFFF, .5, 0, .5, 10, 10, 1, 3 ) ];
txt.filters = [ new DropShadowFilter( 3, 45, 0, 1, 6, 6, 1, 3 ) ];

Let’s see what we have now. The button on top is the original and the button on the button is the new and improved button with the above filters.

Вот это да! Look at that button pop out. Now you can see how important it is to use filters when drawing graphics to add in shadows and other little details that make your graphics that much better.

There are a couple of features within the EditableShape class that we didn’t have time to use. One feature is the clone method. It returns a clone of the parent shape. Another is the useBitmapFill property. You must specify a valid BitmapData object by setting the bitmapData property and you have to set useBitmapFill to true .

Here’s our button with a bitmap fill applied to the base property.

I also didn’t discuss the matchGradientSize property which is true by default. If set to false the width and height of the fill and line gradients will not mimic the size of the actual shape. You can get create by deactivating this property. You can even use TweenLite (or TweenMax) to animate these and most of the properties in the EditableShape class.

Well our button is complete and our client is happy. We have only scratched the surface. There’s a lot more that you can do with Flash’s drawing API. I challenge you to make a CustomShape class that extends EditableShape . The class should use the lineTo methods of the graphics object to draw irregular shapes. Have fun with it. Thanks for tuning in. We’ve learned a lot. Увидимся в следующий раз.