Руководство по actionscript. часть 4, стр. 123

Ш4_ Q Метод setlnterval() языка ActionScript 2.0 может также использовать метод updateAfterEvent() } для вызова постсобытийного обновления экрана, однако вместо метода setlnterval() предпочтительнее использовать класс flash. utils. Timer, поскольку он предоставляет возможность запускать и останавливать события таймера, а также уведомлять о таймерных событиях сразу несколько приемников. Старайтесь избегать использования метода setlnterval() в языке ActionScript 3.0.

Автоматические постсобытийные обновления экрана

В приложении Flash Player 9 определенные «кнопочные» взаимодействия с объектами любого класса, наследуемого от класса Sprite, приводят к автоматическому постсобытийному обновлению экрана (аналогично тому, как программист вызывает метод updateAf terEvent ( ) ). В частности, автоматическое постсобытийное обновление экрана будут вызывать следующие взаимодействия:

? перемещение указателя мыши над экземпляром класса, который наследуется от класса Sprite, или за пределы этого экземпляра;

? нажатие или отпускание основной кнопки мыши, когда указатель мыши находится над экземпляром класса, наследуемого от Sprite;

? использование клавиши Пробел или Enter для активизации экземпляра класса, наследуемого от класса Sprite.

Существует небольшая вероятность, что в будущих версиях приложения Flash Player такое особое поведение, относящееся к автоматическому обновлению экрана, будет применяться только к объектам SimpleButton. В связи с этим вы, вероятно, не захотите полагаться на него в своем коде.

Область перерисовки

Как уже известно из разд. «Запланированные обновления экрана», среда выполнения Flash обновляет экран только в тех случаях, когда это необходимо (то есть когда графическое содержимое было изменено или добавлено). Точнее говоря, когда среда выполнения обновляет экран, она визуализирует только те области, которые изменились с момента последнего обновления. Например, представим анимацию, состоящую из двух кадров, первый из которых содержит круг, а второй — тот же круг, но вместе с треугольником. Когда среда Flash визуализирует второй кадр, она перерисовывает прямоугольную область, содержащую треугольник, но не перерисовывает круг. Прямоугольная область, включающая все содержимое, которое было изменено, называется областью перерисовки (в программировании графики данная область иногда называется измененным прямоугольником).

Руководство по actionscript. часть 4, стр. 124

^ I Чтобы отобразить область перерисовки в отладочных версиях среды выполнения, можно м$ 4 * щелкнуть правой кнопкой мыши в окне приложения Flash Player и в контекстном меню Щ< выбрать пункт show redraw regions (показать области перерисовки).

Оптимизация с использованием события Event. RENDER

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

программисту возможности отложить выполнение всех пользовательских процедур рисования точно до того момента, когда произойдет визуализация экрана, позволяя тем самым избежать повторного выполнения процедур рисования. В отличие от остальных внутренних событий среды разработки Flash, событие Event. RENDER должно быть запрошено программистом вручную. Среда Flash осуществляет диспетчеризацию события Event. RENDER, когда выполняются два следующих условия:

? среда выполнения Flash собирается проверить необходимость обновления экрана (либо при прохождении какого-либо кадра, либо в результате вызова метода updateAf terEvent ( ) );

? программист вызвал метод stage. invalidate ( ) (с его помощью программист может попросить среду выполнения Flash осуществить диспетчеризацию события Event. RENDER в следующий раз, когда произойдет проверка обновления экрана).

Рассмотрим пример, который демонстрирует, как событие Event. RENDER может быть использовано для улучшения производительности. Предположим, что мы создаем класс Ell ipse, который представляет фигуру эллипса на экране. Для простоты предположим, что эллипс всегда заполняется белым цветом и имеет контур черного цвета толщиной 1 пиксел. Наш класс Ellipse должен делать следующее:

? управлять концептуальными данными эллипса (то есть хранить ширину и высоту эллипса);

? рисовать эллипс на экране, используя данные концептуального эллипса, и перерисовывать эллипс на экране при изменении данных концептуального эллипса.

Руководство по actionscript. часть 4, стр. 125

В соответствии с описанными назначениями, в листинге 23.2 представлен вариант реализации класса Ellipse, который приемлем в том случае, когда производительность не имеет решающего значения:

Листинг 23.2. Простейший класс Ellipse

package { import flash. display. Shape;

public class Ellipse extends Shape { private var w:Number; private var h:Number:

public function Ellipse (width:Number, height:Number) { w = width; h = height; draw( );

}

public function setWidth (newWidth:Number):void { w = newWidth; draw( );

}

public function getWidth ( ):Number {

return w;

}

public function setHeight (newHeight:Number):void { h = newHeight; draw( );

}

public function getHeight ( ):Number { return h;

}

private function draw ( ):void { graphics. lineStyle(l); graphics. beginFill(OxFFFFFF, 1); graphics. drawEllipse(0, 0, w, h);

}

}

}

Обратите внимание, что в классе Ellipse есть три места, где происходит изменение данных концептуального эллипса: метод setWidth ( ), метод setHeight ( ) и метод-конструктор класса Ell ipse. Чтобы обеспечить соответствие между концептуальным эллипсом и эллипсом, отображаемым на экране, мы должны убедиться, что перерисовка эллипса, отображаемого на экране, осуществляется в каждом из трех перечисленных мест. Код, представленный в листинге 23.2, для удовлетворения этого требования использует метод решения «в лоб»; он просто вызывает метод draw( ) всякий раз, когда выполняются методы getWidth ( ), getHeight ( ) или метод-конструктор класса Ellipse. Разумеется, если внутри одного цикла обновления экрана эти функции вызываются несколько раз, дублирующие вызовы метода draw ( ) являются избыточными. Это демонстрирует следующий код:

var е:El 1 ipse = new Ellipse (100, 200); // метод draw( ) вызывается здесь e. setWidth(25); // метод draw( ) вызывается здесь снова е. setHeight(50); // метод draw( ) вызывается здесь снова

При выполнении трех предыдущих строк кода метод draw ( ) вызывается три раза, однако на экране будут отображены результаты лишь последнего вызова. Первые два вызова являются избыточными и нерациональными. В простейшем приложении такая избыточность незаметна и, следовательно, может оказаться допустимой. Тем не менее в сложных приложениях подобная избыточность может приводить к тому, что процедуры рисования будут бесполезно выполняться сотни или тысячи раз, представляя собой потенциальный источник серьезных проблем, связанных с производительностью.

Руководство по actionscript. часть 4, стр. 126

Чтобы избавиться от избыточности в классе Ellipse, мы должны изменить подход, применяемый для рисования фигуры. Вместо того чтобы вызывать метод draw ( ) всякий раз, когда изменяются данные концептуального эллипса, мы будем откладывать вызов этого метода до момента обновления экрана. Эта новая стратегия приведет к усложнению кода класса Ellipse, но при этом улучшит его производительность.

Первый шаг в реализации новой стратегии «один вызов метода dr aw ( )» заключается в удалении вызова метода draw ( ) из методов setWidht ( ) и setHeight ( ), а также вызова метода-конструктора класса El 1 ipse. Вместо непосредственного вызова метода draw ( ) эти функции будут вызывать метод stage. invalidate ( ), который приказывает среде выполнения Flash осуществить диспетчеризацию события Event. RENDER при очередной проверке необходимости обновления экрана. Затем из функции-приемника события Event. RENDER мы вызовем метод draw ( ). В листинге 23.3 представлен измененный класс Ell ipse — отличия от кода из листинга 23.2 выделены полужирным шрифтом. Стоит отметить, что метод draw ( ) не должен вызываться, когда объект Ellipse не находится в списке отображения, поэтому вызов метода stage. inval idate ( ) происходит только в том случае, когда объект Ell ipse находится в списке отображения. Чтобы определить, находится ли объект Ellipse в списке отображения, мы проверяем значение унаследованной переменной экземпляра stage этого объекта. Когда значение переменной stage равно null, объект Ellipse не находится в списке отображения.

*«, -

I Объект, запрашивающий уведомление о возникновении события Event. RENDER, м$ л* получит это уведомление даже в том случае, если он не находится в списке отобра-_ ffi жения.

Обратите внимание, что на данном промежуточном этапе нашей разработки класс Ell ipse не является полнофункциональным, поскольку он не регистрирует приемники для событий Event. RENDER. Эту проблему мы решим в ближайшее время.

Листинг 23.3. Измененный класс Ellipse, часть 1

package { import flash. display. Shape;

public class Ellipselnterim extends Shape { private var w:Number; private var h:Number;

public function Ellipselnterim (width:Number, height:Number) { w = width; h = height;

// Если этот объект находится в списке отображения… if (stage!= null) {

// …запрашиваем диспетчеризацию события Event. RENDER

stage. invalidate( );

}

}

public function setWidth (newWidth:Number):void { w = newWidth;

if (stage!= null) { stage. invalidate( );

public function getWidth ( ):Number { return w;

}

public function setHeight (newHeight:Number):void { h = newHeight;

if (stage!= null) { stage. invalidate( );

}

}

public function getHeight ( ).-Number { return h;

}

// Приемник события вызывается перед обновлением // экрана, если был вызван метод // stage. invalidate( )

private function renderListener (e:Event):void { draw( );

}

private function draw ( ):void { graphics. clear( ); graphics.1ineStyle(l); graphics. beginFill(OxFFFFFF. 1); graphics. drawEllipse(0. 0. w, h);

}

}

}

Чтобы метод renderListener ( ) выполнялся всякий раз, когда среда Flash осуществляет диспетчеризацию события Event. RENDER, мы должны зарегистрировать метод renderListener ( ) в экземпляре класса Stage для событий Event. RENDER. Однако когда объект Ellipse не находится в списке отображения, его переменной экземпляра stage присвоено значение null и, следовательно, она не может применяться для регистрации события. Для того чтобы обойти эту проблему, мы определим в классе Ellipse две функции-приемника событий — addedToStageListener( ) и removedFomStageListener( ), которые получают уведомления о возникновении пользовательских событий StageDetector. ADDED_TO_SТAGE и StageDetector. REMOVED_FROM_STAGE. Диспетчеризация первого события происходит, когда объект добавляется в список отображения, и при получении этого события класс Ellipse будет регистрировать метод renderListener ( ) для события Event. RENDER. Диспетчеризация второго события происходит в том случае, когда объект удаляется из списка отображения, и при получении этого события класс Ellipse будет отменять регистрацию метода renderListener ( ) для событий Event. RENDER.

Руководство по actionscript. часть 4, стр. 127

В листинге 23.4 продемонстрирован измененный класс Ellipse (изменения, как и раньше, выделены полужирным шрифтом). Обратите внимание, что метод

addedToStageListener ( ) вызывает метод stage. invalidate ( ), гарантируя, что любые изменения, внесенные в объект Ellipse за то время, пока он не находился в списке отображения, будут визуализированы при добавлении этого объекта в список отображения.

*»4

В листинге 23.4 применяются пользовательские события StageDetector. ADDED_TO_STAGE и StageDetector. REMOVED_FROM_STAGE, диспетчеризация которых осуществляется поль-3?-’ зовательским классом StageDetector. Детальное рассмотрение данного класса можно найти в подразд. «События ADDED_TO_STAGE и REMOVED_FROM_STAGE» разд. «События контейнеров» гл. 20.

Листинг 23.4. Измененный класс Ellipse, часть 2

package { import flash. display. Shape; import flash. events.*;

public class Ellipselnterim extends Shape { private var w:Number; private var h:Number;

public function Ellipselnterim (width:Number, height:Number) { // Регистрируем приемники для получения уведомлений, // когда данный объект добавляется в список // отображения или удаляется из него var StageDetector-.StageDetector = new StageDetector (this); StageDetector. addEventLi stener(StageDetector. ADDEDJTJSTAGE,

addedToStageListener); StageDetector. addEventLi stener (StageDetector. REMOVEDFROMSTAGE,

removedFromStageListener);

w = width; h = height; if (stage!= null) { stage. invalidate( );

}

}

public function setWidth (newWidth:Number):void { w = newWidth; if (stage!= null) { stage. invalidate( );

}

}

public function getWidth ( ):Number { return w;

}

public function setHeight (newHeight:Number):void { h = newHeight;

if (stage!= null) { stage. invalidate( );

}

}

public function getHeight ( ):Number { return h;

}

// Приемник события вызывается при добавлении этой фигуры // в список отображения

private function addedToStageListener (e:Event):void { // Регистрируем приемник для получения уведомлений // об обновлениях экрана

stage. addEventListener(Event. RENDER, renderListener);

// Гарантируем, что любые изменения, внесенные в данный объект // за то время, пока он не отображался на экране, будут // визуализированы при его добавлении в список отображения, stage. invalidate( );

}

// Приемник события вызывается при удалении этой фигуры из списка // отображения

private function removedFromStageLi stener (e: Event) .-void { // Нет необходимости в получении событий // обновления экрана, когда данный объект // не находится в списке отображения stage. addEventListener(Event. RENDER, renderListener);

}

private function renderListener (e:Event):void { draw( );

}

private function draw ( ):void { graphics. clear( ); graphics. lineStyle(l); graphics. beginFill(OxFFFFFF, 1); graphics. drawEllipse(0. 0. w, h);

}

}

}

Класс Ellipse, представленный в листинге 23.4, теперь является полностью функциональным, однако в нем все еще остаются две существенные избыточности. Во-первых, метод addedToStageListener ( ) всегда вызывает метод stage. invalidate ( ), когда объект Ellipse добавляется в список отображения. В результате перерисовка фигуры происходит даже в тех случаях, когда в этом нет необходимости, поскольку за то время, пока эллипс не отображался на экране, данные концептуального эллипса не изменялись.

Руководство по actionscript. часть 4, стр. 128

Во-вторых, не забывайте, что событие Event. RENDER возникает в том случае, когда любой объект — не обязательно текущий — вызывает метод stage. invalidate ( ). Таким образом, в текущем состоянии метод renderListener ( ) будет вызывать метод draw ( ) всякий раз, когда любой объект в приложении вызывает метод stage. invalidate ( ). В приложении с множеством объектов такая избыточность может привести к серьезным проблемам, связанным с производительностью.

Чтобы решить эти две оставшиеся проблемы, мы внесем последний набор изменений в класс Ellipse — добавим новую логику, позволяющую определить, требуется ли перерисовка эллипса при вызове методов addedToStageListener ( ) и renderListener ( ). Во-первых, добавим новую переменную экземпляра changed, которая будет сообщать о том, требуется ли перерисовка объекта El 1 ipse при очередном обновлении экрана. Затем, чтобы устанавливать и сбрасывать значение переменной changed, а также проверять ее статус, добавим три новых метода: setChanged( ), clearChanged ( ) и hasChanged( ). Наконец, всякий раз при изменении эллипса (то есть всякий раз при вызове метода setwidth ( ), setHeight ( ) или метода-конструктора) мы будем присваивать переменной changed значение true.

В листинге 23.5 представлена окончательная версия класса Ellipse с комментариями, которые помогут вам понять код (изменения выделены полужирным шрифтом). Как отмечалось ранее, класс, представленный в листинге 23.5, безусловно, является более сложным, чем его первоначальная, неоптимизированная версия из листинга 23.2. Однако в приложениях, требующих максимальной графической производительности, подход с отложенной визуализацией, применяемый в листинге 23.5, является просто незаменимым. Дополнительную информацию по классам, представляющим фигуры и использующим отложенную визуализацию, можно найти в гл. 25.

Листинг 23.5. Окончательная, оптимизированная версия класса Ellipse

package { import flash. display. Shape; import flash. events.*;

public class Ellipse extends Shape { private var wiNumber; private var h:Number; private var changed:Boolean;

public function Ellipse (width:Number, height:Number) { // Регистрируем приемники для получения уведомлений, когда данный // объект добавляется в список отображения или удаляется из него var StageDetector:StageDetector = new StageDetector(this); StageDetector. addEventListener(StageDetector. ADDED_TO_STAGE,

addedToStageLi stener); StageDetector. addEventLi stener(StageDetector. REMOVED_FROM_STAGE.

removedFromStageListener); // Устанавливаем ширину и высоту w = width; h = height;

// Помечаем, что объект изменился setChanged( );

}

public function setWidth (newWidth:Number):void { w = newWidth; setChanged( );

}

public function getWidth ( ):Number { return w;

}

public function setHeight (newHeight:Number):void { h = newHeight; setChanged( );

}

public function getHeight ( ):Number { return h;

}

// Помечает, что в данной фигуре что-то изменилось private function setChanged ( ):void {

changed = true;

if (stage!= null) { stage. invalidate( );

}

}

// Помечает, что самые последние изменения // были отображены на экране private function clearChanged ( ):void { changed = false;

}

// Сообщает о том, содержит ли данная фигура изменения, // которые еще не были отображены на экране protected function hasChanged ( ):Boolean { return changed;

}

// Приемник события вызывается при добавлении этой фигуры // в список отображения

private function addedToStageListener (e:Event):void { // Регистрируем приемник для получения уведомлений // об обновлениях экрана

stage. addEventLi stener(Event. RENDER, renderLi stener);

// Если за то время, пока объект отсутствовал в списке отображения,

// произошли какие-либо изменения, нарисуем эти изменения

// при следующей визуализации экрана. Однако если с момента,

// когда объект находился в списке отображения, никаких изменений

// не произошло, в его отрисовке нет необходимости, if (hasChanged( )). { stage. invalidate( );

}

}

// Приемник события вызывается при удалении этой фигуры из списка // отображения

private function removedFromStageListener (e:Event):void { // Нет необходимости в получении событий обновления экрана, когда // данный объект не находится в списке отображения stage. addEventLi stener(Event. RENDER. renderLi stener);

}

// Приемник события вызывается перед обновлением экрана, если был вызван

// метод stage. invalidate( )

private function renderListener (e:Event):void {

// Вызываем метод draw ( ), если изменения, внесенные в данную фигуру,

// еще не были отображены.

Руководство по actionscript. часть 4, стр. 129

// Если это событие вызвал другой объект, но данный объект не был // изменен, перерисовка данного объекта выполняться не будет, if (hasChanged( )) {

draw( );

}

} -

private function draw ( ):void { graphics. clear( ); graphics. lineStyle(l); graphics. beginFill(OxFFFFFF. 1); graphics. drawEllipse(0, 0, w, h);

}

}

}

Заставим его двигаться!

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

Руководство по actionscript. часть 4, стр. 130

I Дополнительную информацию о системе обновления экрана среды выполнения Flash [%; А щ можно найти в дневнике Тиника Уро (Tinic Uro), инженера, работающего над созданием —SyJ приложения Flash Player. Этот дневник расположен по адресу: http://www. kaourantin. net/2006/05/frame-rates-in-flash-player. html.

ГЛАВА 24

Программная анимация

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

Никаких циклов

Чтобы создать анимацию на ActionScript, мы постоянно изменяем графическое содержимое с течением времени, создавая иллюзию движения. Например, чтобы анимировать объект TextField, перемещая его по экрану в горизонтальном направлении, мы будем постоянно увеличивать или уменьшать значение переменной экземпляра х этого объекта. В некоторых языках программирования естественным механизмом для постоянного изменения значения переменной экземпляра является инструкция цикла. Таким образом, новички в программировании на языке ActionScript могут подумать, что анимация создается с помощью цикла while, как та, что показана в следующем коде:

public class TextAnimation extends Sprite { public function TextAnimation ( ) { // Создаем объект TextField var t:TextField = new TextField( ); t. text = «Hello»:

t. autoSize = TextFieldAutoSize. LEFT: addChild(t):

// Циклически обновляем горизонтальную позицию объекта TextField // и останавливаемся в тот момент, когда объект достигнет // координаты 300 по оси х while (t. x <= 300) { t.x += 10;

}

}

}

Предыдущий цикл while многократно увеличивает значение переменной экземпляра х объекта TextField, но, как в варианте создания анимации, в нем допущена роковая ошибка: при выполнении тела цикла обновление экрана не происходит.

Руководство по actionscript. часть 4, стр. 131

На каждой итерации цикла горизонтальное положение объекта TextField изменяется, однако визуальный эффект данного изменения не отображается на экране. Визуализация изображения происходит только после завершения последней итерации цикла и выхода из функции-конструктора класса TextAnimation. Следовательно, в тот момент, когда произойдет визуализация изображения, объект TextField уже будет находиться в точке с координатой 300 по оси X.

В языке ActionScript инструкции цикла не могут быть использованы для создания анимации. Помните, что обновление экрана никогда не происходит внутри блока кода. Дополнительную информацию можно найти в подразд. «Никаких обновлений экрана внутри блоков кода» разд. «Запланированные обновления экрана» гл. 23.

В языке ActionScript анимация создается не с помощью циклов, а с помощью многократного вызова функций, которые вносят визуальные изменения и затем завершаются, позволяя выполнить обновление экрана. Существует два механизма для многократного вызова подобных функций: события Event. ENTER_FRAME и TimerEvent. TIMER.

Создание анимации с помощью события

ENTER FRAME

Среда Flash осуществляет диспетчеризацию события Event. ENTER_FRAME всякий раз, когда выполняется проверка запланированного обновления экрана (как было описано в гл. 23). Любая функция, зарегистрированная для получения уведомлений о возникновении события Event. ENTER_FRAME, выполняется многократно с частотой, определяемой текущей скоростью кадров среды выполнения Flash. Визуальные изменения, вносимые любой функцией-приемником события Event. ENTER_FRAME, отображаются сразу после завершения этой функции.

Функция может быть зарегистрирована для получения уведомлений о возникновении события Event. ENTER_FRAME из любого экземпляра класса Di splayOb j ect, независимо от того, находится в настоящий момент этот экземпляр в списке отображения или нет. В качестве примера используем событие Event. ENTER_FRAME, чтобы реализовать анимацию, рассмотренную в предыдущем разделе, — объект TextField перемещается по экрану в горизонтальном направлении до точки с координатой 300 по оси X. Начнем с создания класса TextAnimation, который создает объект TextField и затем добавляет его в список отображения.

public class TextAnimation extends Sprite { private var t:TextField:

public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»:

t. autoSi ze = TextFi eldAutoSi ze. LEFT:

addChild(t);

}

}

Теперь создадим функцию-приемник moveTextRight ( ) для события Event. ENTER_FRAME, которая перемещает объект TextField t вправо на 10 пикселов. Многократный вызов функции moveTextRight ( ) создаст эффект анимации. Обратите внимание, что, поскольку функция moveTextRight ( ) является функцией-приемником события Event. ENTER_FRAME, она определяет единственный обязательный параметр, типом данных которого является Event.

Руководство по actionscript. часть 4, стр. 132

Public function moveTextRight (e:Event):void { t. x += 10;

}

Наконец, внутри функции-конструктора класса TextAnimation мы регистрируем функцию moveTextRight ( ) для событий Event. ENTER_FRAME. Как только она будет зарегистрирована в качестве функции-приемника событий Event. ENTER_FRAME, она будет многократно вызываться с течением времени, в соответствии со скоростью кадров среды выполнения Flash. Новый код выделен полужирным шрифтом:

public function TextAnimation ( ) { // Создаем объект TextField t = new TextField( ); t. text = «Hello»;

t. autoSize = TextFieldAutoSize. LEFT; addChild(t);

// Регистрируем функцию moveTextRight( )

// для получения уведомлений

// о возникновении события Event. ENTERFRAME

addEventListenerCEvent. ENTER_FRAME. moveTextRight);

}

В листинге 24.1 продемонстрирован класс TextAnimation, дополненный методом moveTextRight ( ).



Полезные ссылки
Случайные записи
  • 24.07.2011">Медицинские справки можно получать еще быстрее
  • 22.01.2011">Руководство по actionscript. часть 1, стр. 129
  • 16.03.2011">Руководство по actionscript. часть 3, стр. 026
  • 12.04.2012">Новая модификация троянца Android.Gongfu скрывается в дистрибутиве Angry Birds Space
  • 15.06.2010">Самоучитель по креативному веб-дизайну. Книга 4, стр.36
  • 18.05.2010">Самоучитель по креативному веб-дизайну. Книга 2, стр.69
  • 15.06.2010">Самоучитель по креативному веб-дизайну. Книга 4, стр.46
  • 28.02.2011">Руководство по actionscript. часть 6, стр. 005
  • 06.02.2010">Вывод последней записи из твиттера
  • 24.02.2011">Руководство по actionscript. часть 7, стр. 010
  • 13.03.2011">Руководство по actionscript. часть 3, стр. 112
  • 01.03.2011">Руководство по actionscript. часть 5, стр. 113
  • 11.05.2010">Самоучитель по креативному веб-дизайну. Книга 1, стр.16
  • 02.06.2010">Самоучитель по креативному веб-дизайну. Книга 3, стр.98
  • 22.03.2011">Руководство по actionscript. часть 2, стр. 018
Опрос

Какие цвета вы предпочитаете?

View Results

Loading ... Loading ...