WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 || 3 | 4 |   ...   | 8 |

«F L A S 100 советов и рекомендаций профессионала O'REILLY С&ППТЕР Шам Бхангал F L A S H H A C K S Sham Bhangal O'REILLT Beijing • Cambridge • ...»

-- [ Страница 2 ] --

paefeEet public function die( ):Void // Завершающие действия Листинг получился довольно большим, поэтому мы рассмотрим его подробнее. Но сначала стоит ответить на один вопрос: почему методы пользовательского класса Transform не были добавлены в класс MovieClip или Color? В ActionScript 1.0 такое решение было бы вполне обычным и даже предпочтительным. Однако в ActionScript 2.0 вместо расширения существующих классов рекомендуется создавать пользовательские классы. Если вы еще не знакомы с синтаксисом ActionScript 2.0, обратите внимание на ключевое слово class, использованное для определения класса. В классе объявляется несколько переменных за пределами всех модулей. Статические свойства, также называемые свойствами уровня класса, определяются с ключевым словом static и существуют в единственном числе (в нашем примере таким образом инициализированы различные типы преобразований и интервал между обновлениями RATE, равный 50 мс). Остальные свойства, объявленные без ключевого слова static, являются свойствами экземпляров (то есть каждый экземпляр класса содержит собственную копию свойства). Ключевое слово private означает, что данное статическое свойство или свойство экземпляра недоступно за пределами класса. Переменные, объявленные внутри методов (такие, как переменная getTrans, объявленная внутри метода applyTransform), являются локальными. Типы данных всех переменных, свойств, параметров и возвращаемых значений методов задаются в синтаксисе «двоеточие + тип» (например, :Number). Хорошим тоном в программировании считается использование стандартизированных схем выбора имен переменных во всей программе, но благодаря точному Пользовательский класс цветового преобразования структурированию и типизации данных в ООП эти правила не столь важны. Имена констант (например, статических полей) записываются В ВЕРХНЕМ РЕГИСТРЕ, а для записи имен «полноценных» переменных используется схема «camelCase» (особая смесь символов верхнего и нижнего регистров). Теперь обратите внимание на функцию-конструктор TransformQ (в ActionScript 2.0 эта функция не обязательна), используемую для инициализации экземпляров класса. В нашем примере конструктору передается целевой клип, который позднее будет использоваться другими методами класса. Затем класс определяет несколько открытых методов, которые могут вызываться извне для экземпляров класса (скажем, invert() или fadeToWhiteO), а также приватных методов, предназначенных только для внутреннего использования. Обратите внимание на форму вызова setlntervalQ. В данном примере в первом параметре должен передаваться объект. Мы передаем ключевое слово this, представляющее текущий объект (то есть экземпляр класса Transform, для которого был вызван метод applyTransform()). Во втором параметре передается имя метода, вызываемого для this, а именно "transition" (имя должно задаваться именно так, в строковом виде). Итак, в положенный момент времени метод Transform.transitionQ будет вызван для текущего экземпляра this. Вызов метода для текущего экземпляра гарантирует, что свойства экземпляра (такие, как interval и colorObj) будут доступны внутри transitionQ. Четвертый и пятый параметры setlnterval(), diffTrans и duration, передаются методу transitionQ при его вызове. Метод transition() выполняет заданный переход за заданный промежуток времени и сбрасывает интервал таймера после завершения. Чтобы использовать класс в своей программе, сначала создайте экземпляр класса Transform, как показано далее (где myVideojnc - существующий анимационный клип, имя экземпляра которого было задано на панели свойств): var transformer:Transform = new Transform(myVideo_mc): Затем вызовите для полученного объекта transformer нужные методы класса: transformer.invert(3000);

// Инверсия цветов на 3 секунды transformer.fadeToWhite(2000);

// Растворение на белом фоне за 2 секунды Зачистка после завершения работы с объектом выполняется следующим фрагментом: transformer.die(): delete transformer;

Попробуйте усовершенствовать этот класс и реализовать в нем дополнительные возможности: • предоставьте пользователю возможность изменять значение RATE;

• добавьте элементы для повторения перехода. Это позволит применять мигание и другие повторяющиеся эффекты;

• определите более сложные методы для реализации растворения одного клипа в другом.

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

Глава 2. Цветовые эффекты Некоторым разработчикам объектно-ориентированный стиль ActionScript 2.0 может показаться недостаточно компактным, поскольку определение структуры кода содержит едва ли не больше строк, чем непосредственное решение задачи (это особенно заметно для небольших классов). Однако в долгосрочной перспективе дополнительное структурирование имеет свои преимущества: гибкость кода упрощает его использование в разных приложениях и передачу другим пользователям (последнее особенно удобно в рабочих группах, состоящих из одного программиста в ActionScript и нескольких дизайнеров, не владеющих навыками программирования). И еще одно обстоятельство: хотя объектно-ориентированный код вроде бы занимает больше места, чем в других стилях программирования, откомпилированный байт-код может оказаться более эффективным (см. трюк 100). Flash Player 7 оптимизирован для объектно-ориентированного кода. Сравнительный хронометраж хорошо написанного объектно-ориентированного и процедурного кода Action Script показывает, что ООП повышает быстродействие за счет усиленного использования локальных переменных и передачи данных в аргументах: и то, и другое повышает эффективность сгенерированного байт-кода.

Создание и упорядочение № 1 1 пользовательских каталогов цветов ТРЮК Сохранение и организация каталогов цветов без использования панели Color Swatches. Цветовая схема является одним из важнейших факторов, определяющих общее впечатление и эмоциональное воздействие Flash-сайта. Текущую палитру с панели Color Swatches можно сохранить в виде набора цветов Flash (CLR-файл). Тем не менее, работать с цветами на панели Color Swatchers (например, сгруппировать их удобным для вас способом) не так легко, как хотелось бы. Конечно, ничто не мешает вам создать собственный каталог цветов. Просто создайте слой с именем swatches, преобразуйте его в опорный слой (команда Modify • Timeline • Layer Properties • Type • Guide) и разместите несколько прямоугольников для хранения цветов. Художник выделяет отдельный участок палитры и пробует на нем краски, прежде чем наносить их на холст;

вы тоже можете создать сколько угодно цветовых образцов и упорядочить их по своему усмотрению. Для изменения или получения цвета конкретного образца используются обычные инструменты: «ведро с краской» и «пипетка». На рис. 2.5 набор цветов сохраняется в виде цветового каталога, находящегося вне сцены в документе FLA. Поскольку образцы расположены на опорном слое, они не экспортируются в итоговый SWF-файл. Конечно, остается еще одна проблема: как перенести нужные цвета в Flash?

Импортирование цветов На панели Color Swatches находится пережиток прошлого, динозавр из давно ушедшей эпохи - веб-безопасная палитра. В наше время веб-безопасная палит Создание и упорядочение пользовательских каталогов цветов ра практически не используется. Если компьютер не способен отображать более 256 цветов, скорее всего, он не сможет поддерживать Flash Player (исключение могут составлять некоторые карманные компьютеры). Более того, веб-безопасная палитра спроектирована для работы на оборудовании с поддержкой палитр с 8-, 16- или 32-разрядной кодировкой цвета. Веббезопасные цвета могут неточно отображаться на компьютерах, настроенных на отображение 24-разрядного цвета. Используя градиентные заливки во Flash, вы уже выходите за рамки веб-безопасных цветов, даже если цвета, определяющие градиент, были выбраны из веббезопасной палитры. Для создания цветовых палитр можно использовать Photoshop (или Fireworks). На вкладке Swatches в Photoshop (рис. 2.6) могут отображаться многие палитры, не только веб-безопасные;

кроме того, существует много заготовок палитр, ориентированных на печать.

Рис. 2.5. Сохранение цветовых образцов за пределами сцены на опорном слое 0 Color v Svatches \_Styles \ ш I,„]„.] щ Ш Ш ш ш щ :

щ я щ Щ j эЬ :•s -i ;

: | 1. :

.: •>-. WfiB"-' П Ч| Рис. 2.6. Вкладка Swatches в программе Photoshop К сожалению, Flash не поддерживает импортирование «родных» файлов Photoshop в формате АСО (Adobe COlor). He беспокойтесь: и Flash, и Photoshop поддерживают другой, скрытый формат палитр, а именно файлы ACT (Adobe Color Table).

Глава 2. Цветовые эффекты АСТ-файлы могут создаваться только на базе изображений с индексированными цветами, обычно GIF или PNG-8. Чтобы создать такую палитру в Photoshop, выполните следующие действия: 1) создайте изображение;

2) выполните команду File • Save for Web;

3) выберите формат GIF или PNG-8 в разделе Settings диалогового окна Save for Web;

4) выберите в раскрывающемся списке Colors количество цветов, которые должны присутствовать в палитре;

5) чтобы сохранить палитру, щелкните на кнопке с треугольником в правом верхнем углу окна (рис. 2.7) и выберите в открывшемся меню Options команду Save Color Table.

Lossy. 0 Perceptual U Diffusion [vj Dithtr I ООН > No Transpar»,. V j Amount Рис. 2.7. Сохранение цветовой палитры в Photoshop Чтобы загрузить цветовую таблицу в Flash, выберите команду Add Colors в меню Options панели Color Swatches. Импортированные цвета присоединяются в конец текущего каталога цветов. Существует и другой, гораздо более простой способ импортирования цветов из Photoshop: нарисуйте серию образцов в Photoshop, используя однородную кисть или аэрограф (рис. 2.8), затем импортируйте растровое изображение в формате без потери данных (PNG-32 или TIFF) в Flash.

>•#» Рис. 2.8. Изображение, используемое для перенесения цветовой палитры из Photoshop в Flash Использование естественных цветовых схем Инструмент «пипетка» распознает отдельные пикселы растра;

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

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

Использование естественных №12 цветовых схем ТРЮК Создание цветовых комбинаций и схем на базе реальных изображений. Панель Color Mixer нельзя назвать идеальным способом создания новых цветовых палитр (см. трюк 11). Человеческий глаз относительно плохо различает цвета. Например, темно-красный цвет в окружении белых цветов покажется более темным (и даже черным), чем в окружении других темных цветов, и это вполне естественно, поскольку в процессе эволюции глаз научился выделять не абсолютные цвета, а относительные различия между цветами, отображаемыми в настоящий момент. С учетом сказанного иногда бывает проще выбирать цветовые палитры на основе изображений, взятых из реальной жизни. Например, на рис. 2.9 цвета выбираются из изображений лезвия ножа и цветочных лепестков. Оба изображения были получены на недорогом планшетном сканере. Такой способ гораздо быстрее поиска каталогов веб-безопасных цветов в Веб и намного дешевле приобретения книги цветовых образцов для многокрасочной печати. Но при попытке выбрать цвет из любого из этих изображений немедленно возникают проблемы. Дело в том, что цвета соседних пикселов сильно различаются;

вы пытаетесь выбрать цвет из желтого лепестка, а получаете, к примеру, светло-зеленый образец. Чтобы упростить выбор цветов, следует преобразовать изображение и сделать его более подходящим для применения «пипетки». Проще всего это делается при помощи некоторых фильтров Photoshop. Для уменьшения шума в оцифрованном изображении применяется фильтр Filter • Noise • Despeckle. Цвета становятся более однородными, но проблемы с точным выбором цвета из естественного изображения все равно остаются. К счастью, существует фильтр, обеспечивающий нужный результат. Выполните команду Глава 2. Цветовые эффекты Filter • Pixelate • Pointillize. Изображение разбивается на отдельные пятна, напоминающие мазки краски на палитре художника (рис. 2.10).

Рис. 2.9. Отсканированные изображения ножа (оттенки серого цвета) и цветов (живые естественные краски) Р'-ТТДШВДК*? : '/'Ы Г1' •.,.:.••...:...•..,•.•.

Рис. 2.10. Часть изображения до применения фильтра пуантилизации (слева) и после него (справа) Сохраните полученный результат в формате без потери данных, поддерживаемом Flash (например, PNG-32 или TIFF), и импортируйте его в Flash. Расположите изображение вне сцены на guide layer, чтобы оно не экспортировалось в итоговый SWF-файл. Теперь у вас имеется естественная палитра, причем сходные цвета расположены в ней вблизи тех цветов, с которыми они соседствуют на реальном объекте. Чтобы выбрать цвет из каталога, воспользуйтесь «пипеткой», как показано на рис. 2.11.

Имитация эффекта сепии Рис.

2. 1 1. Выбор цвета из растрового каталога цветов с применением панели Color Mixer и инструмента Eyedropper Итоги Проектирование цветовых схем в Flash (и во многих других приложениях) нередко производится методом проб и ошибок, поскольку цвета, выбираемые по отдельности, воспринимаются совсем не так, как в окружении других цветов. Сканирование изображения реально существующего объекта, уже содержащего желаемую цветовую схему, и его преобразование в цветовой каталог повышает точность выбора цветов, так как последний осуществляется в контексте. Утилита Gliftic автоматически строит цветовые схемы на базе изображений (http://www.ransen.com/ Gliftic/Gallery/Natural-Color-Schemes.htm).

На основе работ Джошуа Дэвиса и других ТРЮК Имитация эффекта сепии Имитация эффектов тона/насыщенности в Flash для создания изображений в сепийной цветовой гамме.

№ Многие графические редакторы, в том числе Photoshop и Fireworks, позволяют изменять тон и насыщенность цветов изображения для создания нестандартных цветовых гамм, в том числе и цветовой гаммы сепии. Flash тоже предоставляет такую возможность, хотя с первого взгляда это может быть неочевидно. В этом Глава 2. Цветовые эффекты трюке вы узнаете, как вручную создать эффект сепии в Photoshop и затем преобразовать результат в Flash.

Создание эффекта сепии в Photoshop Сепийная цветовая гамма имитирует вид сепийных фотографий (также называемых альбуминными), печатавшихся во второй половине XIX века по технологии, предложенной Луи-Дезире Бланкар-Эвраром в 1850 году. Темно-коричневая тональность старых фотографий вызвана не старением, а исходным процессом проявки. Дополнительную информацию об альбуминной печати можно найти на сайте Королевского фотографического общества (http://www.rps.org/book/terms/ albumen.html). Если в вашем графическом редакторе предусмотрена встроенная поддержка эффекта сепии, задача решается элементарно. Например, в Fireworks следует открыть изображение и выполнить команду Commands • Creative • Convert to Sepia Tone. В этом разделе будет рассмотрен более общий технологический процесс в Photoshop, не ограничиваемый сепийными тонами. Откройте изображение в Photoshop и выполните команду Image • Adjustments • Hue/Saturation. На экране появляется окно Hue/Saturation, показанное на рис. 2.12. Установите флажки Colorize и Preview.

•. щшш ;

•' • I O l > ?| * | EPreview I Рис. 2.12. Окно Hue/Saturation в Photoshop Процесс создания сепийной тональности состоит из двух шагов: 1) снижения насыщенности вплоть до получения черно-белого изображения;

2) раскрашивания изображения определенным оттенком (темно-коричневым для создания эффекта сепии). Для снижения насыщенности цветов можно перевести ползунок Saturation в крайнее левое положение, но это перебор - нужно оставить немного цветовой информации для применения цвета. Оставьте его в положении 25. Чтобы окрасить изображение, переведите ползунок Hue в позицию темно-красно-коричневых оттенков (от 0 до 30). Оригиналы сепийных фотографий имеют слегка розоватый оттенок, поэтому значения тона в интервале от 330 до 350 тоже можно считать допустимыми.

Имитация эффекта сепии Воспроизведение эффекта в Flash Цветовые эффекты Flash обеспечивают изменение цветов, но не поддерживают снижения насыщенности, поскольку класс Color использует модель RGB, а следовательно, не может легко отделить цвет от яркости. Единственным способом удаления всех цветов является радикальное увеличение или уменьшение яркости, однако оно приводит к неприятному побочному эффекту - изображение растворяется на белом или черном фоне! Итак, обработка должна начаться с изображения, у которого насыщенность уже была снижена. В Photoshop эта задача решается командой Image • Adjustments • Desaturate. Импортируйте обработанное изображение в Flash, разместите его на сцене. Выделите изображение и преобразуйте его в символ анимационного клипа (F8). Это позволит применять к изображению цветовые эффекты и использовать его с экземплярами Color. Далее следует найти нужный цвет. На панели Color Mixer (Window • Design Panels • Color Mixer) выберите в меню Options режим HSB;

введите значение Hue, использовавшееся в Photoshop (30 в нашем примере), и увеличьте значения Saturation и Brightness на 50 и 75 % соответственно (рис. 2.13). Оставьте панель Color Mixer открытой.

Н;

3D RGB Add Swatch Help Close Panel J M i l y*!ii 50% В 75% Alphati 100% гv Рис. 2.13. Панель Color Mixer Выделив анимационный клип с изображением, задайте на панели свойств параметру Color значение Tint. Задайте параметру Tint Amount (справа от цветового образца) значение 25%. Выделите образец и щелкните на цвете, созданном на панели Color Mixer. Изображение превращается из черно-белого в сепийное. Хотя Flash-версия эффекта сепии сохранит некоторую долю базовых цветов, по точности передачи светлых областей она уступает версии Photoshop. Это связано с тем, что Photoshop выполняет гораздо более сложную обработку цветов для сохранения яркости. В этом проявляется недостаток эффектов реального времени по сравнению с предварительным обсчетом: Flash приходится работать быстро, Глава 2. Цветовые эффекты поэтому не стоит рассчитывать на то, что изменение цветов будет выполнено так же точно, как в Photoshop. Наконец, чтобы воссоздать этот эффект в ActionScript, задайте параметру Color значение Advanced, щелкните на кнопке Settings, запомните цветовые значения и используйте их для определения цветовых преобразований на стадии выполнения (см. трюк 10).

Эффект сепии в статической графике Применение эффекта сепии и других цветовых эффектов на стадии выполнения делает изображение более оригинальным без существенного увеличения размеров SWF-файла. В комбинации с другими эффектами это существенно изменяет изображения. Например, обрезка изображения по круглой границе с размывкой краев (рис. 2.14) скрывает прямоугольную форму исходного изображения (рис. 2.15), а также предотвращает появление артефактов, возникающих при прямой обработке изображения и его экспортировании с низким качеством, характерным для использования в Веб.

Рис.

2.14. Создание границы кадра в виде круглого отверстия с размытыми краями Конечно, кроме сепийных тонов изображения можно раскрашивать и другими базовыми цветами. Например, применение синей колоризации с добавлением линий и фигур в стиле «техно» придает изображению более современный стиль (рис. 2.16). Более того, кроме согласования изображения с общим дизайном сайта, наложение векторных элементов на изображение способно добавить динамики и замаскировать пиксельные артефакты.

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

Рис. 2.15. Отображение портрета в сепийных тонах через круглое отверстие Рис. 2.16. Изображения в стиле «техно» создаются в синей цветовой гамме и содержат наложенный текст Глава 2. Цветовые эффекты Динамический эффект сепии и класс Color Анимация эффекта сепии производится по тем же принципам, что и добавление цветовых эффектов в видео (см. трюк 10). В комбинации с эффектом старой пленки (см. трюк 3) создается впечатление, что перед вами не статическое изображение, а стилизованный видеоклип. Эффект сепии и другие цветовые эффекты позволяют обрабатывать графику на стадии выполнения. Область применения цветовых эффектов не ограничивается одним растровым изображением. Применяя их ко всему SWF-файлу за счет применения к _root, вы сможете быстро изменить цветовую схему сайта. А если объединить эту возможность с заменой сепии на накладку в стиле «техно», вы измените стиль оформления сайта. Цветовые эффекты также могут использоваться для обозначения разных состояний - например, цветом можно выделить недоступные элементы интерфейса или миниатюры в каталоге графики, загруженные и готовые к просмотру.

ГЛАВА Рисование и маски Трюки № 14- Говоря о «рисовании» в Flash, мы нередко говорим о двух разных вещах. Первая, более традиционная форма рисования - рисование «от руки» - используется художниками, создающими анимацию на Flash. Во второй форме графика создается при помощи сценариев;

я называю ее «кинетическим рисованием». Кинетическое рисование не сводится к перемещению по экрану графических объектов вы также можете создавать графику в реальном времени и отображать промежуточные результаты для пущего эффекта. Рисование в реальном времени осуществляется средствами так называемого Drawing API - набора методов класса MovieClip, в том числе lineStyle(), moveTo(), lineToQ, beginFillQ и т. д., предназначенных для создания линий и заливок. Аниматор классической школы найдет в этой главе полезные приемы для решения стандартных проблем - таких, как сокращение пикселизации вокруг краев растрового изображения (см. трюк 23), обеспечивающее эффективное объединение растровой графики с векторным содержимым и ликвидацию неровностей на линиях. Разработчик сценариев узнает, как решать некоторые стандартные проблемы Flash, в том числе проблему неточности свойства _alpha (см. трюк 19) и «сдвига пикселов» в растровых изображениях (см. трюк 24). Также здесь описаны приемы создания стандартных «строительных блоков» при динамическом построении графического контента (скажем, рисование круга из прямой линии см. трюк 14). Навыки рисования в Flash абсолютно необходимы как художникам, так и разработчикам сценариев, поэтому представленные трюки должны быть доступными для всех категорий пользователей Flash. Опытные Flash-разработчики не удивятся тому, что некоторые из трюков основаны на использовании масок. Если вы еще не знакомы с масками, обратитесь к краткому введению в начале главы 1.

Глава 3. Рисование и маски Быстрое построение кругов №14 с заливкой ТРЮК Рисование кругов с заливкой на стадии выполнения требует интенсивной работы процессора. Рисование кругов из прямой линии повышает быстродействие и обеспечивает большую гибкость по сравнению с инструментами, применяемыми на стадии разработки.

Нарисовать прямоугольник с заливкой средствами Drawing API относительно легко - вы определяете четыре угловые точки, и находящаяся между ними область заполняется автоматически. С кругами дело обстоит сложнее. Вам придется либо аппроксимировать кривизну круга многочисленными отрезками, либо создать серию дуг методом MovieClip.curveToQ. В обоих случаях тригонометрические вычисления замедляют работу программы и безнадежно усложняют код для тех, кто слабо разбирается в синусах и косинусах. И все же не стоит огорчаться, в Flash существует очень простой способ рисования кругов с заливкой для этого достаточно нарисовать всего одну прямую линию. Каждый раз, когда вы рисуете прямую, ее концы закругляются (рис. 3.1).

Щ НИ 163. 3. 1. Короткая линия с закругленными концами Рис.

Вероятно, вы подумали: «Понятно, к чему он клонит. Если нарисовать достаточно короткую линию, закругленные концы соприкоснутся, и получится круг, верно?» В каком-то смысле. Инструменты Pencil и Line не позволяют нарисовать достаточно короткую линию и ограничивают толщину линий значением 10, поэтому нарисовать достаточно большой круг таким способом не удастся. К тому же рисовать круги нужно именно на стадии выполнения, поэтому трюк использует ActionScript для рисования очень коротких, очень толстых линий. Попробуйте выполнить следующий фрагмент: var clip:MovieClip = this.createEmptyMovieClip("circlejnc". thi s.getNextHi ghestDepth());

circle_mc._x = circ1e_mc._y = 150;

circle_mc.lineStyle(200. 0x0, 100): circle_mc.moveTo(0, 0);

circle_mc.lineTo(0.2. 0);

Он рисует круг, изображенный на рис. 3.2. Круг представляет собой отрезок длиной всего 0,2 единицы и толщиной 200 единиц. Flash рисует два закругленных конца этой очень короткой линии так Быстрое построение кругов с заливкой близко, что в результате получается почти идеальный круг диаметром 200 единиц.

Рис. 3.2. Очень короткая линия, закругленные концы которой образуют круг Этот трюк можно применять практически в любой ситуации, когда потребуется динамически нарисовать круг.

Программа Следующая программа (файл dynaButton.fIa на сайте книги) строит меню, в котором круглые кнопки используются в качестве маркеров команд. Жирным шрифтом выделен фрагмент, создающий «псевдокруги» в клипе:

function createButton(dynaButton. dynaLabel. depth, x. у) { var c l i p = this.createEmptyMovieClip(dynaButton. depth);

clip.lineStyle(15. 0x0, 100);

clip.moveTo(0, 0 ) ;

clip.lineTo(0.2, 0);

c l i p. _ x = x: c l i p. _ y = y;

var txt_fmt:TextFormat = new TextFormatO;

txt_fmt.font = "_sans";

txt_fmt.size = 12;

this.createTextField(dynaButton + " _ t x t ". depth + 1. x + 10. у - 10. 100. 20);

var textLabel = this[dynaButton + " _ t x t " ] ;

textLabel.text = dynaLabel;

textLabel.setTextFormat(txt_fmt);

} createButton("home_btn". "home". 1. 100. 10);

createButton("products_btn", "products". 3, 100. 30);

createButton("about_btn". "about us". 5. 100. 50);

createButton("links_btn". "links we l i k e ". 7. 100. 70);

home btn.onRelease = functionO { 90 // Необходимые действия products_btn.onRelease = function О // Необходимые действия about_btn.onRelease = f u n c t i o n O Глава 3. Рисование и маски // Необходимые действия 1 inks_btл.onRelease = functionO { // Необходимые действия }: При выполнении этого кода на экране появляется динамически сгенерированное меню, изображенное на рис. 3.3.

ф Ф ф Ф home products about us links we like Рис. 3.3. Использование «псевдокругов» в качестве маркеров меню Усовершенствование трюка Чтобы код стал более универсальным, можно усовершенствовать идею и написать класс создания кнопок на ActionScript 2.0: // Этот к д ActionScript 2.0 должен храниться о / в внешнем файле CreateButton.as /о class CreateButton { // Переменная target определяет временную диаграмму, н которой а / CreateButton будет создавать кнопки. / private var target:MovieClip: // Конструктор public function CreateButton(targetTimeline:MovieClip) { target— targetTimeline;

// Определение метода createBtnO // Аргументы: // buttonName - имя экземпляра создаваемой кнопки // dynaLabel - надпись н создаваемой кнопке а // depth - глубина кнопки // х, у - координаты создаваемой кнопки. // Возвращаемое значение: // Анимационный клип кнопки с. именем экземпляра buttonName. public function createBtnCbuttonName:String. dynaLabel:String, depth:Number, x:Number, y.• Number):MovieClip { // Инициализация var clip:MovieClip:

Быстрое построение кругов с заливкой var clipMask:MovieClip: var txt_fmt:TextFormat;

var clipText:TextField;

// Создание клипа для кнопки clip = target.createEmptyMovieClip(buttonName. depth);

drawPip(clip);

clip._x = x;

clip._y = y: // С з а и о л с и п и а л ж о т к к о к одне бат рнденси нпе c i M s = c i. r a e m t M v e l p " a k. 0);

lpak lpcetEpyoiCi(ms" ciMs.v sbe= fle lpak_i il as;

drawPip(clipMask);

c i. i A e =c i M s ;

lphtra lpak, // С з а и о ъ к а T x F r a д я п и е е и ф р а и о а и одне бет etomt л рмння омтрвня t t f t = n w TextFormatO;

x_m e txt_fmt. f n = " s n " ot _as;

t t f t s z = 12;

x_m.ie // С з а и т к т в г п л ( о е т подписи) о д н е е с о о о оя т сь c i. r a e e t i l C u t n a e + " t t, 1 10. -10. 100. 20);

lpcetTxFedbtoNm _x". c i T x = c i [ u t n a e "_txt"};

lpet lpbtoNm+ ciTx.et= dnLbl lpettx yaae: ci p e t s t e t o m t t t f t ;

l Tx.eTxFra(x_m) r t r clip: eun } private function drawPip(clip);

Void { clip.lineStyle(15. 0x0. 100): clip.moveTo(0. 0);

clip.lineTo(0.2. 0);

Чтобы использовать класс CreateButton, сохраните этот код в файле с именем CreateButton.as. Затем создайте новый файл с расширением.fla в одном каталоге с CreateButton.as. Создайте в этом файле новый экземпляр класса Create Button: var buttonGen:CreateButton = n w CreateButton(this);

e При конструировании экземпляра CreateButton передается один аргумент - временная диаграмма (то есть главная временная диаграмма или анимационный клип), на которой будут создаваться кнопки. После создания экземпляра CreateButton кнопки на целевой диаграмме создаются вызовами метода CreateButton.createBtn(): var home:MovieClip = buttonGen.createBtn("home", "home". this.getNextHighestDepthO. 100. 10);

var products:MovieClip = buttonGen.createBtn("products", "products". this.getNextHighestDepthO. 100. 30);

var about:MovieClip = buttonGen.createBth("about", "about us", this.getNextHighestDepthO. 100. 50):

Глава 3. Рисование и маски var links:MovieClip = buttonGen.createBtn("links", "links we like". this.getNextHighestDepthO. 100. 70);

home.onRelease = functionO { traceC'You clicked the home button");

products. onRel ease = functionO { traceC'You clicked the products button");

about. onRel ease = functionO { traceC'You clicked the about button");

links.onRelease = functionO { traceC'You clicked the links button"): Программа создает те же кнопки, что и предыдущий листинг, но обладает рядом преимуществ: • создаваемые кнопки лучше структурированы. На этот раз текст подписи хранится внутри клипа кнопки. Чтобы щелчок мышью мог осуществляться только на кнопке (а не на тексте подписи), метод createBtnQ также создает клип маски mask, определяющий круг как область, на которой можно щелкать;

• программист сам выбирает местонахождение создаваемых кнопок. Для этого при создании нового экземпляра CreateButton задается временная диаграмма, на которой должны создаваться кнопки. Многие пользователи жалуются на то, что в ситуациях, когда достаточно простых кнопок и полос прокрутки (самых распространенных компонентов пользовательского интерфейса), компоненты Flash MX 2004 «раздуты» ненужной функциональностью (см. трюк 73). Приведенный листинг демонстрирует простое решение - определение пользовательского класса для создания компонента! Такое решение по компактности превосходит не только компоненты Flash MX 2004 v2, но и более старые компоненты Flash MX vl. Конечно, этот нетривиальный трюк с построением круга существенно упрощает генерирование компонента на стадии выполнения и его прорисовку. ПРИМЕЧАНИЕ После компиляции наш класс кнопки занимает менее 1 Кбайт — он состоит только из программного кода и поэтому хорошо сжимается. Благодаря этой особенности он хорошо подходит для сайтов, которые должны занимать как можно меньший объем (например, рассчитанных на мобильные устройства), а также для конкурсов на самую компактную программу.

Круги также часто используются в графических Flash-приложениях и при работе с трехмерными каркасными моделями для представления точек, которые могут перетаскиваться мышью. Пример приведен в следующем листинге (файл dynaPoint.fla на сайте книги): function createPoint(dynaPoint. depth, x, у) { c l i p = this.createEmptyMovieClip(dynaPoint. depth);

clip.lineStyle(20. 0x0. 100);

clip.moveTo(0, 0);

Синтетическая графика c1ip.lineTo(0.2. 0);

clip._x = х;

d i p. _ y = у;

} function drag О { this.startDrag(true);

paper.onMouseMove = drawLine;

this.onPress = drop: } function dropO { this.stopDrag(true);

delete (paper.onMouseMove);

this.onPress = drop;

} function drawLineO { this.clearO;

t h i s. l i n e S t y l e ( 2. 0x0. 100);

this.moveTo(pointl._x, p o i n t l. _ y ) ;

this.lineTo(point2._x, point2._y);

updateAfterEventO;

} // Пример использования: createPointC'pointl". 1. 100. 100): createPoint("point2". 2. 120. 100): pointl.onPress = drag;

point2.onPress = drag;

this.createEmptyMovieClip("paper". 0);

Протестируйте пример: щелкните на точке и перетащите ее, как показано на рис. 3.4. Повторный щелчок останавливает перетаскивание.

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

Синтетическая графика Вместо загрузки больших растровых изображений или создания готовых макетов заставьте Flash генерировать изображения «на ходу». Как известно, компьютеры умеют генерировать песни на основании небольшого набора музыкальных правил. Точно так же их можно заставить генерировать простые изображения на основе нескольких правил построения макета и размещения графических объектов.

_^ Глава 3. Рисование и маски Как и во всем «компьютерном творчестве», художественные способности потребуются в первую очередь программисту, а не компьютеру. Программист сначала создает завершенные, но взаимозаменяемые фрагменты, а затем поручает компьютеру собрать из них готовое произведение. Далее представлена типичная разновидность этого трюка, автором которой является Энтони «Ant» Идеи (a.k.a. arseiam). Его работа основана на построении сетки из клипов, каждый из которых содержит простые геометрические фигуры.

Листинг Изображение строится из нескольких секций, каждая из которых находится на ключевом кадре анимационного клипа node. Примеры секций представлены на рис. 3.5. Исходный текст программы хранится в файле antart.fla на сайте книги.

Рис. 3.5. Фигуры для синтетического изображения Следующий фрагмент генерирует сетку 12 х 8 из наших узловых клипов каждый раз, когда пользователь щелкает на сцене, и останавливает каждую копию на кадре со случайным номером: this.onMouseDown = function О { var depth = 0;

for (var i = 0;

i < 12: i++) { for (var j = 0: j < 8: j++) { var m = "n" + i + j ;

e this.attachMovieC'node", me. depth++);

this[me]._x = 50 * i ;

this[me]._y = 50 * j : this[me].gotoAndStop(random(100) + 1):

Фрагмент строит случайную сетку из фигур;

при этом создаются узоры вроде изображенного на рис. 3.6. Чтобы узор получился более сложным, можно вложить в него дополнительные копии node. Каждая копия node содержит следующий код: i f (Math.ceil (Math.randomO < 0.80)) { this.attachMovieC'node", "n". 0): this["n"]._x = 50*i: this["n"]._y = 50*j;

this["n"]._xscale = n._yscale = _parent._xscale / 1.5;

thi s["n"].gotoAndStop(Math.cei1((Math.random()*10))):

Синтетическая графика Рис. 3.6. Случайный узор, сгенерированный из базовых фигур Если проверяемое условие истинно (а это происходит в 80% случаев), секция if создает уменьшенную копию node поверх текущего экземпляра node. Создание уменьшенной фигуры делает внешний вид текущего узла менее тривиальным. Внутренняя копия тоже содержит этот фрагмент, поэтому с вероятностью 80% в ней создается еще меньшая версия node. Это приводит к дальнейшему усложнению узора. Таким образом, узор является случайным по двум факторам - программа изменяет фигуру в каждом узле сетки и случайным образом выбирает количество вложенных фигур на каждом узле. Один из возможных результатов показан на рис. 3.7.

liliilil Рис. 3.7. Случайный узор, сгенерированный из базовых фигур Глава 3. Рисование и маски Итоги В примерах продемонстрированы два стандартных приема создания синтетической графики: рекурсия (рисование постепенно уменьшающихся копий одного клипа внутри текущего узла) и случайное размещение. Создав набор фигур, которые хорошо сочетаются друг с другом независимо от размещения и вложения, Энт Идеи создал узор, который всегда выглядит естественно, словно рисунок был обдуман заранее. " Конечно, Энт действительно все «обдумал заранее» - но лишь при проектировании форм, рассчитанных на хорошее объединение друг с другом! Именно этот принцип заложен в основу всего «компьютерного творчества» программист проектирует серию фраз или визуальных образов, хорошо комбинируемых друг с другом (или определяет правила построения случайных последовательностей), а затем предлагает программе показать, что же из этого выйдет! Спасибо Энту Идену за творческую идею и разрешение на использование графики ТРЮК Мозаичное заполнение плоскости Создать мозаичные пластинки, идеально прилегающие друг к другу, проще, чем кажется на первый взгляд. Даже человек, лишенный художественных способностей, сможет создавать такие мозаики, используя очень простые приемы.

№ На протяжении всей книги я стараюсь подчеркнуть, что для создания привлекательного контента не обязательно быть профессиональным музыкантом или художником. Данный трюк оценят как художники, так и математики - в нем показано, как создать графические элементы, которые могут использоваться при построении синтетических изображений (см. трюк 15).

Формирование мозаики Принцип создания идеальных мозаик на удивление прост. Начните с однородной сетки из треугольников, квадратов или шестиугольников (см. рис. 3.8), а затем измените каждую плитку и превратите ее во что-нибудь более интересное.

Рис.

3.8. Разбиение плоскости на одинаковые мозаичные плитки \ Мозаичное заполнение плоскости Начнем с мозаики из квадратных плиток, потому что с ними проще всего работать. Чтобы создать более интересный узор, можно вырезать или просто затемнить часть плиток. Для создания квадратной решетки можно воспользоваться средствами привязки Flash (View • Grid • Show Grid and View • Snapping • Snap to Grid). Нарисуйте на сцене темный круг, полностью закрывающий одну ячейку, несколько раз продублируйте его и заполните все квадраты в области 2 x 2 (рис. 3.9).

Рис. 3.9. Первая фаза построения идеальной мозаики Созданные позитивные и негативные области на сетке 2 x 2 преобразуются в узор при помощи различных инструментов выделения, создания заливок и контуров, как показано на рис. 3.10.

Рис. 3. 1 0. Применение контуров и заливок для создания фигур На рис. 3.11 создаются простые круговые и ромбовидные формы вроде тех, что использовались при построении синтетической график№(см. трюк 15).

-ФРис. 3. 1 1. Некоторые узоры, полученные применением контуров и заливок к позитивным и негативным областям Глава 3. Рисование и маски Создав несколько дополнительных ромбовидных фигур, можно преобразовать плитку 2 х 2 в повторяющийся узор, показанный на рис, 3.12.

Рис. 3.12. Повторяющийся ромбовидный узор Далее в полученном узоре выделяется одна плитка (рис. 3.13), которая позднее используется для эффективного воссоздания узора (рис. 3.14).

Рис. 3.13. Плитка, используемая для формирования узора Рис. 3.14. Создание мозаики посредством ручного размещения плиток Узорные заливки Итоги Конечно, ручное размещение плиток - не самый удобный способ;

к тому же плитки относительно бесполезны, если мы не сможем использовать их для заполнения фигур и областей по своему выбору. В трюке 17 будет показано, как организовать мозаичное заполнение фигур. Но прежде чем следовать дальше, попробуйте создать несколько собственных узоров - например, заменив круги треугольниками в квадратных ячейках. Также попробуйте начать с прямоугольных ячеек или других фигур, изображенных на рис. 3.8.

ТРЮК Узорные заливки Flash не обладает средствами для заполнения непрямоугольных областей пользовательскими мозаичными узорами на стадии выполнения. Тем не менее, применение маски позволяет заполнить область произвольной формы узорной заливкой.

№ Во Flash, как и в большинстве графических редакторов, существует специальный инструмент для заполнения произвольных областей однородными и градиентными заливками. С точки зрения Flash, заливка существует отдельно от границы фигуры (контура). Инструмент Paint Bucket использует цвет заливки, заданный образцом Fill Color в секции Colors палитры Tools (Window • Tools). Например, чтобы выбрать градиентную заливку, щелкните на образце Fill Color и выберите в открывшемся меню нужный тип градиентной заливки. Далее панель Color Mixer используется для настройки градиента. Выделите заливку инструментом Selection, затем выполните команду Window • Design Panels • Color Mixer. На панели Color Mixer из меню выбирается тип градиента - линейный (Linear) или радиальный (Radial). Вы даже можете выбрать растровую заливку (Bitmap), и тогда Flash предложит выбрать растовое изображение для мозаичного заполнения области. Таким образом, выбрать заливку области на стадии разработки относительно несложно. Более того, градиентные и растровые заливки можно модифицировать (масштабирование, перекос, повороты и преобразования) при помощи инструмента Fill Transform из палитры Tools. Тем не менее, средства определения заливок в ActionScript более ограничены. Методы fill() и beginGradientFill() класса MovieClip, входящие в Drawing API, позволяют создавать на стадии выполнения однородные, линейные градиентные и радиальные градиентные заливки, но мозаичные заливки не поддерживаются. Ранее было показано, как создать узор многократным повторением базовой «мозаичной плитки» (см. трюк 16). Давайте посмотрим, как использовать эту возможность для заполнения разных фигур нашим узором.

Заполнение прямоугольной области Допустим, в диалоговом окне Symbol Linkage анимационному клипу плитки был присвоен идентификатор компоновки tilePattern. Следующий фрагмент генерирует мозаичный прямоугольный узор из одинаковых плиток:

Глава 3. Рисование и маски function tilerdinkagelD:String. target:MovieClip. clipName:String, depth:Number, x:Number, у:Number, row:Number, column:Number):MovieClip { var pattern:Movie'Clip = target.createEmptyMovieClip(clipName, depth): var depthCount:Number = 0: for (var j:Number = 0: j < column: j++) { for (var i:Number = 0: j < row. i++) { var tile:MovieClip = pattern.attachMoviedinkagelD. " t i l e " + i + "_" + j. depthCount);

t i l e. _ x = x + (tile._width * i ) : t i l e. _ y = у + (tile._height * j ) : depthCount++: return pattern;

} var patternClip:MovieClip = t i l e r C ' t i l e P a t t e r n ". t h i s. "patternClip". 1. 50. 50. 15. 5);

Программа создает клип patternClip и заполняет его узором, состоящим из 15 х 5 плиток;

левая верхняя плитка находится в позиции (50, 50), как показано на рис. 3.15. -w"ф- -ф- -ф"ф- -фА "ф* -^»w -Aw, „ilPSu.

^»ЙЧ^ -JA JL ^AI» -ЖЬ.

-»^ ^V.

^Aw J(L.

^ Рис. 3.15. Большая мозаика, построенная из одинаковых плиток Функция tiler() получает восемь аргументов: • • • • • • linkagelD - идентификатор компоновки символа, определяющего базовую мозаичную плитку;

target - временная диаграмма, на которой строится узор;

clipName - анимационный клип, который будет содержать построенный узор;

depth - глубина создания clipName;

х, у - позиция первого (левого верхнего) угла;

row, column - количество плиток по вертикали и горизонтали.

Функция tiler() позволяет легко создавать векторные узоры в SWF-файле, когда размер сцены меньше окна браузера и вы хотите заполнить неиспользуемую область бордюра (см. трюк 92). Узорная заливка (например, косые диагональные линии) также может обозначать какое-то свойство контента (например, «эта область не может быть выделена» или «эта часть пользовательского интерфейса в данный момент недоступна»).

Узорные заливки Заполнение областей непрямоугольной формы Задача заполнения узором прямоугольных областей решается тривиально, с областями другой формы дело обстоит сложнее. Например, в несложном графическом редакторе, написанном на Flash, было бы желательно разрешить пользователю создавать фигуры с узорной заливкой. Однако на стадии выполнения Flash позволяет создавать векторные фигуры с однородными и градиентными заливками, но не с узорными. Данное ограничение можно обойти при помощи масок. В следующей программе наша функция заполнения прямоугольных областей используется для заполнения круга. Для этого динамически построенный круг назначается маской для мозаичного узора. Давайте разберемся, как работает эта программа. Сначала она создает пустой клип с именем myCircle. Внутри myCircle создаются еще два анимационных клипа. Первый клип (mask) содержит круг, второй (pattern) - содержит область с мозаичным узором, размеры которой по вертикали и горизонтали заведомо достаточны для заполнения круга. На рис. 3.16 изображен прямоугольник с узорной заливкой, на который наложена маска в виде круга.

> 4 > *. 44, •««•. 44 4 * Рис. 3.16. Прямоугольный узор и круглая маска На рис. 3.17 маска скрывает все части прямоугольника за пределами круга;

в результате мы получаем круг с узорной заливкой.

+ 4- 4- + - • $ • •••••>. 4- 4 4 * 4 4 4 % • •*•••••\ • •&••&• "^••4*-^'41"'4'"Ф> ••••••••• + +++++++ + ( ••••••• • + 4- -f ^ > * ••-••••••• - 4- ^ • Л + + * 4 + + + 4 + + 4^ > " - > Ч- 4• Ч- 4- 4- 4- 4 • • • • ' • • • •' • > •••* 4• + - • + * •••••< + +.•.+• + •• +. 4- + •• * • Рис. 3.17. Результат применения маски: все внешние части скрываются, ыостается лишь круг с узорной заливкой Глава 3. Рисование и маски Следующая программа показывает, как использовать функцию tiler() для создания круга с узорной заливкой. В целом она устроена довольно прямолинейно: • Функция pattemCircleO создает дополнительный клип с именем dummy, содержащий только одну плитку. Это делается для определения размеров плитки. Клип dummy стирается узором, поскольку они используют одинаковую глубину. Круг строится из четырех кривых, нарисованных функцией curveTo(). Хотя полученная фигура далека от математически правильного круга, такая аппроксимация ускоряет работу приложения. Впрочем, Flash вообще никогда не строит математически правильные круги (результат всегда строится прич ближенно для повышения быстродействия), поэтому мы не одиноки в этом решении!

• Итак, перед вами исходный код. Функция tiler() не изменилась по сравнению с предыдущим листингом, поэтому здесь мы ее не приводим. function patternCircle(linkagelD:String. target:MovieClip, clipName:String, depth:Number. x:Number, у:Number, г:Number):MovieClip { var r2:Number = r*0.93;

var mc:MovieClip = target.createEmptyMovieClip(clipName, depth);

mc._x = x;

mc._y = y. // Вычисление размера узора var dummy:MovieClip = t i l e r ( " t i l e P a t t e r n ". me. "dummy". 0. 0. 0. 1. 1);

var size:Number = Math.ceiK (2*r) / dummy.Jieight) + 1: // Рисование узора var pattern:MovieClip = t i l e r ( " t i l e P a t t e r n ". me. "pattern". 0, -r. -r, size, size);

// Построение круга var circle:MovieClip = mc.createEmptyMovieClipC'mask". 1);

circle.lineStyleCundefined. 0x0. 100);

circle.moveTo(-r. 0): circle.beginFil1(0x0. 100);

circle.curveTo(-r2. -r2. 0. - r ) ;

circle.curveTo(r2. -г2. г. 0);

circle.curveTo(r2, r2, 0, r ) ;

circle.curveTo(-r2. г2. - г. 0);

circle.endFilK);

// Круг назначается маской для узора pattern.setMask(circle): // Возвращение созданного клипа return me;

} щуСлrcle = patternCircle("tilePattern". t h i s. "myCircle". 1. 270. 200. 100);

Итоги Flash не поддерживает произвольные векторные узорные заливки (растровые заливки поддерживаются, но только на стадии разработки), однако это ограничение можно обойти.

Имитация мозаик Эшера Негативные области позволяют легко создавать мозаичные плитки для заполнения прямоугольных областей (см. трюк 16);

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

ТРЮК № Имитация мозаик Эшера Создание узоров, отдаленно напоминающих работы М. К. Эшера.

Форма мозаичных плиток не ограничивается простыми геометрическими фигурами. В этом трюке рассматриваются принципы разбиения плоскостей для создания сложных узоров. Даже тот, кто никогда не слышал имени М. К. Эшера, наверняка встречал многие знаменитые работы этого голландского художника. Мы не оформляли лицензию на воспроизведение работ Эшера в книге, однако при желании их нетрудно найти в Веб - например, на сайте www.mcescher.com. Во многих известных работах Эшера плоскость разбивается на плитки одинаковой или разной формы, идеально стыкующиеся друг с другом), - в виде птиц, рыб, рептилий и т. д. Множество примеров подобных мозаик можно найти в Веб. Для этого проведите в Google поиск по ключевым словам «tesselation» или «divided plane» (тесселяция - всего лишь научный термин, обозначающий мозаику с идеальным прилеганием плиток).

Разбиение плоскости Методика определения негативных областей (см. трюк 16) станет хорошей отправной точкой для решения поставленной задачи. В этой методике плитка с позитивными и негативными областями использовалась для имитации геометрических узоров, однако сами плитки были квадратными, а мозаика состояла из простых геометрических фигур. А если вдруг потребуется создать узор в стиле Эшера со сложным взаимным переплетением фигур? Как придать плиткам более интересную форму, не ограничивающуюся простыми квадратами и шестиугольниками, но при этом обеспечить их идеальную стыковку? Фокус заключается в том, чтобы начать с геометрически правильной формы и преобразовать ее во что-то еще более интересное. Давайте снова начнем с квадрата, потому что с ним проще всего работать. Нарисуйте квадрат и преобразуйте его в символ анимационного клипа клавишей F8. Разместите рядом с ним на сцене еще несколько экземпляров символа анимационного клипа, чтобы в итоге получилась сетка 3 x 3, изображенная на рис. 3.18. Дважды щелкните на центральном клипе, чтобы войти в режим редактирования «на месте». Теперь измените, скажем, левую сторону квадрата, выгнув ее при помощи инструмента Selection. Изменения отражаются на всех девяти экземплярах клипа, как показано на рис. 3.19;

теперь вы видите, как будет выглядеть сетка 3x Глава 3. Рисование и маски из новых плиток. Однако из рисунка видно, что некоторые плитки скрывают выгнутую сторону других плиток, находящихся под ними, поэтому узор получается не идеальным.

Рис. 3.18. Построение сложного мозаичного узора начинается с простой квадратной сетки Рис. 3.19. Выгнутая сторона придает плитке более интересную форму Итак, правую сторону клипа тоже нужно выгнуть, чтобы она идеально стыковалась с левой стороной соседней плитки. Но как обеспечить идеальное прилегание? Просто скопируйте форму контура с левой стороны клипа и используйте ее для замены правой стороны бывшего квадрата. Конечно, для обеспечения правильной кривизны можно воспользоваться инструментом Zoom. Изменение формы снова немедленно отражается на других экземплярах. На рис. 3.20 изображена мозаика с идеально прилегающими плитками, которые выглядят гораздо интереснее исходных квадратных плиток. Без особых усилий мы создали новую форму плитки, обеспечивающую мозаичное заполнение плоскости.

Имитация мозаик Эшера л III tlllill кliSili { \ I I,.S V ;

:| : :i|^. r: i!

Рис. 3.20. Повторение изгибов с двух сторон обеспечивает идеальное разбиение плоскости ПРИМЕЧАНИЕ Секрет идеального прилегания плиток — всегда добавлять с одной стороны плитки то, что было убрано с другой стороны.

Конечно, результат получился весьма далеким от работ Эшера... А если присмотреться? Вы можете создать плитку произвольной формы, «вырезая» фрагменты с одного края и «вставляя» их с другого края. Далее посмотрите на контур полученной фигуры и подумайте, на что она похожа. Нарисуйте глаз и перья возможно, у вас получится птица;

с чешуей и плавниками плитка может стать похожей на рыбу и т. д. Повторяющаяся плитка не обязана занимать всю площадь. До настоящего момента мы изменяли контуры фигуры, но вы также можете увеличить интервалы между плитками - для этого достаточно просто масштабировать редактируемый клип. Сетка 3 x 3 автоматически изменяется;

плитки разделяются дополнительными интервалами, как показано на рис. 3.21. Теперь можно создать более сложный узор, потому что «сцепление» обязательно лишь в месте соприкосновения плиток. Фактически, мы предполагаем, что интервалы становятся частью плитки, и рассматриваем их как «позитивную область». Достаточно осознать, что плитки могут и не закрывать всю область, а просто должны стыковаться друг с другом, и вы сможете создать новый набор фигур наподобие изображенных на рис. 3.22. Фигура в левом верхнем углу рис. 3.22 вообще не похожа на мозаичную плитку, но она является таковой - в этом нетрудно убедиться, взглянув на узор в нижней части рисунка. На этот раз определение негативных областей производится одновременно с изменением внешнего контура. Заодно плитку можно повернуть: все выполненные операции симметрично отражаются на сетке до тех пор, пока вы остаетесь в режиме редактирования «на месте». В сущности, мы используем свойство симметричности матрицы 3 x 3 относительно линий, проходящих через центр матрицы.

Глава 3. Рисование и маски Рис. 3. 2 1. При масштабировании среднего клипа в режиме редактирования «на месте» остальные экземпляры также автоматически уменьшаются в размерах v / f f f Рис. 3.22. Узор из вертушек Конечно, узор из вертушек - не ахти какое достижение, но это лишь начало. Создав матрицу 3 х 3 из вертушек, я преобразовал их в нечто больше похожее на мозаики Эшера (рис. 3.23). При этом мне пришлось следить за тем, чтобы изгибы крыльев и шей хорошо прилегали друг к другу даже при сдвиге. Для этого я сначала создал большие позитивные области (в частности, квадраты были уменьшены для увеличения интервалов между ними), после чего заполнил их крыльями, хвостами и шеями. Небольшой поворот всей матрицы 3 x 3 имитирует легкий наклон летящей стаи (обратите внимание: если вместо всей матрицы мы повернем только центральный квадрат в режиме редактирования «на месте», то каждая плитка останется на своем месте, но повернется вокруг своей оси). А если подрисовать уши и нос, негативные области на рис. 3.23 начинают походить на спящих кроликов! Новые, более сложные плитки принципиально отличаются от обычных прямоугольных плиток только одним: величина смещения плитки не может быть получена простым обращением к свойствам _height и _width клипа. Вместо этого смещения приходится вычислять вручную на основании позиций плиток при их размещении. Тот же код, который использовался ранее (см. трюк 17), подойдет и для нашей мозаики с летящими птицами.

Исправление неточности свойства _alpha Рис;

3.23. Стая летящих птиц... Почти Эшер Итоги Имитация стиля известных художников - полезное занятие;

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

Исправление неточности №19 свойства „alpha ТРЮК Свойство _alpha анимационных клипов может возвращать неточные результаты. Проблема решается созданием пользовательского внутреннего аналога этого свойства, повышающего точность и плавность переходов. Свойство MovieClip._alpha используется для задания и получения уровня прозрачности клипа. Однако Flash выполняет внутреннее округление значения этого свойства, поэтому его значение, полученное в результате выборки, может отличаться от последнего заданного значения. В этом трюке мы создаем пользовательское свойство для решения проблем, связанных с ошибками округления встроенного свойства Flash _alpha. Свойство MovieClip._alpha хранится во внутреннем представлении в виде целого числа от 0 до 255. Хотя ActionScript заявляет о работе со значениями альфаканала по шкале от 0 до 100 %, на самом деле это неправда. Следующий фрагмент демонстрирует потенциальное накопление ошибок округления: var clip:MovieClip = this.createEmptyMovieClipC "clip". this.getNextHighestDepthO): clip._alpha = 0:

Г л а в а 3. Рисование и маски for(var i = 1: i <= 100;

{ clip._alpha++;

trace(i+"% = " + clip._alpha + "% alpha");

} Код создает пустой клип и изменяет его свойство _alpha от 1 до 100 с приращением 1... во всяком случае, так предполагалось. В действительности Flash преобразует каждое из значений из интервала от 0 до 100 % к ближайшему значению на шкале от 0 до 255. Несколько последних значений, сгенерированных этим фрагментом, выглядят так: 9 % = 74.21875*. alpha 5 9 % = 75 alpha 6 972 = 75.78125% alpha 9 % = 76.5625% alpha 8 9 % = 77.34375% alpha 9 100% = 78.125% alpha К тому моменту, когда по нашим расчетам, альфа-канал должен достигнуть 100 %, Flash накапливает такую ошибку округления, что отображаемое значение составляет 78,125% - погрешность превышает 20%! Как это могло произойти? Дело в том, что каждый раз, когда мы задаем свойство _alpha, значение округляется до ближайшей величины из интервала от 0 до 255. Но при последующей выборке значение _alpha округляется с понижением, в результате при каждом приращении значение увеличивается менее чем на 1%. Чтобы предотвратить ошибку округления, следует либо использовать альфа-уровни, не подвергаемые округлению, либо написать программу так, чтобы задаваемое значение альфа-канала сохранялось в пользовательском свойстве.

Пять значений, не создающих ошибки округления Ошибка округления значения свойства _alpha возникает из-за того, что значения из интервала от 0 до 100 % преобразуются в интервал целых чисел от 0 до 255. Хотя большинство процентов не имеет целого представления в диапазоне 0-255, у пяти значений такие аналоги существуют: 0, 25, 50, 75 и 100 %. Допустим, вы хотите задать альфа-уровень клипа «почти прозрачным». Тогда лучше всего остановиться на значении 25 %, потому что в этом случае клип будет прозрачным ровно на 25 %. Это объясняется тем, что значение 25 % по шкале от 0 до 100 % имеет целое представление в интервале 0-255, а именно 64. В этом нетрудно убедиться: var clip:MovieClip = this.createEmptyMovieC1ip("clip", this.getNextHighestDepthO);

clip._alpha = 25: trace(clip._alpha);

// Выводит 25 clip._alpha = 20;

trace(clip._alpha);

// Выводит 19.921875 Если задать свойству _alpha значение 25% и прочитать его, мы получаем ту же, неизмененную величину 25%. При других значениях (например, 20%) возвращаемое значение близко к заданному, но не совпадает с ним.

Исправление неточности свойства _alpha Создание зеркального свойства Конечно, пять альфа-уровней, округляемых без ошибки, - штука хорошая, если вы собираетесь задать их и оставить без изменения, но при создании анимированных переходов от одного альфа-уровня к другому этот вариант не подойдет. В таких случаях приходится писать код, не зависящий от точности округления _alpha. Например, следующая программа создает переход альфа-уровня от 0 до 100 % и заставляет Flash использовать правильные значения: function fader(me. startAlpha, endAlpha) { mc.fadel = startAlpha;

mc.fade2 = endAlpha;

mc.onEnterFrame = fade;

} function fadeО { this._alpha - this.fadel++;

if (this.fadel >= this.fade2) { this._alpha = this.fade2;

delete this.fadel: delete this.fade2: delete this.onEnterFrame;

var clip:MovieClip = this.createEmptyMovieClipC'clip". this.getNextHighestDepth()): var size:Number = 100: clip._x = 275;

clip._y = 200: clip.lineStyleCO. 0x0. 100): clip.beginFillCOxOOOOFF. 100);

clip.moveTo(-size/2. -size/2);

clip.lineTo(size/2, -size/2);

clip.lineTo(size/2. size/2): clip.lineTo(-size/2. size/2);

clip.endFilK): clip._alpha = 0;

faderCclip. 0. 100);

Поскольку значение, полученное при обращении к свойству _alpha, может быть неточным (округленным по отношению к ранее заданному), мы считаем, что полагаться на него не следует. Вместо того чтобы увеличивать _alpha напрямую, программа получает значение отдельного свойства fadei и задает его свойству _alpha. Этот прием предотвращает постепенное накопление ошибок. Вместо того чтобы проверять значение _alpha и смотреть, достигло ли оно 100 %, мы проверяем свойство fadei, так как оно не содержит погрешности. При достижении пороговой величины (в нашем случае 100 %) свойству _alpha явно задается нужное значение. ПРИМЕЧАНИЕ Альфа-прозрачность замедляет воспроизведение анимации в Flash из-за необходимости обработки пикселов как изображения, так и фона. Если в результате ошибки округления альфа-уровень будет представлять собой дробную величину менее 100 %, это замедлит работу Flash. После перехода мы задаем alpha значение 100, чтобы предотвратить это падение быстродействия.

Глава 3. Рисование и маски Предотвращение ошибок округления с использованием классов/прототипов При создании анимации с интенсивным использованием альфа-эффектов дополнительный код, решающий проблемы с ошибками округления _alpha, усложняет чтение и сопровождение программы. В таких случаях подумайте о решении проблемы при помощи пользовательского класса. В следующем примере мы создаем новый класс с именем AlphaClip, который должен храниться во внешнем файле AlphaClip.as. Класс определяет функции чтения и записи своего внутреннего свойства alphalnternal, не подверженного ошибкам округления MovieClip._alpha. Обратите внимание: AlphaClip не объявляется субклассом MovieClip, а лишь хранит ссылку на экземпляр этого класса в одном из свойств. // Этот код ActionScript 2.0 должен храниться // во внешнем файле AlphaClip.as class AlphaClip { private var alphalnternal:Number;

private var target:MovieClip;

public function AlphaClip(mc:MovieClip) { target = me: alphalnternal = mc._alpha;

} public function get _alpha():Number { return alphalnternal;

} public function set_alpha(alphaIn:Number):Void { target._alpha = alphaln;

alphalnternal = alphaln:

Допустим, на сцене существует анимационный клип с именем myClip;

прямые операции чтения и записи свойства MovieClip._alpha могут привести к расхождениям между заданным значением свойства и тем, которое будет получено при последующем чтении. Но если заменить его свойством _alpha нашего пользовательского класса AlphaClip, возвращаемое значение будет совпадать с заданным, поскольку во внутренних операциях класса используется более точное свойство AlphaClip.alphalnternal;

тем самым предотвращается накопление ошибок округления. Мы определяем методы доступа, чтобы разработчик мог работать со знакомым свойством _alpha, не обращаясь напрямую к внутреннему свойству alphalnternal. var myAlpha:AlphaClip = new AlphaClip(myClip);

// Прямое изменение свойства _alpha (старый способ) myClip._alpha = 20: trace(myClip._alpha);

// Выводит: 19.921875 // Косвенное изменение myClip._alpha через myAlpha._alpha myAlpha._alpha = 20: trace(myAlpha._alpha);

// Выводит: Исправление неточности свойства _alpha Поставленная задача решена, но на практике пользоваться таким решением неудобно, потому что разработчик должен помнить о необходимости создавать экземпляр AlphaClip помимо основного клипа каждый раз, когда он хочет избежать потенциальных ошибок округления MovieClip._alpha. Более формальный объектно-ориентированный подход ActionScript 2.0 заключается в создании субкласса, расширяющего встроенный класс MovieClip (то есть наследующего от него). Вариант с наследованием обладает существенным преимуществом: разработчику не нужно создавать отдельные экземпляры AlphaClip и MovieClip для одного клипа. Впрочем, он все равно должен помнить о том, что для решения проблемы с округлением альфа-уровня вместо экземпляра MovieClip нужно создать экземпляр AlphaClip. В предыдущем примере вместо формального наследования использовался упрощенный механизм композиции. Другими словами, вместо того чтобы расширить класс MovieClip с ключевым словом extends, мы внедрили в класс AlphaClip отдельный экземпляр MovieClip (свойство target). И все же для удобства разработчика было бы предпочтительнее заменить Movie Clip._alpha напрямую, без создания экземпляров отдельного субкласса. Исходя из этих соображений, мы воспользуемся стилем ActionScript 1.0 и изменим класс MovieClip посредством присоединение свойств и методов к его прототипу. Этот код также работает в ActionScript 2.0:.// Определение методов доступа getAlpha = function () { return this.alphalnternal;

}: setAlpha = function (alphaln) { this._alpha = alphaln: this.alphalnternal = alphaln: }: initAlpha = function () { return 100;

}: // Добавление нового свойства MovieClip.alpha (без подчеркивания!) MovieClip.prototype.addProperty("alpha". getAlpha. setAlpha);

MovieClip. prototype, alpha Internal = initAlphaO;

На этот раз метод addProperty() создает новое свойство MovieClip с именем alpha (без подчеркивания), для чтения и записи которого используются методы доступа. Свойство работает точно так же, как и MovieClip._alpha, но оно избавлено от проблем с округлением за счет использования промежуточной переменной alphalnternal. Следующий фрагмент создает в обработчике onEnterFrame эффект растворения, который останавливается при уменьшении свойства alpha до нуля: myClip.onEnterFrame = functionO { this.alpha--: i f (this.alpha == 0) { delete this.onEnterFrame;

traceC'done") Глава 3. Рисование и маски Если бы вместо пользовательского свойства alpha (без подчеркивания) использовалось встроенное свойство _alpha (с подчеркиванием), обработчик onEnterFrame никогда бы не завершил свою работу, потому что значение _alpha никогда не уменьшилось бы до нуля: myClip.onEnterFrame = functionO { this._alpha--;

i f (this._alpha == 0) { delete this.onEnterFrame;

traceC'done") Итоги Анимированные альфа-эффекты интенсивно используют вычислительные мощности процессора, поэтому потенциальная неточность свойства _alpha способна сильно замедлить работу приложения. Клип с альфа-уровнем 99,6078 % внешне не отличается от клипа с альфа-уровнем 100 % (то есть полностью непрозрачного), но воспроизводится гораздо медленнее! Чтобы написать эффективный код анимации альфа-эффекта, необходимо знать о потенциальных ошибках округления свойства _alpha и уметь справляться с ними. Вероятно, борцы за чистоту ООП брезгливо поморщатся при виде решения в стиле ActionScript 1.0, основанного на применении прототипов, однако этот синтаксис поддерживается в ActionScript 2.O. Помните, что субклассы ActionScript 2.0 компилируются в тот же байт-код, что и решение с применением прототипов. Используйте тот вариант, который вам кажется более удобным (наследование на базе прототипов, композиция или формальное наследование).

Использование сложных фигур в качестве масок Flash не поддерживает маски с внутренними отверстиями (например, в форме бублика). Как обойти это ограничение? Маски - один из аспектов Flash, которые на первый взгляд находят мало практических применений, однако опытные Flash-разработчики хорошо знают, что маски задействованы едва ли не в каждом нетривиальном графическом эффекте Flash. Как объяснялось в начале главы 1, маски могут назначаться как на стадии разработки, так и на стадии выполнения. Flash MX стал первой версией с поддержкой сценарных масок, то есть масок, динамически назначаемых на стадии выполнения методом MovieClip.setMask(). Естественно, разработчик должен учитывать влияние сценарных масок на быстродействие приложения. В бета-версии Flash MX фирма Macromedia разрешила использовать в качестве масок фигуры произвольной сложности, но позднее отменила эту возможность по соображениям быстродействия. Одним из самых яростных противников зап Использование сложных фигур в качестве масок рета на использование сложных масок стал Эрик Нацке (http://www.natzke.com). Эрик является автором множества потрясающих графических эффектов, от которых голова идет кругом (см. трюк 25). Неудивительно, что в своей работе он часто использует маски. В этом трюке показано, как реализовать функциональность сложных масок без ухудшения быстродействия. В материале главы использованы некоторые темы, обсуждавшиеся специалистами Macromedia и бета-тестерами во время бета-тестирования.

Сложные маски Маски Flash должны быть сплошными. Если в качестве маски используется сложная фигура (например, бублик), Flash упрощает ее. В этом нетрудно убедиться на следующем простом примере. В новом документе присвойте первому слою имя background, затем добавьте над ним два уровня с именами maskLayer и actions (рис. 3.24).

Рис. 3.24. Подготовка слоев для демонстрационного примера На слое background создайте заполненный прямоугольник, накрывающий всю сцену. Назначьте ему линейную градиентную заливку, как показано на рис. 3.25. Нажмите клавишу F8, чтобы преобразовать прямоугольник в символ анимационного клипа, и присвойте ему имя back в диалоговом окне Symbol Properties. Введите имя экземпляра backClip на панели свойств. Заблокируйте слой.

• iiiiiiiie • • 1Ё1Й! |Я1Щ|1§1 • Рис. 3.25. Градиент Глава 3. Рисование и маски На слое maskLayer создайте фигуру в виде бублика (рис. 3.26). Нажмите клавишу F8, чтобы преобразовать ее в символ анимационного клипа. Присвойте символу имя mask, задайте клипу имя экземпляра maskClip.

• I L '•:•• • •, •••,:•:. '.;

:i :

i Л |- / г • • i Рис. 3.26. Фигура в виде бублика над прямоугольником с градиентной заливкой Наконец, свяжите следующий сценарий с кадром 1 слоя actions: function dragDrop(mc:MovieClip){ mc.onPress = functionO { this.startDrag(true): this.onMouseMove = functionO { updateAfterEventO: } mc.onMouseUp = functionO delete this.onMouseMove;

this.stopDragO;

} D ( sC) d g rp akp r om i: a l bcC.e ak akp akp t s( sC) isM m i ;

l l Небольшое отступление: плавное перетаскивание клипа является задачей настолько распространенной, что для ее решения стоит создать специальный класс. Далее приведен пример класса для выполнения плавного перетаскивания: // Этот код ActionScript 2.0 должен храниться // во внешнем файле SmoothDrag.as class SmoothDrag { public function SmoothDrag(targetClip:MovieClip) { dragDrop(targetClip);

} private function dragDrop(mc:MovieClip):Void { Использование сложных фигур в качестве масок mc.onPress = functionO { mc.onMouseMove = functionO { updateAfterEventO;

} mc.onMouseUp = functionO delete mc.inMouseMove;

mc.stopDragO;

При наличии такого класса программа сокращается до пары строк: var myClipDraggerSmoothDrag = new SmoothDrag(maskClip);

backClip.setMask(maskClip): Однако мы продолжим разработку с исходной версией, не использующей класс. Функция dragDrop() позволяет перетаскивать maskClip мышью;

при отпускании кнопки мыши клип «сбрасывается». Казалось бы, после назначения maskClip маской для backClip мы должны видеть только ту часть backClip, которая находится под maskClip. Но на самом деле будут видны все области backClip, находящиеся внутри периметра maskClip. Flash превращает сложную маску-«бублик» в простой круг (рис. 3.27).

Рис. 3.27. Бублик превращается в круг Для масок Flash устанавливается одно важное ограничение: они должны иметь непрерывный периметр. Таким образом, если прорезать в «бублике» небольшую щель, его контур будет непрерывным. Проблема в том, что после вырезания щели О-образный бублик превратится в букву С. Щель должна быть настолько маленькой, чтобы Flash игнорировал ее при выводе, но достаточно большой для того, чтобы вся фигура рассматривалась как единая форма с непрерывным периметром. Для этой цели будет использоваться прорезь в виде волосяной линии. Выделите экземпляр maskClip и дважды щелкните на нем, чтобы перейти в режим редактирования «на месте». Активизируйте инструмент Line и нарисуйте волосяную линию по радиусу «бублика», как показано на рис. 3.28.

Глава 3. Рисование и маски У Urn tool •Ы-ihairtne Рис. 3.28. Добавление волосяного разреза к маске в виде бублика В ы д е л и т е н а р и с о в а н н у ю л и н и ю и в ы п о л н и т е к о м а н д у Modify • S h a p e • Convert Lines to Fills, чтобы преобразовать линию в фигуру. Обратите внимание: толщина созданной линии меньше 1 пиксела (на моем компьютере она составляет 0,3 пиксела). Теперь удалите фигуру. После удаления на «бублике» остается щель толщиной 0,3 пиксела. Если увеличить масштаб и сравнить изображение с сеткой пикселов, как показано на рис. 3.29, становится видно, что толщина линии меньше 1 пиксела.

Рис. 3.29. Щель толщиной менее 1 пиксела Хотя Flash считает, что мы создали букву С с единым непрерывным периметром, механизм визуализации рисует фигуру в виде буквы О без каких-либо прорезей. Убедитесь в этом, выполнив команду Control • Test Movie. Возможен один из двух результатов. Если повезет, вы увидите маску в форме бублика без всяких пробелов, как на рис. 3.31. Если не повезет, на маске будет видна волосяная линия, как в левой части рис. 3.30 (справа та же линия изображена при большом увеличении).

Рис. 3.30. Маска с тонким разрывом (слева) и она же при большом увеличении (справа) При возникновении проблемы, изображенной на рис. 3.30, воспользуйтесь инструментом Subselection и уменьшите разрыв. Иногда проблему удается решить при помощи режима Snap to Pixel: переместите край прорези, удаленный от ли Интерференционные картины и волновые эффекты нии привязки, и совместите его с линией. В итоге должна получиться идеальная маска, изображенная на рис. 3.31. Возможно, при увеличении разрез станет виден, но при перерисовке с нормальным увеличением он остается незаметным.

Рис.

3. 3 1. Идеальная маска в форме бублика В трюке 21 будут продемонстрированы некоторые интересные эффекты, созданные с помощью бесполезных на первый взгляд масок.

Интерференционные картины №21 и волновые эффекты ТРЮК Создание интерференционных картин и волновых эффектов с применением сложных масок. Маски - одна из тех загадочных возможностей, которые на первый взгляд кажутся бесполезными. Все меняется, когда вы начинаете учиться и видите, что можно сделать с их помощью. В этом трюке мы рассмотрим ряд неожиданных применений сценарных масок в форме бублика (см. трюк 20), и это лишь вершина айсберга. Из серии кругов можно создать впечгггляющие интерференционные узоры, как на рис. 3.32. Эффект симметричен в том отношении, что любая из двух фигур может использоваться в качестве маски.

Рис.

3.32. Интерференционная картина, нарисованная при помощи масок Глава 3. Рисование и маски Эффект на рис. 3.32 был создан при помощи двух наборов концентрических колец с прорезями (см. трюк 20), гарантирующими, что в масках, несмотря на их внешний вид, отсутствуют замкнутые области. При размещении фигур друг над другом получается однородный круг, но если сместить одну фигуру и назначить ее маской для другой, получится узор, изображенный на рис. 3.32. А если уменьшить толщину кругов и сблизить их, картина приобретает по-настоящему психоделический вид (рис. 3.33).

Рис. 3.33. Интерференционная картина с масками, состоящими из концентрических колец Применяя эти узоры для маскирования графики или другого контента, можно создать интересные эффекты переходов. Вдобавок тот, кто не знает об этом трюке, не сможет повторить его - несомненное преимущество для законодателей моды Flash. При помощи сложных масок можно разделить изображение на несколько раздельно маскируемых частей, а также применить сценарные волновые эффекты. Визуальный эффект на рис. 3.34 создан посредством применения маски к двум листам, один из которых был повернут.

Рис. 3.34. Волновой эффект, созданный применением маски к изображениям листьев Итоги Многие разработчики недооценивают возможности масок. Хотя в документации Flash сказано, что маски предназначены для скрытия контента, в действительно Сглаживание краев на растровых изображениях сти эта формулировка не дает должного представления о том, что можно сделать при помощи масок. Область применения масок чрезвычайно широка - от экзотических переходных эффектов до сугубо утилитарных пользовательских компонентов. Когда сценарные маски впервые появились в Flash, они стали настоящим даром небес для квалифицированных разработчиков. Позднее народ осознал ограниченность простых масок. Но после знакомства с представленным трюком препятствий на вашем пути станет гораздо меньше! Еще один пример нетривиального использования масок встречается при описании трюка с листанием страниц (см. трюк 25).

Сглаживание краев на растровых №22 изображениях ТРЮК Векторная графика при любом разрешении отличается хорошим сглаживанием краев, но" в некоторых ситуациях приходится использовать растровые изображения. От очевидных дефектов краев растровых изображений можно избавиться при помощи альфа-канала или векторных краев. Иногда растровая графика оказывается предпочтительнее векторной, особенно при работе с текстурными объектами (в отличие от областей с однородной или почти однородной заливкой, которые лучше представляются в векторной графике). Именно эта причина обусловила применение растровых изображений в некоторых современных анимациях (хорошие примеры - http://www.centrifuga.net/ desiderata.html и http://www.centrifuga.net/gab.html). Серьезным недостатком растровых изображений является пикселизация. В настоящем трюке представлены два способа борьбы с неровностями краев на растровых изображениях. Как правило, пикселизация сильнее всего проявляется при резких различиях между цветами изображения и его фона (на границах с менее резкими переходами цветов пикселизация обычно не столь заметна). На техническом жаргоне эти зубчатые линии обычно называются «пилой». Впрочем, такая ступенчатость характерна для любых цифровых данных (в том числе и звуковых - см. трюк 58) при недостаточной частоте выборки, которая и является причиной появления артефактов. Методика решения проблемы носит название сглаживания (antialiasing). Одно из решений основано на применении альфа-прозрачности: размывка областей, прилегающих к краям растрового изображения, улучшает слияние с фоном и одновременно предотвращает эффект ореола (когда изображение окружается по периметру светлыми пикселами).

Размывка краев растровых изображений Предположим, мы хотим импортировать в Flash растровое изображение с рис. 3.35. На расстоянии смотрится очень мило, но при увеличении пограничных областей крыльев становятся отчетливо видны ужасные зазубрины (рис. 3.36).

Глава 3. Рисование и маски Рис. 3.35. Растровое изображение бабочки Рис. 3.36. Неровности контура, заметные при увеличении Один из способов избавиться от неровностей основан на размывке краев в Photoshop перед импортированием графики в Flash. Загрузите изображение в Photoshop. Если потребуется, «сплющите» изображение, чтобы все пикселы находились на одном слое Photoshop с именем Background. Photoshop не обладает простыми средствами задания альфа-уровня для пикселов этого слоя. Щелкните правой кнопкой мыши (Windows) или щелкните с нажатой клавишей « I (Mac) на этом слое на панели Layers, выберите в контекстном меню команду H Duplicate Layer. Подтвердите имя, предложенное по умолчанию (Background copy). Выделите фоновую область вокруг изображения при помощи инструмента Photoshop Magic Wand. Чтобы предотвратить появление ореола, выполните команду Select • Modify • Expand и расширьте выделение на 1 пиксел. Удалите фон нажатием клавиши Delete;

результат показан на рис. 3.37.

Сглаживание краев на растровых изображениях Рис. 3.37. Изображение бабочки без фона В процессе удаления фона были утрачены усики бабочки, но в Flash мы заменим их двумя векторными кривыми. Далее производится размывка - постепенное изменение, цвета, скрывающее пикселизацию краев. Не снимайте выделения с контура, а если оно было случайно снято - восстановите его, щелкнув инструментом Magic Wand в любой точке изображения, не содержащей пикселов (то есть помеченной клеточным узором). Выполните команду Select • Feather и задайте параметру Feather значение 2 пиксела. Используя инструмент Eraser (100% непрозрачный), сотрите контур вдоль периметра выделенной области. На рис. 3.38 показано, как выглядит край растрового изображения до и после выполнения операции.

Рис. 3.38. Изображение до (слева) и после (справа) размывки края в Photoshop Глава 3. Рисование и маски Если потребуется, повторите размывку с интервалом в 1 пиксел. Наконец, сохраните изображение в формате PNG командой File • Save As. Photoshop предложит выбрать сохранение файла в чересстрочном (Interlaced) или обычном (Non-interlaced) формате. Выберите второй вариант. Импортируйте файл в Flash командой File • Import • Import to Library;

перетащите ресурс из библиотеки на сцену, чтобы использовать его на временной диаграмме. На рис. 3.39. видно, что после размывки крыло бабочки выглядит гораздо менее пикселизованным, чем на исходном растре.

Рис.

3.39. Край размытого (слева) и неразмытого (справа) растра при увеличении Размывка края производится изменением альфа-уровня, а не цветовым переходом;

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

Добавление векторного контура №23 к растровому изображению ТРЮК Если графика должна содержать и текстуры, и четко очерченный край, возникает противоречие. Векторные фигуры обеспечивают четкие очертания, но не позволяют использовать сложные текстуры. Растры обеспечивают сложные текстуры, но не имеют четких краев. Возьмите все лучшее от векторной и растровой графики и создайте растр с векторным контуром. В некоторых видах графики (например, в логотипах или других графических конструкциях, содержащих текст или «острые» края, отличающиеся от плавно закругленного крыла бабочки) методика размывки (см. трюк 22) приводит к потере четкости краев оригинала. К счастью, можно пойти по обратному пути и создать векторные очертания для растровых объектов. Давайте попробуем спрятать неровности под векторным контуром или удалить их при помощи векторной маски. Описанные далее операции с растровым изображением выполняются в Photoshop, но аналогичного результата можно добиться и в других графических редакторах, включая Fireworks.

Добавление векторного контура к растровому изображению Выделите изображение, созданное в предыдущем трюке (см. трюк 22), и экспортируйте его в формат P N G командой File • Save As. Также необходимо экспортировать второе изображение, в котором все ненулевые пикселы окрашены в черный цвет (рис. 3.40). В Photoshop это делается командой Image • Adjustment • Brightness/Contrast. Переведите оба ползунка в диалоговом окне Brightness/Contrast в крайнее левое положение, чтобы все пикселы окрасились в черный цвет.

Рис. 3.40. Черный силуэт (слева) исходного изображения бабочки (справа) И м п о р т и р у й т е о б а и з о б р а ж е н и я в F l a s h к о м а н д о й File • Import • Import t o S t a g e. Выделите черный силуэт и преобразуйте его в векторную форму командой Modify • Bitmap • Trace Bitmap - получится черная векторная фигура. Если определить для нее контур, он будет точно соответствовать внешним очертаниям растрового изображения. Мы можем либо создать контурную линию, как на рис. 3.41, либо использовать фигуру как стандартную векторную маску. Второй способ более очевиден, но зато первый более эффективен с точки зрения производительности, потому что Flash не приходится многократно применять маску (а это может быть весьма существенно, если растр позднее потребуется анимировать) Рис. 3. 4 1. Контур растрового изображения Преобразование PNG в векторную форму не приводит к созданию черной фигуры, заключенной в белую фигуру (как при импортировании растрового изображения с фигурой на белом фоне). Поскольку в файле PNG фон отсутствует, Глава 3. Рисование и маски a Flash правильно преобразует пикселы с нулевым альфа-уровнем в «отсутствие векторной графики», векторное преобразование выполняется быстрее и требует меньше служебных операций впоследствии. Итак, мы выбираем первый способ как существенно более творческий и неочевидный. Разместите контур на слое, находящемся над растром, и совместите его с изображением, как показано на рис. 3.42. При необходимости воспользуйтесь инструментом Subselection, чтобы контур лучше соответствовал форме края. Постарайтесь, чтобы контур слегка перекрывал границу растрового изображения. Возможно, при этом слой, содержащий векторную графику, стоит отображать в режиме Outlines (щелкните на цветном квадрате слева от названия слоя).

Рис. 3.42. Создание контура.

Произведите разбивку растра командой Modify • Break Apart. Это позволит работать с растром, применяя векторные инструменты. Переместите векторный контур с текущего слоя на один слой с растровым изображением. Перемещение проще всего выполняется через буфер обмена: 1) заблокируйте все слои кроме слоя, на котором находится векторный контур;

2) нажмите клавиши Ctrl+A (Windows) или « > А (Мае), чтобы выделить контур. Н+ Скопируйте его в буфер клавишами Ctrl+X (Windows) или §€+Х (Мае);

3) разблокируйте слой, на котором находится растровое изображение. Заблокируйте все остальные слои. Убедитесь в том, что текущее выделение отсутствует, щелкните правой кнопкой мыши (Windows) или щелкните с нажатой клавишей Ж (Мае) и выберите в контекстном меню команду Edit • Paste in Place. Выделите остальные пикселы за пределами контура и нажмите клавишу Delete. Наконец, осторожно удалите контур (рис. 3.43) - вокруг растрового изображения появляется идеально четкая векторная граница!

Исправление ошибки сдвига Рис.

3.43. После удаления контура открывается четкая векторная граница Растровое изображение превратилось в гибридную конструкцию, обладающую преимуществами как векторной графики (четкие края), так и растровой графики (сложные текстуры). Что еще интереснее, остается возможность редактирования векторного края. Да, вы не ошиблись - при необходимости его даже можно анимировать (рис. 3.44).

Рис.

3.44. Анимация векторного контура растрового изображения Итоги Настоящий раздел убедительно показывает, что для интеграции растров в четко очерченный векторный мир Flash можно сделать довольно много. Круг возможностей не ограничивается скрытием неровностей при помощи альфа-масок существует и такой вариант, как создание «псевдовекторной фигуры», состоящей из растра с векторным контуром (впрочем, правильнее было бы назвать ее векторным контуром с растровой заливкой).

Исправление ошибки сдвига В Flash Player версий 6 и ниже растровые изображения искажаются и отображаются в неверной позиции. Необходимо выяснить, как сместить растровое изображение в нужное место и внести поправку на искажение в этих версиях Flash Player. Как правило, сложные инструменты не идеальны. Это относится и к Flash Player вам придется освоить некоторые трюки для исправления его недостатков. Одним Глава 3. Рисование и маски из таких недостатков является ошибка сдвига растровых изображений. Она появилась в ранних версиях Flash Player и была исправлена лишь недавно в Flash Player 7. Даже тем, кто создает анимацию в Flash MX 2004, придется решать эту проблему при экспортировании роликов для Flash Player версии 6 и ниже. А если учесть, что Flash Player 7 еще не получил повсеместного распространения, наверняка разработчикам придется еще некоторое время иметь дело с этой проблемой. Flash прежде всего использует векторный механизм визуализации, а функции отображения растровой графики в Flash Player содержат ряд ошибок, приводящих к искажению растровых изображений. В результате откомпилированная анимация Flash в браузере не всегда выглядит так, как при тестировании в среде разработки: растровые изображения сдвигаются и дергаются даже в том случае, если к ним не применялись эффекты движения. Ошибка сдвига растровой графики описана в техническом документе Macromedia 14256 «Bitmaps shift in Macromedia Flash» (http://www. macromedia. com/support/flash/ts/documents/bitmaps_shift. html). На практике это означает, что при использовании растровой графики в Flash часть изображения сдвигается на 1 пиксел вправо и вниз. Обратите внимание: сдвигается не все изображение, а только некоторые пикселы внутри него. Помимо сдвига происходит искажение графики: одни строки/столбцы пикселов исчезают, другие повторяются. Если изображение используется в анимационном клипе, в зависимости от точки регистрации (начала координат клипа) искажению подвергаются разные части изображения. Сказанное проще пояснить наглядным примером. Допустим, у нас имеется изображение (слева, рис. 3.45). При импортировании в среду разработки Flash (в том числе и Flash MX 2004) разместите его по границам пикселов, без дробных значений координат X и У, и экспортируйте его как клип Flash Player 6. В результате будет получено искаженное изображение, показанное в правой части рис. 3.45.

Рис.

3.45. Исходное изображение (слева) и результат ошибки сдвига (справа) Графические данные сдвигаются вниз и вправо. Хотя левый столбец и верхняя строка пикселов остаются без изменений, вследствие сдвига они дублируются, тогда как нижняя и правая стороны исчезают. В зависимости от местонахождения точки регистрации растра возможны сдвиги в разных частях изображения.

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

Исправление ошибки сдвига • применения к изображению 99 % альфа-уровня. Этот способ избавляет от мерцания в кадрированной анимации, но искажает цвета изображения (пусть и незаметно) и замедляет воспроизведение анимации на фоне изображения. Кроме того, он не решает проблему со сдвигом графического содержания;

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

• включения режима Allow Smoothing в свойствах растрового изображения в библиотеке. Это избавляет от неприятного мерцания при переходе от подвижного изображения к статическому, но не имеет отношения к ошибке сдвига и не решает проблемы;

• разбиения изображения и его перемещения по частям. Фактически, проблема делится на несколько частей и перемещается в другие, менее заметные фрагменты изображения;

• малозаметного масштабирования изображения. Хотя масштабирование предотвращает нежелательные смещения пикселов, оно все равно приводит к модификации исходного изображения. Представленные «решения» могут быть эффективными до некоторой степени, но в действительности они не решают проблему, поэтому рекомендовать их я бы не стал (кроме масштабирования, которое может пригодиться в одном специфическом случае - с м. далее).

Исправление ошибки (правильный способ) При тщательном анализе выясняется, что ошибка происходит только при положительных значениях координат X и Y изображения внутри клипа или на сцене. Таким образом, простейшее решение заключается в перемещении изображения в область клипа, в которой Flash Player для вычисления позиций каждого пиксела придется использовать отрицательные числа. Сделать это несложно: разместив изображение на сцене в нужной позиции, преобразуйте его в символ анимационного клипа нажатием клавиши F8. В диалоговом окне Convert to Symbol (рис. 3.46) введите имя символа и, что не менее важно, выберите в качестве точки регистрации правый нижний квадратик. В результате содержание клипа (то есть растровое изображение) размещается слева наверху от начала координат (в квадранте с отрицательными значениями X и У).

гййЭЗИ Ш^ШШШЩ^ Щ1ШР^^?^- Й&: •• • • ! : :

ок Cancel' Рис, 3.46. Назначение точки регистрации в диалоговом окне Convert to Symbol Глава 3. Рисование и маски Перемещение точки регистрации исправляет ошибку сдвига при работе с растровой графикой в среде разработки Flash - ни искажения, ни мерцания уже не будет. И все же одна проблема остается - фирма Macromedia «исправила» ошибку в Flash Player 7, хотя на самом деле речь идет о простой замене знака! В Flash Player 7 ошибка сдвига возникает при отрицательных, а не при положительных значениях X и Y. Это объяснялось тем, что практически все разработчики размещают графику в области положительных координат клипа.

ПРИМЕЧАНИЕ Получается, что, исправляя ошибку для Flash Player 6, вы создаете ошибку в Flash Player 7. Если результат должен экспортироваться в формат Flash Player 7, не используйте этот трюк.

Исправление ошибки в динамически загружаемых файлах (эффективный способ) Хотя предыдущее решение позволяет справиться с ошибкой сдвига при работе с растровой графикой в среде разработки Flash, оно не подходит при загрузке изображений в Flash методом loadMovieQ. К сожалению, точку регистрации загруженного изображения нельзя сместить так, чтобы изображение находилось в левом верхнем квадранте (с отрицательными координатами X и Y): поскольку загруженное изображение само является контейнером клипа, его содержимое окажется в области положительных координат. Например, следующий фрагмент приводит к ошибочному результату, представленному на рис. 3.45, - графическое содержание сдвигается вниз и вправо: // Создание и загрузка изображения на сценеthi s.createEmptyMovieClip("mylmage". 1);

this.mylmage.1oadMovie("testImage.jpg"): Тем не менее, у проблемы существует простое решение. Масштабируя изображение с ничтожным уменьшением - настолько малым, что оно остается незаметным для зрителя, - можно заставить Flash Player использовать точные значения, вследствие чего итоговое изображение выглядит правильно при вычислении позиции пиксела. Изменение масштаба должно производиться после загрузки изображения. Существует несколько способов незначительного масштабирования изображений. В следующей программе сразу же после загрузки изображения автоматически выдается команда на изменение размеров, обеспечивающая исправление ошибки: // Создание и загрузка изображения в клипе mylmage // и его размещение на сцене. this.createEmptyMovieClip("mylmage". 1): thi s.mylmage.1oadMovie("test Image.jpg"): // Создание клипа-"наблюдателя" для исправления размеров изображения // сразу же после его загрузки.

Эффект листания страниц this.createEmptyMovieClip("myImageLoader". 2);

this.mylmageLoader.onEnterFrame = functionO { i f (this._pa'rent.mylmage._width > 1) { // Изображение было загружено this._pareirt. my Image. _xscale = 99.98: this._pa rent, my Image. _yscale = 99.98;

thi s.removeMovi eCli p():

В общем случае масштабировать изображения не рекомендуется, но в данном конкретном случае этот прием допустим как оптимальный способ предотвращения ошибки сдвига. Как правило, искажение, обусловленное изменением размеров, остается незаметным для зрителя. Например, в предыдущем фрагменте изменение размеров решает проблему сдвига без изменения ширины и высоты изображения;

если после загрузки изображения вывести свойство _width функцией trace(), вы заметите, что изображение сохранило исходную ширину. Зё Фернандо ТРЮК Эффект листания страниц Создание анимации листания страниц и других эффектов Flash с использованием сценарных масок и свойства симметрии.

№ Многие разработчики при виде нетривиальных эффектов Flash думают: «Ого! Интересно, как это сделано?» После просмотра чернового варианта этой главы я не мог отделаться от ощущения, что в ней отсутствует один важный момент: как придумывать новые графические эффекты или воспроизводить фокусы, созданные другими дизайнерами. На первый взгляд, умение воссоздать эффект, просто взглянув на анимацию SWF, выглядит некой разновидностью шаманства, но в действительности задача вполне реальная, если научиться выделять стандартные структурные аспекты (наподобие того, как мы воспроизводим музыкальное произведение на гитаре, прослушав его несколько раз). В этом трюке я покажу, как выполнялось деконструирование модного эффекта листания страниц. Но сначала давайте рассмотрим базовый структурный аспект симметрию. Кстати, в работе нам пригодятся хорошо знакомые маски.

Математический зеркальный коридор В математике выражение в левой части конструкции со знаком «=» приравнивается к выражению в правой части: 3=1+2;

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

Глава 3. Рисование и маски Знак равенства можно рассматривать как формальное представление зеркала. Выражение в левой части равно выражению в правой части. Как только вы осознаете этот факт, в вашем распоряжении появляется мощный инструмент для создания графических эффектов. (Не путайте знак равенства в обычной математике с оператором присваивания = в ActionScript: оператор вычисляет значение выражения в правой части и присваивает его переменной или свойству в левой части. В данном трюке речь идет о симметрии/равенстве в математическом понимании. Если уж на то пошло, в ActionScript для проверки равенства используются операторы == и ===.) Каждый раз, когда вы планируете новый эффект (или пытаетесь воспроизвести самый популярный фокус этого месяца), прежде всего поищите симметрию. Чтобы показать, как это делается, мы рассмотрим эффект, который на первый взгляд кажется очень сложным. Но при всей внешней нетривиальности этот эффект основан на простой симметрии.

Листание страниц Недавно арсенал классических трюков Flash пополнился эффектом листания страниц (один из ранних примеров можно найти по адресу http://welcome.hp.com/country/us.en/msg/corp/flashdreamworks.html). Во время начального обсуждения подборки трюков для включения в книгу мой редактор Брюс Эпстейн сказал: «Было бы здорово, если бы мы могли описать эффект листания страниц. Я видел, как Эрик Нацке кратко описывал процесс на семинаре Flash Forward. Ты знаешь, как это делается?» Я понятия не имел, как реализовать этот трюк, но во время разговора я машинально делал наброски (я вообще всегда что-нибудь рисую, когда говорю по телефону, - это помогает думать).

Рис.

3.47. Реализация эффекта листания страниц На рис. 3.47 представлена схема моего решения эффекта листания страниц. Рисунок дает полное представление об идее, на которой базируется весь эффект;

набросав эту схему, я сразу сказал Брюсу: «Думаю, я довольно быстро разберусь с этим эффектом, ставь его в план».

Эффект листания страниц Что же означает рисунок, и как я пришел к этой мысли?

Прежде всего было ясно, что эффект листания страниц как-то связан с листанием страниц (надо же!), поэтому я нарисовал переворачиваемую страницу. Далее: я знал, что в большинстве сценарных эффектов в том или ином виде используется симметрия, поэтому и продолжал смотреть на диаграмму до тех пор, пока не нашел симметричную структуру. На этот раз она находилась на самом видном месте. Треугольник, изображающий перелистываемую страницу, является зеркальным отображением треугольника, находящегося под ним. Два треугольника симметричны относительно пунктирной линии А (рис. 3.47). В конце анимации с переворачиваемой страницей появляются две страницы, симметричные относительно корешка книги. Фактически, ось симметрии А перемещается в положение В. В сущности, сценарий анимации переворота страницы сводится к перемещению оси симметрии во время анимации! Симметрия встречается в природе, при работе с фракталами (зеркальное воспроизведение одного процесса на всех уровнях) и частицами (построение больших эффектов из множества мелких идентичных эффектов). Природа также часто вносит элементы симметрии в иерархии, поэтому сама симметрия может иметь рекурсивную природу, как при ветвлении дерева (см. трюк 6). Практически все сложные эффекты строятся на базе симметрии. Графические эффекты Flash, не использующие симметрию (обычно с некоторой долей случайности, чтобы изображение не казалось слишком симметричным), можно пересчитать по пальцам. Схема изображения на рис. 3.47, представляет собой то, что я называю визуальным уравнением. Как и в математическом уравнении, в нем есть знак равенства, но речь идет о равенстве в его исходной форме - визуальном представлении проблемы, а не о переходе к абстрактному миру математических уравнений. Схема указывает, что некоторые части уравнения симметричны (равны) относительно пунктирных линий. Центральная линия со знаком равенства нарисована исключительно для наглядности - не думайте, что это какое-то стандартное обозначение. Впрочем, в геометрии поперечные знаки равенства на отрезках означают, что эти отрезки имеют равную или пропорциональную длину. Что же, исходные условия ясны. Но как эффект работает на практике? Переходим к следующей фазе - "формулировке отношений, которые описывают происходящее во времени. В процессе анимации (изображенной в виде четырех рисунков слева направо на рис. 3.48) изменяется прежде всего позиция оси симметрии. Сначала она располагается под углом 45° в правом нижнем углу переворачиваемой страницы, а затем перемещается в левый верхний угол той же страницы под углом 90° (то есть принимает вертикальное положение). Ось симметрии - всего лишь концепция, заложенная в основу эффекта, а не сам эффект. Помните об этом в процессе обобщения! Чтобы создать эффект, нужно разбить его на легко реализуемые составляющие. Примерный путь к решению показан на рис. 3.49.

Глава 3. Рисование и маски § Рис. 3.48. Перемещение оси симметрии Анимация эффекта основана на перемещении двух клипов вокруг оси симметрии. Один клип - зеркальное отражение переворачиваемой страницы (для наглядности назовем его reverse_mc). В начальном положении он повернут на 90° и находится в позиции пунктирного прямоугольника на рис. 3.49. Клип reversejnc стремится совместить свой правый край с осью симметрии. Другой клип - маска для клипа reversejnc;

назовем его maskReversejnc. В начальном положении клип наклонен под углом 45°, а в конечном положении он должен занять ту же позицию, что и reversejnc. Это означает, что в конце анимации клип reversejnc виден полностью, поскольку ни одна из его частей не скрывается маской.

Текущая страница Маска для зеркального отражения W^ г.

[Зеркальное отражение переворачиваемой страницы Рис. 3.49. Одна из двух масок, контролируемых осью симметрии Из факта симметрии следует, что светло-серая область на рис. 3.49 (видимая область новой страницы) симметрична рассматриваемой области. Открываемый клип остается неподвижным, но его маска ведет себя почти так же, как maskReverse_mc: она двигается аналогично, но симметрично относительно оси. На рис. 3.50 новая маска обозначена именем maskNewjnc.

maskReverse me maskNew me Рис. 3.50. Зеркальная маска Эффект листания страниц Симметричность двух масок относительно оси объясняет, почему площади переворачиваемой и открываемой страниц остаются симметричными. В данном случае мы имеем дело с проявлением иерархии симметрии;

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

Итоги Эффект листания страниц впервые был создан Эриком Нацке (http:// www.natzke.com), выдающимся дизайнером и автором многих впечатляющих SWF. Именно после знакомства с его работами я осознал, что многие графические эффекты базируются на симметрии и эффектах частиц (а иногда - и том и другом!) Соответствующий образ мысли поможет вам не только воспроизвести работы Эрика, но и расширить их. В этом трюке представлены основные концепции эффекта листания страниц, демонстрирующие общий подход к воссозданию подобных эффектов: • найдите и выделите базовые структурные элементы (из которых самым распространенным является симметрия, но также встречаются и другие - например, фракталы и эффекты частиц). Нарисуйте диаграммы с указанием места этих концепций в эффекте. Используйте ту систему наглядных обозначений, которая вам покажется наиболее удобной;

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

не пытайтесь немедленно преобразовать все концепции в математические формулы, потому что визуальное предоставление упрощает выделение закономерностей или поиск полезных концепций высокого уровня. Несмотря на все преимущества, математические уравнения плохо подходят для преобразования в визуальную форму. Они скорее абстрактны, нежели визуальны (вероятно, именно из-за этого большинство дизайнеров испытывает к ним подсознательное отвращение!).

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

ГЛАВА Анимация Трюки № 26- Настоящая глава посвящена подлинной сущности Flash: анимации. Интерфейс Flash основан на приемах традиционной покадровой анимации. Временная диаграмма Flash является электронным аналогом серии кадров;

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

разные элементы располагаются на разных глубинах. Ключевые и промежуточные кадры Flash тоже покажутся знакомыми всем, кто имел дело с традиционной анимацией: обычно ведущий художник рисует ключевые кадры, а его подручные занимаются черновой работой по построению промежуточных кадров. В Flash ключевые кадры создает дизайнер, a Flash автоматически генерирует промежуточные изображения. Все изменения, вносимые аниматором (например, перемещение графики на новое место), должны происходить в ключевых кадрах. Кадр 1 всегда является ключевым, а дополнительные ключевые кадры создаются командой Insert • Timeline • Keyframe (или клавишей F6). Допустим, вы создали графический объект в кадре 1 и разместили его в левой части сцены. Затем в кадре 19 создается ключевой кадр, и графический объект перемещается в новую позицию в правой части сцены. Чтобы создать анимацию, выделите начальный и конечный ключевые кадры на временной диаграмме и задайте параметру Tween на панели свойств значение Motion (или выполните команду Insert • Timeline • Create Motion Tween). Flash автоматически генерирует промежуточные изображения, в результате чего объект перемещается по сцене за 19 шагов. Чтобы убедиться в этом, достаточно прокрутить воспроизведение первых 19 кадров. Графика, размещенная на слое, отображается на сцене до тех пор, пока на этом слое не встретится пустой ключевой кадр. Например, если вы хотите убрать графический объект со сцены после кадра 19, вставьте пустой ключевой кадр в кадре 20 (команда Insert • Timeline • Blank Keyframe или клавиша F5). Хотя Flash выполняет за разработчика большой объем работы, в этой главе будут представлены многие трюки, которые пригодятся как опытному аниматору, так и новичку. Создание анимации требует времени и опыта, поэтому трюки прежде всего направлены на ускорение работы и упрощение процессов. В отли Плавное сценарное движение чие от аниматоров традиционной школы, аниматорам Flash приходится думать и о времени загрузки, и о скорости работы на стадии выполнения. Мы рассмотрим несколько способов автоматизации или сокращения объема работы: • • • упрощение анимации (как для аниматора, так и для Flash);

генерирование сложных анимаций с использованием стороннего инструментария, особенно при анимации персонажей;

построение сюжетов из коротких повторяющихся анимаций.

Flash также позволяет генерировать сценарное движение, при котором внешний вид следующего кадра анимации вычисляется и воспроизводится средствами ActionScript. Эта форма чаще всего применяется при создании интерактивных анимаций и в тех случаях, когда анимация определяется математическими правилами (например, физическими уравнениями). Учтите, что при использовании сценарной анимации Flash работает в системе координат печати, а не с математическими координатами. Начало координат печатной страницы находится в левом верхнем углу, тогда как в традиционной декартовой системе оно расположено в левом нижнем углу. Это означает, что начало координат Flash находится в левом верхнем углу сцены, а положительная полуось Y направлена сверху вниз, как показано на рис. 4.1.

-•X У •х Рис. 4. 1. Система координат Flash (слева) и традиционная декартова система координат (справа) Основным ограничением для сценарного движения является фактор быстродействия, поэтому далее будет показано, как на программном уровне построить плавную анимацию, чувствительную к действиям пользователя. Также будут описаны некоторые приемы ускоренного создания распространенных эффектов анимации.

ТРЮК № Плавное сценарное движение С увеличением частоты смены кадров механизм визуализации Flash начинает интенсивно поглощать процессорное время. Однако для создания субъективно плавной анимации не обязательно увеличивать частоту смены кадров.

Одна из худших привычек Flash-дизайнера - поднимать частоту смены кадров до абсурдно высокой величины, чтобы анимация казалась более гладкой. Такой вариант подойдет для простого ролика FLA с одной анимацией, но при построении большой анимации Flash или сайта нельзя разрешать процедуре перерисовки экрана поглощать все процессорное время. Если установить частоту смены кадров равной 95 кадрам в секунду (fps), Flash приходится постоянно строить Глава 4. Анимация новые изображения и выводить их на экран. Нехватка свободного времени приведет к перебоям со звуком и обработкой событий, что вызовет замедление отклика и ухудшение интерактивности приложения. В этом трюке мы разберемся, как многие начинающие Flash-дизайнеры учатся создавать анимации и почему этот способ не всегда является лучшим. Затем будет показано, как создать плавную анимацию без изменения частоты смены кадров.

Активизация движения пользователем (нарушение монополии onEnterFrame) В простейшем случае анимация создается на стадии разработки, индикатор текущей позиции Flash перемещается по шкале временной диаграммы, а кадры отображаются по очереди. Данный механизм называется покадровой анимацией и представляет собой электронный аналог серии рисунков. В таком сценарии самым очевидным способом повышения скорости анимации является повышение частоты смены кадров (частота смены кадров задается в диалоговом окне Document Properties, вызываемом командой Modify • Document). Когда аниматор классической школы начинает осваивать Flash, этот способ кажется ему хорошо знакомым. Но когда речь заходит о сценарной анимации Flash, подход приходится менять. Многих начинающих аниматоров учат, что сценарное движение следует реализовывать через обработчики onEnterFrame. В простых случаях, когда сценарная анимация привязывается к умеренной частоте смены кадров, такой способ вполне разумен. Однако с усложнением анимации приходится совершенствовать методику ее реализации. В противном случае либо пострадает быстродействие, либо придется смириться с,тем, что некоторые из ваших творческих замыслов реализовать не удастся. Не стоит полагать, что вся анимация должна находиться под управлением обработчиков onEnterFrame, - это заблуждение. Так как события onEnterFrame привязываются к частоте смены кадров, самый простой способ создания плавной анимации заключается в повышении частоты, что приводит к более частому генерированию событий onEnterFrame. Но если пользователь взаимодействует с графикой и анимацией, для управления анимацией гораздо эффективнее использовать обработчик onMouseMove. В частности, анимация перетаскивания объектов мышью является хорошим кандидатом на реализацию через обработчик onMouseMove. Режимы, в которых пользователь управляет ходом анимации или рисует на экране указателем мыши, также могут управляться событиями onMouseMove. Допустим, у нас имеется клип, перетаскиваемый мышью. Чтобы сделать анимацию перетаскивания более плавной, вместо повышения частоты смены кадров лучше воспользоваться обработчиком onMouseMove и организовать перерисовку сцены во время перемещения указателя мыши. Следующий фрагмент обеспечит плавную анимацию перетаскивания даже в том случае, если задать крайне низкую частоту смены кадров (даже 1 fps): function pressHandlerO { this.startDragO;

this.onMouseMove = functionO { Плавное сценарное движение // Обновление сцены во время перетаскивания объекта updateAfterEventC): }: this.onRelease = function О { this.stopOragO: delete this.onMouseMove;

}: this.onReleaseOutside = this.onRelease;

} // Создание анимационного клипа.и разрешение перетаскивания var puck:MovieClip = this.createEmptyMovieClipC'puck". this.getNextHighestDepth()): puck.1ineStyle(40. O C C C. 100);

xCCC puck.moveTo(-l, 0);

puck.lineToCl. 0);

puck._x = 275;

puck._y = 200;

puck.onPress = pressHandler;

'• Проанализируем ключевые части этого кода. Основная часть (код, следующий за определением pressHandler()) создает небольшой анимационный клип и назначает функцию pressHandler() его обработчиком события onPress. Код обработчика выполняется, когда пользователь щелкает на анимационном клипе. Функция pressHandler() присоединяет к клипу обработчик события onMouseMove, постоянно обновляющий экран в процессе перетаскивания. При такой реализации Flash осуществляет более частую перерисовку экрана только во время перетаскивания объекта, тем самым обеспечивается плавность движения без повышения частоты смены кадров. Эта реализация приведена для того, чтобы использующие ActionScript 1.0 усвоили суть данной методики. Аналогичная схема может быть реализована с использованием ООП и ActionScript 2.0 (см. трюк 20). Тот же принцип используется в следующем листинге, который создает простой «карандаш» для рисования на экране на базе обработчика события onMouseMove. Если переместить код из обработчика onMouseMove в обработчик onEnterFrame, то он будет выполняться даже тогда, когда это не нужно (то есть когда указатель мыши остается неподвижным): function penDownO { this.moveTo(_xmouse. _ymouse): this.onMouseMove = functionO { this.lineStyle(null. OxCCCCCC. 100): this.lineTo(_xmouse. _ymouse);

updateAfterEvent(): }: this.onMouseUp = functionO { delete this.onMouseMove;

} var drawClip:MovieClip = this.createEmptyMovieClipC'drawClip", thi s.getNextHi ghestDepth()): drawClip.onMouseDown = penDown;

Глава 4. Анимация Итоги Мысль об использовании обработчиков onEnterFrame для анимации выглядит заманчиво, и все же это не единственное событие, применяемое для создания анимации в Flash. В этом разделе была рассмотрена ситуация, в которой обработка события onMouseMove обеспечивала более эффективную и плавную анимацию. Эффективность объясняется меньшими затратами вычислительных мощностей (экран обновляется только при необходимости), а плавность - тем фактом, что частота возникновения событий onMouseMove не связана с частотой смены кадров (в отличие от события onEnterFrame). Хотя анимация на базе обработки события onMouseMove возможна только в том случае, если действия пользователя связаны с перемещением мыши (например, при операциях перетаскивании или рисования), при желании можно найти и другие события, синхронизируемые с обновлением экрана. Например, обновление может происходить при получении данных или завершении воспроизведения звука. Тем не менее, в некоторых случаях анимация должна синхронизироваться по времени. К счастью, onEnterFrame - не единственное событие, связанное с временем. Если потребуется создать несколько анимаций с разными частотами смены кадров, обычно лучше использовать функцию setlntervalQ для раздельного хронометража событий (см. трюк 27), вместо того чтобы использовать обработчик onEnterFrame для всех анимаций.

ТРЮК Синхронизация анимации по времени Сценарные субанимации не обязаны синхронизироваться по частоте смены кадров. Для воспроизведения субанимации с частотой, не зависящей от частоты смены кадров, применяются таймеры.

№ Скорость как сценарных, так и обычных анимаций изменяется несколькими способами. Первый способ - распределение анимации на большее (или меньшее) количество кадров или изменение частоты смены кадров. Чтобы вставить дополнительный кадр на временной диаграмме, нажмите клавишу F5. Чтобы удалить кадр, щелкните на нем правой кнопкой мыши (Windows) или с нажатой клавишей 8§ (Мае) и выберите в контекстном меню команду Remove Frames. Для синхронизации сценарной анимации по времени можно увеличить частоту смены кадров и выполнять обновление экрана в обработчике события onEnterFrame. Также возможно использование обработчиков других событий (например, onMouse Move - см. трюк 26), что позволит более рационально организовать обновление экрана и повысить плавность анимации без увеличения нагрузки на процессор. Но в некоторых ситуациях анимация должна синхронизироваться по времени, а не по внешним событиям наподробие перемещения мыши. При высоких требованиях к плавности анимации вместо завышения частоты смены кадров лучше создать интервальный таймер функцией setlntervalQ. Такое решение обладает тремя преимуществами: • Flash не тратит время на частую перерисовку всего экрана (что неизбежно при простом повышении частоты смены кадров;

Синхронизация анимации по времени • разные составляющие анимации могут воспроизводиться с разной скоростью;

• существует возможность относительно точно задавать скорость анимации независимо от частоты смены кадров или продолжительности анимации на временной диаграмме. Стандартные интервальные таймеры создаются вызовами следующего вида: интервал = set Interval(обработчик, период, аргументы):

где:

• интервал - идентификатор интервала, возвращаемый функцией setlnterval(). Идентификатор необходим для сброса (то есть остановки) существующих интервалов;

• обработчик - имя функции, используемой в качестве обработчика события таймера (то есть функции, автоматически вызываемой по истечении интервала);

• период - интервал между вызовами обработчика (в миллисекундах);

• аргументы - ноль и более аргументов, передаваемых' обработчику события. Чтобы передать несколько аргументов, разделите их запятыми (аргумент!, аргумент!,..., аргумент_п). Обработчик события, вызываемый функцией setlnterval(), принципиально отличается от обычных обработчиков, связываемых с экземплярами (вроде onMouseMove или onEnterFrame), тем, что при его вызове область видимости отлична от области видимости метода экземпляра. Чтобы метод вызывался для объекта (то есть в контексте конкретного экземпляра), используйте альтернативную форму setlnterval(): интервал = setInteryа](объект, "метод", период, аргументы): где: • объект - объект (например, экземпляр MovieClip), для которого вызывается метод, заданный вторым аргументом;

• "метод" - имя метода, вызываемого для объекта с заданными интервалами (в строковом формате).

Остальные аргументы имеют тот же смысл, что и в предыдущей форме setlnterval(). Если передать в параметре объект значение this, то вызов метода будет производиться в контексте текущего экземпляра (см. трюк 10), то есть обработчик получит доступ к свойствам текущего экземпляра. Функцию также можно вызвать в контексте клипа, передав соответствующий экземпляр в первом аргументе. Следующий вызов setlntervalQ создает интервальный таймер для экземпляра анимационного клипа myClip, который вызывает метод myEvent() каждую миллисекунду (или около того - все зависит от точности, которую сможет обеспечить Flash): interval ID = setlnterval(myClip. "myEvent". 1): Функция вызывается снова и снова до тех пор, пока интервальный таймер не будет сброшен. Сброс интервального таймера осуществляется методом clearlntervalQ: clearlnterval(interval ID);

Глава 4. Анимация При сбросе интервального таймера необходимо проследить за тем, чтобы переменная intervallD была видна в текущем контексте. Чаще всего функция, вызываемая setlnterval(), сбрасывает интервал при выполнении какого-либо условия (или при первом вызове, если событие является одноразовым). В другом варианте setlntervalQ вызывает метод экземпляра клипа. Такое решение гарантирует, что обработчик выполняется в контексте клипа, с которым мы работаем. В следующем примере интервальный таймер обеспечивает вызов метода mover() клипа myClip. Метод вызывается каждую миллисекунду (в рамках точности Flash) и увеличивает свойство myClip._x. Анимация клипа продолжается до тех пор, пока значение _х не превысит 300. Но при этом возникает одна проблема: методу mover() неизвестен идентификатор интервального таймера (intervallD), необходимый для его сброса. По этой причине мы делаем идентификатор свойством клипа, чтобы к нему можно было обратиться в методе mover() с использованием синтаксиса this.intervallD: function mover() { this._x++;

i f (this._x > 300) { clear-Interval (this.interval ID);

} updateAfterEventO;

} //Сохранение идентификатора интервального таймера в свойстве клипа myClip.interval ID = setlnterval(myClip. "interval". 1): myClip.interval = mover:

Код Следующий фрагмент кода демонстрирует преимущества setlnterval() перед обработчиком onEnterFrame для обновления анимации. Код создает два анимационных клипа и перемещает их по сцене с шагом в 1 пиксел. Кажется, что клип pucki движется гораздо быстрее и плавнее, потому что он анимируется с максимальной быстротой, доступной для Flash Player. Скорость перемещения клипа puck2 определяется текущей частотой смены кадров, которая по умолчанию равна 12 fps. mover = functionO { this._x++ = speed: i f (this._x > 550) { clearlnterval(this.interval): } updateAfterEventO: function enterFrameMover():Void { this._x += speed;

i f (this._x > 550) { delete this.onEnterFrame: } function drawBall(clip:MovieClip. x:Number. у:Number):MovieClip var mc:MovieClip = this.createEmptyMovieClip(clip.toString(). thi s.getNextHi ghestDepth()):

Быстрая и компактная анимация символов mc.lineStyle(40. OxCCCCCC. 100): mc.moveTo(-l, 0);

mc.lineTod, 0);

me._х = х;

mc._y = у;

return me;

} var speed:Number = 1;

var puckl:MovieClip = drawBalKpuckl. 20. 200): var puck2:MovieClip = drawBall(puck2. 30. 300);

puckl.interval Mover = mover;

puckl.interval = setlnterval(puckl. "intervalMover". 1);

puck2.onEnterFrame = enterFrameMover;

Обратите внимание на то, как создается интервальный таймер: • • идентификатор, возвращаемый при вызове setlnterval(), относится к типу Number;

идентификатор должен быть известен внутри метода, чтобы мы могли сбросить интервальный таймер в конце анимации. Идентификатор можно передать в аргументе, но в данном случае мы включаем его в клип puckl в виде переменной interval.

Конечно, очень высокая частота смены кадров (например, 95 fps) обеспечит быстрое и плавное воспроизведение обоих клипов, но у этого способа есть два недостатка. Во-первых, зависимость анимации от частоты смены кадров усложняет изменение скорости разных графических элементов. Во-вторых, если заставить все двигаться слишком быстро, Flash Player не сможет обеспечить затребованную частоту смены кадров. Решение с setlntervalQ позволяет избирательно задавать те объекты анимации, для которых высокая скорость действительно критична (см. трюк 71). Стоит заметить, что при ускорении всего SWF-ролика максимальная частота смены кадров, которой вам удастся достичь, гораздо ниже той, которая достигается при ускорении отдельных частей.

Итоги Хотя кое-кто жалуется на недостаточную быстроту Flash Player, существует много способов ускорить работу кода. Правильный выбор обработчиков событий (см. трюк 26), благодаря которому код выполняется только в случае необходимости, делает анимацию более плавной и улучшает ее субъективное быстродействие. Другие трюки из области оптимизации приводятся в главе 9.

Быстрая и компактная анимация №28 символов ТРЮК Ручное построение анимации занимает целую вечность. Несколько фокусов «для служебного пользования» от аниматора, работавшего на студии Диснея, избавят вас от части тяжелой работы. При работе над анимацией Flash всегда приходится решать вопрос, как добиться цели минимальными средствами, поскольку пропускная способность канала Глава 4. Анимация и быстродействие Flash являются величинами ограниченными. Одним из фокусов, позволяющих свести к минимуму объем работы и нагрузку на канал связи, является цикл ходьбы. Создание многократно повторяемой анимации левого и правого шагов, которая на экране выглядит как плавная и непрерывная анимация шагающего человека, существенно экономит ресурсы по сравнению с полной анимацией с разными шагами. Трюк, представленный в этом разделе, был создан в процессе работы по вебдизайну, которой я занимался совместно с Адамом Филипсом, аниматором из студии Диснея и обладателем ряда призов. У Диснея ему приходилось работать над оптимизацией анимации с получением'минимального количества кадров - этот навык был в полной мере использован им в веб-комиксах (http:// www.biteycastle.com). Мы рассмотрим некоторые приемы, которые использовались для сокращения количества кадров для анимации персонажа по имени Скриббл.

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

• скорость ходьбы должна соответствовать скорости движения. Звучит тривиально, но вам, наверняка, попадались видеоигры или дешевые мультфильмы, в которых персонажи словно скользят вдоль пола. Эффект возникает из-за того, что скорость движения не совпадает с частотой перестановки ног. Для создания реалистичного цикла ходьбы необходимо множество кадров и хорошее чутье аниматора. Одно из возможных решений проблемы - создание цикла ходьбы, в котором нарушение перечисленных правил несущественно. Создайте упрощенную анимацию (с меньшим количеством кадров), которая будет выглядеть эксцентричной и непохожей на нормальную походку;

персонаж даже может ненадолго зависать в воздухе, что сведет к минимуму эффект скольжения. При грамотной реализации стилизованная походка даже сделает вашего персонажа более оригинальным. Крайним случаем такого рода служит Тасманийский дьявол из мультфильма студии «Warner Bros». Он двигается настолько быстро, что его невозможно разглядеть, - по экрану просто проносится вихрь из одной точки в другую. Это хороший пример того, как простота анимации изначально закладывается в концепцию персонажа. Сайт, над которым работали мы с Адамом, обладал оригинальным интерфейсом - вместо того чтобы перемещаться по сайту при помощи мыши, посетитель водил за ней персонажа по имени Скриббл. Если Скриббл не занимался чем-то иным (например, обижался, разглядывал найденный предмет или просто думал о чем-то своем), он следовал за указателем мыши.

Быстрая и компактная анимация символов Например, если указатель мыши находился справа от Скриббла (рис. 4.2), он переходил в текущую позицию указателя с использованием цикла ходьбы, изображенного на рис. 4.3.

Pages:     | 1 || 3 | 4 |   ...   | 8 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.