В этой шестой части AS3 101 мы будем изучать темы, связанные с «Display List», системой рендеринга Flash. С глубоким пониманием того, как программировать список отображения, открывается целый мир динамических возможностей. Вы больше не будете привязаны к статической структуре отображения, которую вы создаете на временной шкале в IDE, но вы можете создавать и уничтожать мувиклипы (и многое другое) с помощью кода.
Нам нужно многое знать, поэтому нам лучше начать. Наша конечная цель — стена кликабельных изображений (посмотрите демо ). Несмотря на простоту, он вводит много методов программирования списка отображения.
Шаг 1. Что такое список отображения?
Список отображения — это термин, используемый в отношении механизма и процесса рендеринга Flash. Технически, это не сам движок, а иерархическое дерево того, что визуализируется. Все, что можно увидеть в фильме Flash, является частью списка отображения. Следствием этого понятия является то, что если объект находится в списке отображения, то он рисуется средством визуализации. Если объект отсутствует в списке отображения, он не рисуется.
Мы рассмотрим некоторые из основных концепций списка отображения в следующих шагах.
Шаг 2. Объект DisplayObject
ActionScript определяет то, что называется «DisplayObject». Мы собираемся опередить себя здесь; Мы еще не обсуждали методы объектно-ориентированного программирования в AS3 101, и, к сожалению, эту конкретную тему лучше понять с пониманием наследования. Не беспокойся Мы скоро расскажем об ООП, а пока приложу все усилия, чтобы скрыть от вас эти странные подробности.
Таким образом, DisplayObject — это практически все, что можно отобразить в списке отображения. Например, старый добрый MovieClip является типом DisplayObject. Тем не менее, массив не является. ActionScript 3 определяет довольно много типов отображаемых объектов, и вы можете определить свои собственные, как это происходит. Ниже приведен список объектов DisplayObject, предоставляемых проигрывателем Flash Player.
- Форма Форма может отображать векторную графику. Если вы рисуете фигуру на сцене и публикуете свой SWF, эта форма представляется в ActionScript как объект Shape. Вы можете работать с Shapes программно, если вы хотите создавать векторные объекты с кодом.
- Растровое изображение По сути просто изображение. Если вы загрузите файл JPG и отобразите его, он будет представлен в виде растрового изображения. Любопытно, что если вы импортируете растровое изображение в библиотеку Flash и перетащите его экземпляр на сцену, он не станет экземпляром растрового изображения в ActionScript. Вместо этого это объект Shape с растровым заполнением. Странно, я знаю.
- Видео Как и следовало ожидать, что-то, способное отображать видео через NetStream или встроено в SWF.
Есть больше типов, но мы доберемся до них вовремя. На данный момент важно отметить, что хотя формы, растровые изображения и видео отображают вещи по-разному, на самом деле они имеют много общего. Наиболее очевидно, что все они что-то показывают. Их можно добавить в список отображения. Да, они специализируются на различных типах задач отображения, но, в конце концов, все они являются объектами DisplayObject и, следовательно, могут отображаться проигрывателем Flash Player.
Кроме того, все объекты DisplayObject могут выполнять определенные задачи, связанные с отображением. Это включает, но не ограничивается:
- Установка положения (через свойства x и y и в Flash Player 10, z )
- Настройка вращения (а в Flash Player 10 — вращения X , вращения Y и вращения Z )
- Установка масштаба (через свойства height , width , scaleX и scaleY )
- Настройка альфа
- Установка blendMode
- Настройка фильтров
- Применение маски
- Выполнение тестов на попадание (через hitTestObject и hitTestPoint )
Вы можете видеть, что это довольно большой список. Конечно, любой отображаемый объект должен иметь некоторые базовые функциональные возможности. Любой другой DisplayObject может делать все вышеперечисленное, но начинает добавлять больше функциональности.
Итак … почему бы просто не пойти с более функциональными, более способными типами? Во-первых, специализация. Только растровое изображение может отображать загруженное изображение, и только видеообъект может отображать видео. Но есть и то, что называется накладными расходами. Вы можете нарисовать векторную графику в MovieClip точно так же, как вы можете использовать Shape, но поскольку MovieClip делает намного больше, чем Shape, для его существования требуется больше памяти. Если все, что вам нужно, это отображать векторную форму, а не возиться с интерактивностью мыши или временными шкалами, тогда вам следует просто использовать форму, а не мувиклип.
Шаг 3: Интерактивный объект
Кроме того, ActionScript определяет нечто, называемое «InteractiveObject». InteractiveObject — это другой тип DisplayObject, поэтому он может выполнять все те общие вещи, о которых мы говорили на последнем шаге. Но InteractiveObjects добавляет ключевой элемент: интерактивность (вы должны были увидеть, что он появится). Существует два типа интерактивных объектов:
- TextField Вы не ожидаете, что TextField будет интерактивным, но если TextField является полем ввода, он может реагировать на пользовательские события, такие как изменения в набранном тексте. В то же время вы можете определить, когда пользователь фокусирует и де-фокусирует TextField.
- SimpleButton SimpleButton — это представление ActionScript символа библиотеки кнопок. Большинство разработчиков ActionScript избегают как версии библиотеки, так и версии ActionScript, но приятно знать, что это здесь.
Несмотря на то, что TextFields и SimpleButtons сильно отличаются, они оба имеют общие черты, в дополнение к общим чертам, общим для всех объектов DisplayObject. InteractiveObjects может:
- иметь контекстное меню
- получать события мыши от пользователя
- получать ключевые события от пользователя
- вкладываться с помощью клавиши табуляции
Опять же, это то, что могут делать все InteractiveObjects. Форма, однако, не может делать эти вещи. И наоборот, TextField имеет такие вещи, как альфа и фильтры, как и все объекты DisplayObject. Глубже в кроличью нору мы идем …
Шаг 4: DisplayObjectContainer
Наконец, ActionScript определяет нечто, называемое «DisplayObjectContainer». DisplayObjectContainer является типом DisplayObject, поэтому его можно позиционировать, фильтровать и так далее. Это также тип InteractiveObject, поэтому он может получать события мыши и так далее. Но это дополнительно добавляет больше функциональности.
Вы, вероятно, знакомы с идеей размещения одного мувиклипа внутри другого мувиклипа. Тот факт, что MovieClip может содержать другие экранные объекты, делает его DisplayObjectContainer. Эта идея сдерживания — это то, что DisplayObjectContainer добавляет в таблицу. Практически все, что определено в DisplayObjectContainer, связано с управлением дочерними объектами этого контейнера. Контейнер может содержать другой контейнер, как вы знаете из файлов Flash с вложенными структурами MovieClip. DisplayObjectContainers может:
- добавить детей
- удалить детей
- получить ссылки на своих детей
- управлять глубинами (порядок размещения) своих детей
- включить или отключить мышь или вкладку сразу для всех дочерних элементов
Как вы можете видеть, DisplayObjectContainer в основном связан с содержанием других объектов DisplayObject. Но помните, что это все, чем является DisplayObject (его можно перемещать, масштабировать, поворачивать, фильтровать и т. Д.), А также все, что представляет собой InteractiveObject (имеет события мыши и клавиш и т. Д.). Есть три DisplayObjectContainers, о которых нужно знать:
- Спрайт Вы можете думать о Спрайте как о мувиклипе без временной шкалы. Его можно перетаскивать, связывать со звуком, отображать программно нарисованную векторную графику и изменять его интерактивное поведение с помощью таких вещей, как useHandCursor и hitArea . Большинство компонентов пользовательского интерфейса, которые поставляются с Flash, — это спрайты (а не мувиклипы)
- MovieClip — это тот, кого вы знаете и любите. Вы можете думать о Спрайте как о мувиклипе без временной шкалы, или вы можете думать о мувиклипе как о спрайте с временной шкалой. MovieClip может делать все, что может делать Sprite, плюс он добавляет свойства и методы для работы со своей временной шкалой (например, gotoAndStop , currentFrame и т. Д.).
- Загрузчик Загрузчик может загружать и впоследствии отображать внешний SWF-файл или файл изображения. Работа с загрузчиками важна, потому что они являются единственным способом загрузки этих внешних ресурсов.
По общему мнению, если вам не нужна временная шкала, Sprite предпочтительнее, чем MovieClip, потому что это связано с меньшими накладными расходами.
Шаг 5. Визуальная работа со списком отображения
Я уверен, что вам удобно помещать вещи в список отображения, даже если вы не знакомы с этим термином. Когда вы создаете символ MovieClip и перетаскиваете его экземпляр на сцену, вы создаете объект MovieClip и добавляете его в список отображения. Давайте сделаем следующее:
Создайте новый документ ActionScript 3. Нарисуйте фигуру на сцене, выберите ее и превратите в символ MovieClip. Дайте ему имя экземпляра. Я буду использовать «manualClip».
Теперь щелкните в первом кадре слоя и откройте панель «Действия». Введите следующий код:
1
2
3
4
5
|
var len:int = this.numChildren;
for (var i:int = 0; i < len; i++) {
var display:DisplayObject = this.getChildAt(i);
trace(display.name);
}
|
Если вы запускаете фильм, вы должны увидеть следующее на панели «Вывод»:
Теперь, что мы сделали в этом сценарии? Мы начали работать с некоторыми темами списка отображения.
Сначала было ням детей . Это свойство любого DisplayObjectContainer, и оно возвращает количество непосредственных дочерних элементов, содержащихся в нем (оно углубляется только на один уровень: все дочерние элементы, которые в дальнейшем содержат дочерние элементы, не учитываются). Мы устанавливаем это значение в другую переменную (называемую «len») согласно хорошим методам зацикливания, обсуждавшимся в предыдущем уроке AS3 101 .
Затем внутри цикла мы используем другой метод DisplayObjectContainer, getChildAt . Это один из способов получения доступа к отдельным дочерним элементам внутри контейнера. В этом случае мы получаем их по глубине.
Затем, когда у нас есть объект DisplayObject, мы отслеживаем его имя.
Вас может удивить или не удивить, что объект, который мы поместили вручную в IDE, обнаруживается с помощью ActionScript. По большей части, есть небольшая разница между работой в IDE или с кодом, когда дело доходит до фактически отображаемого объекта. Много раз просто нарисовать или разместить активы на сцене визуально. В других случаях нам может понадобиться быть более динамичным и создавать объекты с кодом. Приятно знать, что в любом случае, мы в значительной степени имеем полный контроль над процессом.
Шаг 6: Программная работа со списком отображения
Мы сделаем еще шаг вперед (отсюда увеличение числа шагов …). Теперь мы создадим и отобразим объект DisplayObject с кодом. Используйте FLA, с которого вы начали на последнем шаге, и добавьте следующий код вверху (перед кодом, который вы написали в прошлый раз).
1
2
3
|
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill(0xff9933, 1);
sprite.graphics.drawRect(0, 0, 200, 200);
|
Что это делает? Ну, это создает спрайт. Спрайт может быть создан только программно; все символы в вашей библиотеке — мувиклипы. Помните, что Sprite — это просто мувиклип без временной шкалы. Поскольку мы не можем реально создавать контент на временной шкале с использованием кода, мы также можем использовать спрайты всякий раз, когда нам нужно создать DisplayObjectContainer программно.
После того, как у нас есть наш Sprite, мы используем свойство graphics, чтобы нарисовать квадрат с кодом. Мы можем многое сделать с помощью API рисования, но в двух словах это как пошаговый набор инструкций о том, как использовать перо для рисования фигур. Сначала мы устанавливаем цвет заливки, затем используем удобный метод для перемещения пера по прямоугольнику, рисуя квадрат.
К сожалению, у нас нет времени глубже вникать в объект Graphics, но ознакомьтесь с документацией для получения полного списка вещей, которые вы можете сделать, чтобы нарисовать программно. Это довольно глубоко и может сделать гораздо больше, чем рисовать прямоугольники. Фактически, ядро 3D движка Flash Player 10 основано на способности рисовать треугольники. Это сходит с ума … но получайте удовольствие!
В любом случае, если вы запустите фильм сейчас, вы не увидите изменений с прошлого раза. Это почему? Потому что, хотя мы создали Sprite, его еще нет в списке отображения. Запомни это. Это поездки опытных разработчиков все время. Вам необходимо добавить Sprite (или любой другой объект DisplayObject, созданный с помощью кода) в список отображения. Это делается с помощью метода addChild контейнера, который вы хотите использовать, например:
1
|
this.addChild(sprite);
|
Теперь, если вы запустите сценарий, вы должны не только увидеть большой оранжевый квадрат, занимающий ваш экран (и, возможно, скрыть другой созданный вами клип), вы должны увидеть дополнительную строку на панели «Вывод»:
Заметьте имя, которое ему дали? Это имя по умолчанию. Flash ведет подсчет количества созданных экземпляров (я полагаю, в течение жизни фильма) и просто применяет имя по умолчанию «экземпляр N » для всех новых экранных объектов. Это имя может быть установлено в любое время. Это не обязательно, но может быть полезно дать вашим программно созданным экранным объектам уникальное имя, чтобы вы могли легко идентифицировать их позже, либо по трассировке, либо при использовании getChildByName. Давайте добавим следующую строку (еще до цикла, но после создания спрайта);
1
|
sprite.name = «orangeBox»;
|
И это приводит к:
Для справки, вот весь сценарий, как он должен стоять:
01
02
03
04
05
06
07
08
09
10
11
|
var sprite:Sprite = new Sprite();
sprite.graphics.beginFill(0xff9933, 1);
sprite.graphics.drawRect(0, 0, 200, 200);
sprite.name = «orangeBox»;
this.addChild(sprite);
var len:int = this.numChildren;
for (var i:int = 0; i < len; i++) {
var display:DisplayObject = this.getChildAt(i);
trace(display.name);
}
|
Возможность создавать и добавлять DisplayObject во время выполнения является мощной техникой Flash и открывает двери для динамических дисплеев, как мы увидим в конце урока.
Шаг 7: Понимание глубины
Само понимание глубины не так сложно: все экранные объекты наслоены в «порядке наложения». Объект с большей глубиной будет «сверху» и затемнит объект с меньшей глубиной.
И не трудно иметь в виду, что контейнеры имеют свой собственный набор глубин для своих детей. MovieClip, полный Shapes, сложит Формы в порядке. Поместите этот мувиклип поверх другого мувиклипа, полного фигур, и все верхние фигуры мувиклипа будут поверх нижних форм мувиклипа. Нет чередования дочерних элементов одного контейнера с дочерними элементами другого контейнера.
Сложно понять, как ActionScript 3 обрабатывает глубину. Но даже это не так уж и плохо, и вам может быть интересно узнать, что это намного лучше, чем то, как ActionScript 2 обрабатывает глубину.
Во-первых, вы должны знать, что самая низкая глубина любого контейнера равна 0. Нет отрицательных глубин.
Во-вторых, глубина любого данного контейнера всегда плотно упакована . Это означает, что в числах глубины нет пустых мест. Если есть 10 детей, они занимают глубины от 0 до 9, без исключений.
В-третьих, поймите, что ActionScript управляет этой плотно упакованной системой нумерации для вас. Каждый раз, когда вы добавляете дочерний элемент в контейнер, этот дочерний элемент вставляется в стек как отдельная карта, помещаемая обратно в колоду. Ребенок, добавленный на определенной глубине, выталкивает всех детей на глубину и высоту вверх в порядке наложения. Ребенок, которого удаляют с определенной глубины, заставляет всех детей выше скользить вниз, чтобы заполнить пробел.
И, наконец, вы должны изучить несколько методов, которые мы можем использовать для манипулирования дочерними элементами контейнера.
addChild — это самый простой способ добавить DisplayObject. Вы просто вызываете его из контейнера, к которому хотите добавить, и передаете DisplayObject. Это автоматически помещает дочерний элемент на вершину стека. Мы видели это на последнем этапе. У нас уже был MovieClip на этапе до запуска нашего кода, а затем мы использовали addChild на нашем новом Sprite. Это поместило Спрайт на глубину 1 выше мувиклипа.
addChildAt похож на addChild, только вы можете указать индекс, по которому нужно добавить дочерний элемент. Если мы изменим код предыдущего шага, чтобы прочитать:
1
|
this.addChildAt(sprite, 0);
|
Мы добавим этот спрайт на глубину 0. Поскольку мувиклип уже был на глубине 0, он поднимается на глубину 1, а спрайт входит на глубину 0. Если вы запустите этот фильм, вы увидите спрайт на дно, и изменение на выходе:
removeChild и removeChildAt работают так же, как их счетчики «добавить». removeChild указывает дочерний элемент, который нужно удалить, так же, как addChild. removeChildAt немного отличается тем, что все, что вам нужно, это номер глубины. Что бы ни был ребенок на этой глубине, его удаляют. Например, основываясь на нашем предыдущем примере, мы могли бы удалить мувиклип, который мы добавили в IDE:
1
|
this.removeChildAt(1);
|
или
1
|
this.removeChild(manualClip);
|
И то, и другое полезно для разных вещей … иногда вы точно знаете, какой клип удалить, иногда просто хотите удалить дочернего элемента, который находится внизу или сверху.
getChildAt предоставляет один способ доступа к дочерним элементам контейнера. В этом случае мы получаем его по глубине. Мы использовали это в начале нашего текущего примера FLA.
1
|
this.getChildByName(«manualClip»);
|
получит ссылку на мувиклип, который мы поместили в IDE (если, конечно, мы его уже не удалили)
getChildIndex дает нам значение глубины предоставленного дочернего объекта
setChildIndex берет дочерний объект и новый индекс глубины и меняет глубину вокруг, чтобы разместить запрос. Опять же, это имеет эффект, похожий на перемещение одной карты в колоде карт. Промежуточные дети скользят вокруг, чтобы освободить место и заполнить пробел.
Есть больше методов для манипулирования глубинами и многое другое. Ознакомьтесь с документацией класса DisplayObjectContainer для получения дополнительной информации.
Шаг 8: альфа, видимый и находящийся в списке отображения
Есть три способа не видеть данный экранный объект: установите его альфа в 0, установите его видимость в false или передайте его в removeChild. Какая разница между ними? Рад, что ты спросил.
Установка альфа в 0 сделает так, чтобы ваши глаза не могли видеть объект. Тем не менее, он все еще присутствует. Альфа 0 похожа на человека-невидимку: вы, возможно, не сможете его увидеть, но вы все равно можете столкнуться с ним. Если у вас есть мувиклип с подключенными к нему событиями мыши, и вы установили альфа в 0, вы все равно увидите триггер этих событий мыши. Это может быть полезно для создания области попадания, которая запускает события в другом месте фильма.
Если ваша цель состоит в том, чтобы просто не видеть объект, то альфа 0 по-прежнему проблематичен, потому что средство визуализации все еще рисует объект. В результате вы не можете его видеть, но Flash все еще работает для рендеринга прозрачного пикселя. Лучше установить видимость на false.
Если для свойства visible установлено значение false, это приводит к удалению экранного объекта из очереди рендеринга, поэтому, если объект исчез, после того как вы закончите с этим, вы должны установить для visible значение false. Тем не менее, невидимый экранный объект все еще немного невидим, занимая место. Может быть, отношение к нему как к газообразному элементу более уместно. Он не вызывает событий, но занимает место в стеке глубины, и его размеры влияют на размеры его контейнера. В противном случае, это довольно хорошо не существует.
Когда вы удаляете ребенка, вы все равно можете привязать его к переменной. Он все еще существует в памяти, и затем вы можете вызвать addChild с ним в любой момент, чтобы вернуть его в список отображения. Однако, если у вас нет другой ссылки на него при вызове removeChild, объект становится пригодным для сборки мусора и будет удален полностью, если вы не будете на нем висеть.
У каждого из этих методов есть время и место, и хороший разработчик ActionScript знает, когда и какой из них использовать. Существует хорошая сводка трех техник, обсуждаемых Колином Моком в InsideRIA .
Шаг 9. Создание предопределенных объектов DisplayObject из библиотеки
Одна из самых крутых вещей, которые мы можем сделать, — это объединить инструменты визуального выражения Flash IDE и динамичность списка отображения на основе кода. Мы можем создать символ во Flash, настроить его так, чтобы ActionScript имел к нему доступ, а затем создать экземпляры этих символов с помощью кода. Давай проверим это.
Сначала нарисуйте несколько рисунков и превратите их в символ. Однако, когда вы получите диалоговое окно для создания символа, убедитесь, что ваше окно выглядит так:
Вместо этого:
Если оно выглядит как окно меньшего размера, нажмите кнопку «Дополнительно», чтобы увеличить его.
Теперь нажмите на флажок «Экспорт для ActionScript». При этом весь раздел «Связывание» становится активным.
Текст, введенный в поле «Class:», будет основан на имени, которое вы ввели в Имя. Если вы хотите изменить его, имейте в виду, что применяются определенные правила. По сути, это должно быть допустимое имя переменной ActionScript: только цифры, буквы, подчеркивания и знаки доллара, и оно не может начинаться с цифры.
Оставьте все остальное как есть и нажмите ОК. Вы должны получить это предупреждение:
Когда вы получаете это окно, оно говорит что-то, о чем вам не нужно беспокоиться прямо сейчас. Тем не менее, вы захотите узнать об этом в конце концов (об этом мы поговорим в следующем выпуске AS3 101). Я бы посоветовал вам не поддаваться искушению, чтобы проверить вариант «Не предупреждать меня снова». Пока вам не нужно это предупреждение, но придет время, когда вы действительно захотите это предупреждение, потому что вы ожидаете, что произойдет что-то еще. Однако вы можете включать и выключать это предупреждение, перейдя в настройки в разделе «Предупреждения» и отметив опцию «Предупреждать об автоматическом создании классов ActionScript для временных шкал».
Итак, что все это сделало? Это дало нам доступ к этому символу через ActionScript. Имя, которое вы ввели в поле «Класс», — это имя, которое необходимо использовать для создания нового символа с кодом. Например, я ввел «Ящик» в поле «Класс», и теперь я могу написать этот скрипт:
1
2
|
var box:Box = new Box();
addChild(box);
|
Запустите его, и вы увидите хотя бы один экземпляр вашего символа на экране (возможно, он уже был там, когда вы его создали). Дело в том, что мы выполнили действие ActionScript, аналогичное перетаскиванию этого символа на сцену в IDE.
Это невероятно полезно; он сочетает в себе удобство создания мувиклипа с использованием визуальных инструментов IDE с динамической природой размещения объектов на сцене с помощью кода. Представьте, что у вас есть клип, который представляет собой нечто большее, чем просто загруженное изображение; возможно у этого есть лейбл, и некоторая дополнительная художественная работа для эффекта ролловера. Вы можете легко настроить клип в IDE, а затем создать столько, сколько вам нужно, с помощью ActionScript. На самом деле, мы сделаем именно это всего за несколько шагов.
Шаг 10: родители просто не понимают
На самом деле, в ActionScript родители понимают. Простите за милое звание.
Особенность экранных объектов в том, что если вы добавили объект в качестве дочернего элемента, будь то через IDE или через код, то у этого объекта есть родительский объект. Однако, в отличие от ваших или моих родителей, вы можете произвольно переназначить отношения родитель-ребенок по мере необходимости. Это включает в себя не только методы addChild и removeChild, которые мы уже рассмотрели, но и идею повторного воспитания . Это просто сводится к использованию addChild для экранного объекта, у которого уже есть один родительский элемент, чтобы поместить его в другого родительского объекта. Учтите следующее. Предполагается, что в вашей библиотеке есть символ Box.
01
02
03
04
05
06
07
08
09
10
|
var parent1:Sprite = new Sprite();
parent1.x = 100;
addChild(parent1);
var parent2:Sprite = new Sprite();
parent2.y = 100;
addChild(parent2);
var box:Box = new Box();
parent1.addChild(box);
|
Если вы запустите этот фильм, вы увидите то, что ожидали: окно на экране. Блок наследует позицию x, установленную parent1, так как мы добавили его как дочерний элемент для этого спрайта, поэтому блок должен находиться на расстоянии 100 пикселей от левого края.
Теперь давайте добавим это после сценария, который мы только что написали:
1
2
3
4
5
|
box.addEventListener(MouseEvent.CLICK, reparentBox);
function reparentBox(me:MouseEvent):void {
parent2.addChild(box);
}
|
Теперь запустите фильм, и коробка находится там же. Но теперь нажмите на коробку, и она будет двигаться! Почему? потому что мы заново сделали это. Мы попросили parent2 с x = 0 и ay = 100 быть родительским элементом окна. Это неявно удаляет блок из дочернего для parent1. И как только parent2 становится родителем блока, блок наследует позицию (вместе со всеми другими соответствующими свойствами, такими как альфа или вращение) parent2, поэтому теперь он должен быть вдоль левого края и на 100 пикселей вниз от вершины.
Эта идея довольно мощная, и она позволяет вам повторно использовать экранные объекты, которые в противном случае пришлось бы уничтожать и воссоздавать, что гораздо менее эффективно.
Этот метод переучивания, вероятно, наиболее часто используется при использовании Loaders. Загрузчик сам по себе является экранным объектом. Таким образом, вы можете добавить загрузчики в список отображения. У объекта Loader, как его потомка, есть другой экранный объект, который является фактическим загруженным содержимым. Таким образом, вам может быть удобно использовать Loader исключительно в качестве загрузочного устройства и после загрузки содержимого перезаписать содержимое Loader в другой контейнер. Это позволило бы избежать накладных расходов на наличие контейнера (загрузчика), который в противном случае ничего не добавляет к отображению. Простой пример:
01
02
03
04
05
06
07
08
09
10
11
12
|
var imageContainer:Sprite = new Sprite();
imageContainer.x = 100;
imageContainer.y = 100;
addChild(imageContainer);
var loader:Loader = new Loader();
loader.load(new URLRequest(«someImage.jpg»));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
function onImageLoad(e:Event):void {
imageContainer.addChild(loader.content);
}
|
Чтобы разбить это, сначала мы создаем Sprite, который в конечном итоге будет содержать загруженное изображение.
Затем мы создаем Loader, загружаем изображение и добавляем прослушиватель событий для события COMPLETE (обратите внимание, что события Loader связаны не с самим Loader, а со свойством Loader contentLoaderInfo . Это странно, но так оно и есть. I Имейте проблемы с запоминанием этого тоже, так что не расстраивайтесь, когда вы ошибаетесь).
Переопределение происходит в обработчике события COMPLETE. Мы не можем сделать это раньше, так что имейте это в виду. Но все, что мы делаем, это говорим контейнеру изображений Sprite добавить объект содержимого Loader как дочерний. Контент является дочерним DisplayObject загрузчика. Но без этой строки наш скрипт не будет отображать загруженное изображение, потому что загрузчик никогда не был добавлен в список отображения. Однако Sprite был, и поэтому мы можем неявно добавить загруженное изображение в список отображения, добавив его в Sprite.
Имейте в виду, что, хотя это полезный метод, нет правила, согласно которому вы должны делать это таким образом. Прекрасно просто создать загрузчик и добавить его в список отображения. Но будут времена, когда у вас уже будет контейнер, и вы, возможно, захотите устранить дополнительные издержки. Или вы можете захотеть, чтобы способность Sprite, скажем, установила для buttonMode значение true, чего не может сделать Loader.
Шаг 11: этап и корень
Последняя тема обсуждения, прежде чем мы перейдем к практическому примеру. Все экранные объекты имеют свойство, называемое stage, и одно, называемое root . Если вы привыкли к ActionScript 2, эти концепции звучат знакомо, но были пересмотрены в ActionScript 3.
Этап — самый верхний, внучатый, самый плохой из всех DisplayObjectContainer. Этап в основном представляет собой холст, на котором рисует Flash Player. Все остальные фрагменты ролика, изображения, текст и т. Д. В конечном итоге добавляются в рабочую область. В результате, есть только одна стадия. И все видимые экранные объекты имеют в качестве родителя, непосредственно или через родословную, Стадию.
Свойство stage разрешает доступ к этому специальному контейнеру. Почему это имеет значение? Потому что со сценой вы можете получить положение мыши по отношению к Flash Player, а не по отношению к рассматриваемому экранному объекту. Вы также можете добавить прослушиватели событий на сцену, такие как MOUSE_MOVE или MOUSE_UP. Учти это:
01
02
03
04
05
06
07
08
09
10
11
12
|
var box:Box = new Box();
addChild(box);
box.addEventListener(MouseEvent.MOUSE_MOVE, boxMouseMove);
stage.addEventListener(MouseEvent.MOUSE_MOVE, stageMouseMove);
function boxMouseMove(me:MouseEvent):void {
trace(«mouse moving on the box»)
}
function stageMouseMove(me:MouseEvent):void {
trace(«mouse moving on the stage»)
}
|
Попробуйте (если у вас все еще есть коробка в вашей библиотеке). Теперь наведите курсор мыши … обратите внимание, где появляются следы. Событие на сцене будет происходить постоянно. Но событие «бокс» происходит только тогда, когда мышь находится над боксом. Поэтому, если вы хотите слушать движение мыши в целом, а не только когда мышь оказывается в определенной области, вы должны слушать сцену.
Корень — это еще один специальный контейнер, но не такой особенный, как сцена. Корень на самом деле является основной временной шкалой любого данного SWF. При создании документа Flash вам всегда предоставляется временная шкала, которая является временной шкалой верхнего уровня. Вы всегда можете создавать больше мувиклипов с большим количеством временных шкал и вкладывать их в иерархию, но всегда есть одна основная временная шкала. Это корень.
Однако не всегда может быть только один корень. Когда вы создаете два SWF-файла и загружаете один в другой, root означает разные вещи в зависимости от того, где указан код. То есть каждый из двух SWF-файлов имеет свою собственную основную временную шкалу, и root в SWF 1 относится к основной временной шкале SWF 1, а root в SWF 2 относится к основной временной шкале SWF 2.
Таким образом, может быть только один этап, но корней столько же, сколько загруженных SWF-файлов.
Теперь, прежде чем мы оставим эту тему, очень важно понять один момент: этап экранного объекта и корневые свойства устанавливаются только тогда, когда экранный объект находится в списке отображения. Если это не так, то эти свойства являются нулевыми . Например:
1
2
3
4
5
6
|
var sprite1:Sprite = new Sprite();
addChild(sprite1);
var sprite2:Sprite = new Sprite();
trace(sprite1.stage);
trace(sprite2.stage);
|
Первая трассировка выведет [объект Stage], а вторая будет нулевой, поскольку sprite2 никогда не добавлялся в список отображения.
Это распространенный источник ошибок в более сложных проектах. Иногда вы ожидаете, что будет установлен этап , но объект еще не был добавлен в список отображения, поэтому вы получаете нулевые ошибки ссылки.
Просто что-то, чтобы знать.
Шаг 12: Наша миссия
Мы собираемся взять на себя проект, который откроет нам столько реальных тем из списка отображения, сколько я смогу втиснуть в одно руководство. Мы будем работать в основном с кодом, но мы также создадим плитку с изображениями в Flash IDE.
Фактическая часть будет представлять собой простую «стену» изображений, которая загружает изображения извне (с помощью Loaders), использует переопределение и замену глубины, а также сцену для события перемещения мыши. Это много, чтобы покрыть около 10 шагов, так что давайте начнем.
В загрузочном .zip-файле содержится начальный FLA-файл, который содержит иллюстрации и просто ждет, пока вы его закодируете. Но эти следующие шаги помогут разобраться в структуре художественного произведения, поэтому даже если вы пойдете по пути загрузки, прочитайте следующие шаги, чтобы лучше понять, что мы пытаемся сделать.
Шаг 13: настройте плитку изображения
Сначала создайте новый документ AS3 Flash. Сохраните его в папке, посвященной этому проекту.
Сделайте размер сцены 600 x 430. Добавьте фон, если хотите.
Теперь нарисуйте квадратное основание размером около 200 х 200 пикселей. Выберите его и нажмите F8, чтобы превратить его в мувиклип. Поместите пункт регистрации в центре.
Убедитесь, что отображаются параметры «Дополнительно», и установите флажок «Экспорт для ActionScript». Назовите класс «ImageTile» (я предпочитаю сопоставлять имя класса с именем символа, но это не обязательно). Кроме того, поскольку мы не будем использовать какие-либо временные шкалы, измените базовый класс с «flash.display.MovieClip» на «flash.display.Sprite». Нажмите OK и закройте диалоговое окно «определение для этого класса не найдено».
Вам не нужен экземпляр этого символа на сцене. Один удобный трюк — разместить его на сцене для удобного редактирования, но на собственном направляющем слое, чтобы он не экспортировался с SWF
Шаг 14: Добавьте подпись к плитке изображения
Войдите в режим редактирования символа ImageTile, если вы еще не там. Добавьте новый слой над слоем обложки. Добавьте динамическое текстовое поле в нижней части квадрата. Дайте ему следующие свойства:
- Имя экземпляра «caption_tf»
- Маленький размер шрифта
- Выровненный по центру
- Встраивание символов «Basic Latin»
- белый цвет
- Фильтр Glow с черным цветом и высокой размытостью
Шаг 15: Построй сетку плиток
Начните с подготовки вашего сценария. На основной временной шкале (обязательно выйдите из режима редактирования плитки), создайте новый слой, назовите его как-нибудь «код», заблокируйте его и нажмите на первый кадр. Затем нажмите F9, чтобы открыть панель «Действия».
Начнем с создания контейнера для плиток:
1
2
|
var wall:Sprite = new Sprite(); addChild(wall); |
Затем мы создадим массив URL-адресов изображений:
01
02
03
04
05
06
07
08
09
10
11
12
|
var images:Array = [ "mp3.jpg" , "tween.jpg" , "uploader.jpg" , "dice.png" , "clock.jpg" , "flickr.jpg" , "tagcloud.jpg" , "biggest.jpg" , "best.jpg" , "memory.png" ];
|
И, наконец, переберите этот массив и создайте новый ImageTile для каждого URL:
1
2
3
4
5
6
7
8
|
var iLen:uint = images.length; var tile:ImageTile; for ( var i:uint = 0; i < iLen; i++) { tile = new ImageTile(); wall.addChild(tile); tile.x = i % 5 * 210 + 110; tile.y = Math.floor(i / 5) * 210 + 110; }
|
Вот довольно стандартный цикл. Это будет выглядеть знакомо, если вы работали с AS3 101, часть 4, на массивах и AS3 101, часть 5, на циклахтак что я не буду вдаваться в подробности. Но вкратце, мы перебираем массив и создаем новый ImageTile для каждой итерации. Мы добавляем плитку к стене Sprite. И мы размещаем плитку с небольшой математикой, которая была рассмотрена в учебнике по массиву. По сути, мы можем взять номер индекса и получить индекс столбца, используя модуль, и индекс строки, разделив и округлив вниз. Умножьте каждый из них на некоторое количество проставок (в нашем случае — 210; наши плитки имеют площадь 200 пикселей, что дает нам промежуток в 10 пикселей), и вы получите макет сетки. Мы должны сместить x и y на 110 пикселей, тем не менее, потому что x / y относится к точке регистрации, которая находится в центре рисунка.
Шаг 16: установите заголовок
Мы сделаем вещи простыми (но менее практичными), просто сделав заголовок именем файла изображения. В цикле, который устанавливает плитки, мы получим ссылку на скрытый внутри TextField и установим его текстовое свойство. Добавьте это в конце цикла:
1
2
3
|
var imageName:String = images[i]; var caption:TextField = tile.getChildByName( "caption_tf" ) as TextField; caption.text = imageName; |
Во-первых, мы получаем значение из массива.
Затем следует тема нашего списка отображения: мы извлекаем дочерний элемент из контейнера, ссылаясь на его имя. Мы дали ему имя экземпляра «caption_tf», чтобы мы могли использовать это имя. Однако getChildByName возвращает DisplayObject, который может представлять собой любой тип фактического экранного объекта. Нам особенно нужен TextField, потому что если мы попытаемся установить свойство text для любого другого экранного объекта, ActionScript не очень понравится.
Поэтому мы делаем то, что вам приходится делать большую часть времени, когда вы используете getChildByName : мы должны привести его. Это просто позволяет ActionScript знать, что, хотя язык был определен так, чтобы возвращать getChildByName как один тип, мы знаем, что это будет другой тип, и позволяет нам обращаться с ним как таковым. В качестве ключевого слова взять операнд на левой и слепков его в качестве операнда справа. Итак, мы получаем ребенка по имени и проверяем, что это TextField. Обратите внимание, что ключевое слово as возвращает ноль, если объект, который будет приведен, не может быть приведен к указанному типу. Так что если бы getChildByName фактически возвратил объект Bitmap, у нас были бы проблемы.
Наконец, мы берем это TextField и помещаем imageName в свойство text.
Запустите фильм сейчас, и вы должны увидеть, что каждая плитка имеет уникальный заголовок.
Шаг 17: Загрузите изображения
Теперь мы можем загрузить изображения. Это займет немного настройки. Во-первых, нам понадобится словарь, чтобы отслеживать, какой загрузчик идет с каким ImageTile. Перед циклом создайте словарь:
1
|
var tileMap:Dictionary = new Dictionary(); |
Также перед циклом объявите другую переменную для повторного использования каждый раз, когда мы создаем Loader в цикле:
1
|
var loader:Loader;
|
А в цикле создайте Loader и загрузите изображение после всего остального в теле цикла:
1
2
3
4
|
loader = new Loader();
loader.load( new URLRequest( "images/" + imageName)); tileMap[loader] = tile; loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad); |
Обратите внимание, что я храню свои изображения в подпапке, называемой «images», и поэтому я добавляю имя, выходящее из массива, с относительным путем к этой папке.
Я также использую словарь для отслеживания Loader, который мы только что создали с помощью ImageTile, который мы также создали в этой итерации. Используя Loader в качестве ключа в словаре, ImageTile в качестве значения, мы можем легко найти соответствующий ImageTile, когда Loader завершает загрузку. Для обсуждения того, как это работает, смотрите мой учебник AS3 101 по ветвлению .
Прежде чем мы сможем проверить это, нам нужно написать эту функцию onImageLoad.
Шаг 18: Показать загруженные изображения
Вне цикла создайте новую функцию для обработки события ЗАВЕРШЕНИЯ загрузчика:
1
2
3
4
5
6
7
8
9
|
function onImageLoad(e:Event):void { var loader:Loader = e.target.loader; var tile:ImageTile = tileMap[loader] as ImageTile; var image:Bitmap = loader.content as Bitmap; image.x = -100; image.y = -100; tile.addChild(image); tileMap[loader] = null ; }
|
Вот что происходит: мы получаем цель события, которая является объектом contentLoaderInfo в Loader, и используем его свойство loader для получения Loader.
Затем мы используем этот Loader в качестве ключа для tileMap, чтобы вернуть ImageTile. Здесь мы снова приводим, потому что значения не типизированы и выходят из словарей (и массивов и объектов). Не строго необходимо, но приятно делать (см. Мое руководство по переменным для этого обсуждения).
Затем мы получаем содержимое загрузчика в виде растрового изображения. Это экранный объект, который был загружен, и который мы поместим в ImageTile. Нам нужно установить его x и y, потому что 0/0 находится в центре плитки.
Затем мы просим, чтобы ImageTile был родителем изображения.
На данный момент Loader представляет собой пустую оболочку, которую на самом деле нельзя использовать повторно, поэтому мы отказались от нее, установив значение Dictionary в null . Это удаляет ссылки на загрузчик и позволяет сборщику мусора уничтожать его.
Если вы запустите фильм сейчас, вы увидите, что у нас загружаются изображения! Но подписи сейчас скрываются …
Шаг 19: покажи Lodaed изображения в нужном месте
Что случилось с подписями? Хорошо, помните, как работают глубины, и как работает addChild . У нас уже есть два дочерних элемента: фон фигуры и текстовое поле. Форма должна быть на глубине 0, текстовое поле на глубине 1. Когда мы используем addChild, экранный объект get размещается в верхней части стека, поэтому изображение теперь находится на глубине 2, закрывая форму и текстовое поле.
Как мы можем решить это? Используя addChildAt , конечно. Это позволяет нам указать глубину, на которой нужно добавить объект. Все «в пути» будет просто скользить вверх в порядке расположения.
Простое решение состоит в том, чтобы изменить это:
1
|
tile.addChild(image); |
К этому:
1
|
tile.addChildAt(image, 1); |
Хотя это работает, это не идеально. Если мы когда-нибудь вернемся и перепроектируем вещи таким образом, чтобы была задействована другая форма, текстовое поле или изображение, у нас было бы больше элементов в стеке, и кто знает, будет ли 1 больше правильной глубины? Вместо этого, мы будем немного больше вовлечены и убедимся, что изображение всегда находится за текстовым полем. Сначала мы получим глубину текстового поля, затем вставим изображение на глубину, толкая текстовое поле вверх.
Попробуйте это вместо этого:
1
2
3
|
var textField:DisplayObject = tile.getChildByName( "caption_tf" ); var textFieldDepth:int = tile.getChildIndex(textField); tile.addChildAt(image, textFieldDepth); |
Обратите внимание, что на этот раз нас не волнует приведение TextField; мы просто используем эту переменную для передачи в getChildIndex, который будет работать с любым DisplayObject. Затем getChildIndex возвращает глубину этого потомка, которую мы затем используем в addChildAt. Более сложный, но теоретически более гибкий.
Шаг 20: переместить стену
Хорошо, мы продвинулись довольно далеко, но мы все еще упускаем кучу изображений. Здесь мы установим «автопрокрутку» стены. Чем дальше вправо вы перемещаете мышь, тем дальше влево перемещается стена. Вы, наверное, видели это раньше. Мы пойдем на простой свиток. Они могут стать более причудливыми, добавляя замедление движения и прочее, но наша цель здесь — добавить событие на сцену, так что мы пойдем с мясом и картошкой.
Так как мы двигаем стену, имеет смысл начать там. Однако, как упоминалось ранее, если мы слушаем событие MOUSE_MOVE, мы должны действительно слушать его со сцены, иначе будут области, где мы не получим событие. Итак, нам нужно добавить событие на сцену, и на самом деле не имеет значения, как мы доберемся до сцены, но поскольку стена — это Sprite, который заинтересован в событии, давайте использовать это. Эта строка может быть добавлена в любом месте за пределами цикла функции загрузки изображения. Можно и воткнуть в конце:
1
|
wall.stage.addEventListener(MouseEvent.MOUSE_MOVE, onWallMouseMove); |
Теперь давайте напишем функцию обработчика событий:
1
2
3
|
function onWallMouseMove(me:MouseEvent):void { wall.x = ((stage.stageWidth - 20) - wall.width) * (stage.mouseX / stage.stageWidth); }
|
Это много математики! Но вот что происходит: мы берем всю ширину сцены и вычитаем ширину стены. Поскольку стена шире сцены, мы получаем отрицательное число. Получается, что число — это значение x, которое должна быть у стены, чтобы выровнять ее правый край с правым краем сцены. Но мы вычитаем 20 из ширины сцены, потому что мы хотим немного маргина. Таким образом, число фактически помещает правый край стены на 20 пикселей левее правого края сцены.
Затем мы берем положение мыши по оси x и делим ее на ширину сцены. Это дает нам процентное значение, от 0 до 1, от того, где находится мышь, слева (0) и справа (1).
Умножая разницу в ширине на этот процент, мы получаем значение, которое помещает стену где-то между 0 и отрицательным максимумом.
В процессе мы использовали два ценных свойства сцены. Мы можем получить ширину (и высоту) сцены и положение мыши по отношению к сцене. Для многих типов интерактивности эти фрагменты информации являются ключевыми.
Шаг 21: выведите сфокусированные изображения на передний план
В качестве завершающего штриха давайте позволим каждому изображению увеличиваться в размере при нажатии. Это на самом деле немного связано. Это влечет за собой не только масштабирование кликаемого изображения, но и управление его глубиной, чтобы оно располагалось поверх других плиток.
Во-первых, давайте добавим событие клика. Где-нибудь в теле цикла добавьте это:
1
|
tile.addEventListener(MouseEvent.CLICK, onTileClick); |
Затем напишите обработчик:
1
2
3
4
|
function onTileClick(me:MouseEvent):void { var tile:ImageTile = me.<span class = "" style= 'font-family: "Helvetica", sans-serif; font-size: 12px;background-color:rgb(0,0,0) ; ' > </span>currentTarget as ImageTile; tile.scaleX = tile.scaleY = 1.3; }
|
Здесь мы снова пользуемся системой событий, чтобы сопоставить одну функцию нескольким событиям. Нам просто нужно получить цель мероприятия и оперировать им. Мы просто устанавливаем масштаб немного больше, чем обычно.
Проверьте это, и вы столкнетесь с двумя проблемами. Во-первых, изображения больше, но на самом деле они не на переднем крае. Они будут позади определенных плиток, в зависимости от того, как плитки были добавлены к стене. Давайте исправим это, управляя глубинами.
1
2
3
4
5
|
function onTileClick(me:MouseEvent):void { var tile:ImageTile = me.<span class = "" style= 'font-family: "Helvetica", sans-serif; font-size: 12px;background-color:rgb(0,0,0) ; ' > </span>currentTarget as ImageTile; tile.scaleX = tile.scaleY = 1.3; wall.setChildIndex(tile, wall.numChildren - 1); }
|
Это заставило стену сделать некоторые глубинные манипуляции со своими детьми Мы хотим, чтобы плитка находилась на вершине стека, и мы можем получить этот индекс, выяснив, сколько дочерних элементов в стене (wall.numChildren) и вычтя 1 (это похоже на то, как массив с длиной 10 имеет индексы 0-9 заняты). Использование setChildIndex перераспределяет порядок наложения так, чтобы указанная плитка была как вверху.
Другая проблема заключается в том, что изображения остаются большими после нажатия на другое изображение. Мы могли бы отслеживать переменную, чтобы сохранить текущее выбранное изображение, и использовать это значение, чтобы вернуть плитку обратно в нормальное состояние (см. AS3 101, Первая часть, как это сделать ). Но давайте потренируем мускулы нашего списка отображения и просто получим самый верхний экранный объект и уменьшим его.
1
2
3
4
5
6
7
|
function onTileClick(me:MouseEvent):void { var tile:ImageTile = me.currentTarget as ImageTile; tile.scaleX = tile.scaleY = 1.3; var topMost:DisplayObject = wall.getChildAt(wall.numChildren - 1); topMost.scaleX = topMost.scaleY = 1; wall.setChildIndex(tile, wall.numChildren - 1); }
|
Во-первых, мы получаем самый верхний элемент с помощью getChildAt, указывая самый высокий индекс, как мы это сделали с setChildIndex. Затем мы устанавливаем значения масштаба обратно на 1. Нам просто нужно убедиться, что мы добились того, чтобы элемент масштабировался до нормального, прежде чем мы установим новый элемент для увеличения.
Шаг 22: Сглаживание
Один небольшой совет: вы заметите, что масштабирование не очень хорошее для изображений. Есть очень простой способ решения проблемы: включить сглаживание растровых изображений. В onImageLoad:
1
|
image.smoothing = true ; |
Это оно!Растровые объекты имеют свойство сглаживания, которое по умолчанию имеет значение false. Включите его, как только он загрузится, и вы сможете поворачивать и масштабировать с большей точностью, чем могли бы в противном случае (сглаживание влечет за собой некоторые накладные расходы при рендеринге, но это жертва, которую вы можете выбрать, когда это уместно).
Шаг 23: Вывод
Теперь, когда вы знаете, что знаете, возможно, вы видите, как некоторые из наших предыдущих проектов AS3 101 могут быть упрощены с помощью программирования Списка отображения. Grid Game из AS3 101, часть 4 (на массивах), возможно, было бы намного проще построить, если бы мы сделали плитку доступной для ActionScript и использовали нашу итерацию, чтобы поместить их в список отображения. Поверьте мне, на самом деле мне было больно сдерживаться на эту тему. Но теперь вы готовы ко всей правде! Если вы хотите выполнить некоторые упражнения, вернитесь к нашим предыдущим учебникам серии AS3 101 и посмотрите, сможете ли вы улучшить данный код, чтобы использовать программные манипуляции со списком отображения. Большинство из них могут.
Еще лучше, надеюсь, вы увидите, как внедрение этих методов в ваши собственные проекты может упростить жизнь вам, разработчику ActionScript. Признайся, это то, что ты есть!
Далее в нашей серии будет работа с XML. Сочетание XML с массивами и циклами, а также с динамическими объектами отображения приводит к очень гибкому набору инструментов. Будьте на связи; большая отдача идет!