Импорт внешних фильмов и изображений

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

Уровни flash-плейера. Одновременное проигрывание нескольких фильмов
Наверное, вы уже обратили внимание, что при трассировке имени клипа вместо _root в его абсолютном адресе отображается достаточно загадочный идентификатор _level0. Аналогично трассируется и указывающее на основную временную диаграмму свойство:

trace(_root); // Выводит: _level0

Хотя _level0, в отличие от _root, не подсвечивается, использовать его для адресации на основную временную шкалу можно не менее эффективно. Например:

_level0.startDrag(); // Строчка делает основную временную диаграмму
// протаскиваемой

Возникает вопрос: чем _root отличается от _level0, а если ничем, то с какой целью в ActionScript были введены два свойства, указывающие на основную временную диаграмму? Чтобы дать ответ на поставленный вопрос, достаточно внимательно проанализировать имя соответствующего свойства: _level0 — это по-английски «уровень».


Если при трассировании свойства _root возвращается _level0, следовательно, основная диаграмма фильма находится на некоем нулевом уровне. Но что тогда располагается на остальных уровнях? Неужели другие основные временные диаграммы?

Каким бы смелым и неправдоподобным не показалось данное предположение, но оно является истинным. Еще во Flash 3 появилась возможность одновременного проигрывания в плейере нескольких swf-фильмов. Каждый фильм при этом располагается на особом, подобном виртуальным слоям клипов слое, называемом уровнем. На каждом уровне может размещаться только один фильм. Чем больше уровень, тем выше отображается расположенный на нем фильм. Адресуются уровни по общей схеме: _leveln, где n — номер нужного уровня. Отсчет уровней ведется с 0. Ограничений на высоту уровня не существует. При загрузке первого фильма он помещается на самый нижний уровень: _level0. По этой причине подгружаемые при помощи прописанных в нем скриптовых команд другие фильмы будут отображаться строго выше него.

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


Свойство _root всегда указывает на главную временную шкалу того фильма, в котором расположен соответствующий код (т. е. оно локально). Свойство _leveln позволяет обращаться к главной временной диаграмме нужного фильма из любого другого фильма, подгруженного в плейер (т. е. оно более универсально).

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

Если размер поля подгруженного фильма окажется большим, чем у фильма на _level0, то он попросту обрежется. Если же его поле будет меньшим, чем поле главного фильма, то этого нельзя будет заметить по причине того, что уровни прозрачны. Иными словами, если загрузить на _levell фильм таких же размеров, как и расположенный на _level0, то последний скрыт не будет. Просто элементы фильма с _levell окажутся отображенными выше объектов фильма, занимающего_level0.

Важно четко понять, что фон — это характеристика окна плейера, а не конкретного фильма.


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

Загрузка на _level0 нового фильма сопровождается удалением всех проигрывающихся в плейере фильмов. По этой причине использовать нулевой уровень нужно предельно осторожно.

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

Импорт внешних фильмов при помощи функций loadMovie() и loadMovieNum()
Основным инструментом, используемым для импорта внешних фильмов, является функция loadMovie(). Ее синтаксис:

loadMovie("url", target, [method]),
где:

• "url" — абсолютный или относительный адрес подлежащего загрузке swf-фильма. Должен быть задан в виде строки. Если вы хотите прописать адрес как относительный, то за точку отсчета нужно взять директорию, из которой поступил загруженный на _level0 фильм. В случае задания абсолютного адреса необходимо указывать протокол: http://, https:// или ftp://;

• target — уровень или клип, в который нужно подгрузить фильм.


На пустой уровень поместить фильм, используя loadMovie(), невозможно. Для этого нужно обратиться к функции loadMovieNum();

• [method] — необязательный параметр, определяющий HTTP-метод, с использованием которого будут отправляться переменные для импортируемого фильма. Может принимать значения «GET» или «POST». Если переменные отправляться не будут, то задавать параметр method не нужно.

Помимо функции loadMovie(), имеется и одноименный метод класса MovieClip. В общем, они абсолютно идентичны за тем исключением, что метод использует в качестве контейнера для импорта фильма клип, его вызвавший. Единообразия ради описывать тонкости работы с loadMovie() мы будем исключительно на примере глобальной функции.

Функция loadMovie() позволяет подгрузить swf-фильм в клип или заместить им уже проигрывающийся в плейере фильм. Чтобы понять, как она работает, создайте новый документ, «перетащите» на его рабочее поле кнопку из встроенной библиотеки, назовите ее but и наберите следующий код:

// При нажатия кнопки будет подгружен внешний фильм
but.onRelease=function():Void {
// Создаем клип-контейнер для импорта фильма
_root.createEmptyMovieClip("clip", 0);
loadMovie("2.swf", clip); // Импортируем фильм
}

Опубликуйте созданный фильм как 1.swf в произвольную директорию.


Основной фильм у нас уже есть. Теперь необходимо создать фильм, который будет подгружаться по нажатию кнопки but. Сделайте это, нарисовав в нем, например, квадрат, и сохраните затем его как 2.swf в той же директории, что и 1.swf.

Запустите проигрывание 1.swf и нажмите кнопку but. При этом содержимое фильма 2.swf отобразится (если, конечно, все было сделано верно) в плейере над объектами фильма 1.swf. Замечательно!

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

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

Имя клипа, в который был импортирован фильм, остается прежним, равно как сохраняют работоспособность и все ссылки на него.

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

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

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

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

Чтобы передать данные импортируемому loadMovie() фильму, необходимо выполнить действия, описанные ниже.

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

Передать информацию можно лишь в строковом формате. Если вы присвоите переменной объект данных типа, отличного от string, то система проведет преобразование к строковому типу.

• Далее нужно определить, посредством какого HTTP-метода будут отправлены переменные:

GET или POST. В случае метода GET переменные присоединяются к URL с адресом фильма.

Главным его недостатком является то, что длина URL чаще всего не может превышать 256 символов. В методе POST переменные помешаются в «тело» HTTP-запроса, При этом нет жестких ограничений на объем передаваемых данных. Однако использовать метод POST можно, лишь если фильм проигрывается в браузере. Если же его воспроизводит автономный плейер, то применен может быть только метод GET.

Определившись с HTTP-методом, его нужно указать в качестве третьего параметра функции loadMovie(). При этом все не защищенные от перечисления циклом for-in переменные той временной диаграммы, к которой относится вызов loadMovie(), будут объединены в одну строку, подвергнуты URL-кодированию и отправлены фильму. Если третий параметр loadMovie() не задан, то переменные пересылаться не будут.

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

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

Любому фильму присуща библиотека. Но что с ней происходит, когда фильм загружается в клип?

Можно выдвинуть два предположения. Или библиотека импортируемого фильма становится частью библиотеки фильма, к которому относится клип-контейнер, или же она остается доступной только ему. В первом случае неизбежны многочисленные проблемы, связанные с конфликтом имен символов и их идентификаторов для ActionScript-импорта. Скорее всего, реализуется второй вариант. Несложно проверить, что это действительно так. Однако тот факт, что импортированный в клип фильм сохраняет собственную библиотеку, имеет несколько тонких следствий. Так, на временную диаграмму клипа, в который был подгружен внешний фильм, можно программно «перетаскивать» экземпляры клипов из библиотеки этого фильма. Но попытка поместить туда, используя метод attachMovie(), экземпляр, соответствующий которому символ располагается в библиотеке основного фильма, завершится неудачей. Если же необходимо, чтобы элементы из библиотеки главного фильма могли использоваться подгруженным в расположенный в нем клип другим фильмом, нужно применить удаленный импорт символа.

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

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

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

Особенностью функции loadMovie() является то, что заранее неизвестно, когда завершится выполняемая ею задача (это зависит только от скорости соединения). По этой причине код, следующий за строчкой ее вызова, проделывается, не дожидаясь того момента, когда будет загружен нужный фильм. А это означает, что участок сценария, отвечающий за необходимые операции с новым фильмом, вы не можете прописать непосредственно после импортирующего предложения с loadMovie(). При этом все команды будут обращены в пустоту, так как загрузиться внешний фильм не успеет, даже если он хранится на данной машине. Соответствующий код должен быть проделан лишь тогда, когда закачка фильма завершится. Чтобы «отловить» этот момент, нужно воспользоваться событием onData и методами getBytesLoaded() и getBytesTotal() (или событием onLoad).

Если в качестве параметра target функции loadMovie() передается undefined, то она замешает фильм, в котором располагается строчка с ее вызовом новым фильмом. Часто подобный сбой возникает, если клип, который должен быть использован как контейнер, не существует. Особый случай связан с попыткой импортирования фильма на свободный уровень.

Функция loadMovie() не способна загружать фильм на уровень, если на нем нет другого фильма. Она может только замещать содержимое уже существующих в плейере клипов и фильмов, но не создавать их. Если необходимо поместить фильм на свободный уровень, нужно использовать функцию loadMovieNum(). Ее синтаксис:

loadMovieNum("url", level, [method]),

где level — порядковый номер уровня, на который должен быть помещен фильм (остальные параметры идентичны loadMovie()). Например:

loadMovieNum("l.swf", 1); // Фильм 1.swf помещается на _level1

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

Практическая значимость функций loadMovie() и loadMovieNum() огромна. Благодаря их наличию при помощи Flash можно создавать целые сайты. Почему этой возможности не существовало бы при их отсутствии? Подумайте, ведь если бы инструментов, позволяющих проводить динамическую подгрузку swf-фильмов, не было, то весь сайт должен был бы размешаться в одном ролике.

А это означало бы, что, вне зависимости от того, какие его разделы интересны посетителю, ему пришлось бы скачать его весь. Очевидно, что создать сайт, хранящий значительный объем информации, было бы при этом невозможно. Используя же loadMovie(), каждый раздел можно поместить в отдельный фильм и закачивать его лишь при необходимости. При этом, если не слишком увлекаться растровой графикой, градиентами и анимационными эффектами, можно создать flash-сайт, который будет «весить» ненамного больше своего HTML-аналога, Однако эффектность страницы, созданной при помощи Flash, будет на порядок выше (что важно в случае сайтовпрезентаций).

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

Наиболее просто можно импортировать изображения в формате jpg. Для этого нужно воспользоваться функцией loadMovie() или loadMovieNum(), прописав в качестве первого параметра путь к необходимому файлу. Например:

loadMovie("my_photo.jpg", clip);

Изображение помещается в клип таким образом, что его верхний левый угол совпадает с точкой отсчета координат. Глубина, которую оно занимает, соответствует минимально возможному в случае виртуальных слоев клипов значению -16383.

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

Сложнее обстоит дело с динамической загрузкой изображений в других форматах. Импортировать gif- или png-картинку, используя loadMovie(), невозможно. Это связано с тем, что flash-плейер умеет работать только с jpg-форматом. Картинки же других форматов, импорт которых возможен во fla-файл, просто переводятся в jpeg при публикации swf-фильма. Так как возможностью преобразовывать картинки из одного формата в другой плейер не обладает, все поступающие в него изображения должны быть предварительно сконвертированы в jpg-формат.

Если по каким-то причинам изображение не может быть преобразовано в jpeg, то его нужно сохранить в swf-фильме, динамически подгрузить который не составит никакого труда.

Импорт фильмов и изображений при помощи класса MovieClipLoader
Неплохие инструменты для осуществления импорта внешних фильмов имелись еще во Flash 5. Функция loadMovie() давала возможность проводить непосредственно загрузку фильма, методы getBytesLoaded() и getBytesTotal() позволяли определять, сколько процентов фильма уже закачалось, событие onData помогало реагировать на новую порцию поступивших данных (например, обновляя вид предзагрузчика), событие onLoad служило для определения момента окончания закачки фильма. В общем, этих инструментов для опытного разработчика было вполне достаточно, чтобы решить практически любую задачу, связанную с загрузкой внешнего фильма. Однако у новичков при использовании указанных инструментов возникали огромные сложности, особенно при создании предзагрузчиков. Причины этих сложностей следующие:

• Событие onData возникает лишь при полной загрузке кадра. Это не позволяло с его помощью создавать прелоадеры, в которых в анимационной форме отображался бы ход загрузки. Приходилось использовать событие onEnterFrame, считывая при каждом кадре количество скачанный байтов при помощи метода getBytesLoaded().

• Начиная со Flash MX обработчики событий принято создавать в форме методов. Однако, когда в клип загружается новый фильм, все его свойства уничтожаются. Следовательно, будут стерты и обработчики событий onLoad и onData, на основании которых можно было бы создать предзагрузчик.

Описанная проблема имела три решения. Во-первых, можно сделать обработчики событий onLoad и onData наследуемыми. Это неплохой вариант, если фильм подгружается один, но его довольно сложно применить, если сайт или сетевая игра образованы десятками импортируемых по мере необходимости компонентов. Во-вторых, можно создать для данных событий обработчики в стиле Flash 5 — такие обработчики не удаляются при замене содержимого клипа. Но это решение не идеально, так как у модели событий Flash 5 имеется огромное количество недостатков. В-третьих, можно поместить предзагрузчик непосредственно в скачиваемый фильм. Однако подобный внутренний прелоадер качественно работает далеко не всегда.

• Не имелось возможности динамически прекратить подгрузку фильма или изображения. Потребность же такая возникает очень часто — например, пользователь может перейти к новому разделу сайта, не дождавшись полной загрузки старого.

• Метод getBytesLoaded() по-разному работал на начальных этапах загрузки в различных браузерах, что осложняло создание качественных и универсальных предзагрузчиков.

• Не имелось поддержки серверных http-сообщений вроде 404 (указывает, что ресурс не был найден) или 403 (означает, что доступ к ресурсу заблокирован).

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

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

Сложности, которые были связаны с созданием предзагрузчиков, были, пожалуй, самым крупным недостатком ActionScript во Flash 6. Они создавали такое огромное количество проблем, что сообщество разработчиков в лице известного гуру Полина Мука (автор книги «ActionScript; подробное руководство», вышедшей на русском языке в издательстве «Символ+») обратилось к компании Macromedia с официальной петицией ввести в ActionScript более качественные инструменты i загрузки внешних фильмов и изображений. И просьба была услышана.

Во Flash MX 2004 инструменты загрузки фильмов были централизованы и большинство из приведенных выше их недостатков исправлены. Кроме того, появилось несколько новых событий, облегчающих импорт фильмов. Так возник новый класс MovieClipLoader.

Создаются объекты класса MovieClipLoader точно так же, как и любого другого: при помощи функции-конструктора и оператора new. Параметров конструктор MovieClipLoader не принимает:

var loader:MovieClipLoader=new MovieClipLoader(); // Готов новый загрузчик...

Основным методом класса MovieClipLoader является loadClip(url, target),
где:

• url — абсолютный или относительный адрес swf-фильма или изображения;
• target — клип, в который внешний элемент должен быть подгружен.

Метод loadClip() практически аналогичен функции loadMovie(), за исключением того, что он не позволяет отправлять импортируемому фильму переменные. А так как последнюю мы рассмотрели чрезвычайно подробно, то обсуждать особенности применения метода loadClip() не имеет смысла. Поэтому ограничимся лишь примером:

// Сохраните на рабочем столе изображение и назовите его picture.jpg.
// Создайте фильм и сохраните его там же. На первый кадр фильма "повесьте"
// следующий код:
this.createEmptyMovieClip["clip", 0);
var loader:MovieClipLoader = new MovieClipLoader();
loader.loadClip("picture.jpg", clip); // При тестировании изображение
// появится в фильме

Метод loadClip(), как и метод loadMovie(), не может подгружать клипы на несуществующие уровни — соответсвующий контейнер (клип или уровень) должен уже существовать. Чтобы выполнить эту операцию, необходимо обратиться к глобальной функции loadMovieNum(). Выгрузить содержимое клипа или уровня позволяет метод MovieClipLoader.unloadClip(), аналогичный методу unloadMovie() класса MovieClip.

А сейчас приведем лишь пример:
// Нарисуйте на первом кадре _root кружок, а затем наберите там же код;
var loader:MovieClipLoader = new MovieClipLoader();
loader.unloadClip(_root); // При тестировании кружок отображен не будет

Аналогом методов getBytesLoaded() и getBytesTotal() класса MovieClip является метод getProgress() класса MovieClipLoader, служащий для определения того, какая часть элемента уже загрузилась. В качестве параметра он принимает ссылку на клип, в который осуществляется загрузка. Результатом работы метода getProgress() является объект с двумя свойствами: bytesLoaded — объем загруженной информации в байтах, bytesTotal — размер импортируемого элемента в байтах.

На практике метод getProgress() используется, главным образом, при создании предзагрузчиков. Но иногда ему находится и более интересное применение. Например, следующий код начинает прокрутку фильма после того, как он загрузится на 75%:

this.createEmptyMovieClip("clip",0);
var loader:MovieClipLoader=new MovieClipLoader();
loader.loadClip("movie.swf",clip);
_root.onEnterFrame=function():Void {
var data:Object=loader.getProgress(clip);
if(data.bytesLoaded/data.bytesTotal)0.75) {
clip.play();
delete _root.onEnterFrame;
}
}

Вы можете спросить: если методы loadClip(), unloadClip() и getProgress() класса MovieClipLoader абсолютно аналогичны методам loadMovie(), unloadMovie(), getBytesLoadedO и getBytesTotalQ класса MovieClip, то какой смысл их применять? Действительно, никаких особых преимуществ у методов класса MovieClipLoader по сравнению с аналогичными методами класса MovieClip нет.

Однако у класса MovieClipLoader есть несколько событий, наличие которых, наряду с централизацией инструментов импорта фильмов, оправдывает его существование, Перечислим их:

• onLoadStart. Данное событие происходит, если методу loadClipO удается начать загрузку внешнего фильма или изображения. Функции-обработчику при этом в качестве параметра передается ссылка на клип, в который осуществляется импорт. Использовать событие onLoadStart стоит тогда, когда успешное начало загрузки должно сопровождаться изменениями в фильме (например, появлением предзагрузчика). Данное событие не имеет аналогов среди событий класса MovieClip, и эмулировать его невозможно (но ему практически соответствует первый вызов события onData).

• onLoadError. Происходит, если загрузка фильма или изображения не может быть осуществлена.

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

o URLNotFound — показывает, что адрес к объекту импорта указан неверно;
o LoadNeverCompleted — закачка была успешно начата, но внезапно произошел сбой (перестал отвечать сервер по причине перегрузки, оборвалась связь с Интернетом и т. д.).

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

Событие onLoadError не имеет аналогов среди событий класса MovieClip — и эмулировать его довольно сложно.

• onLoadInit. Происходит, когда выполняется код первого кадра подгруженного фильма (после осуществления инициализации фильма). Знать это событие чрезвычайно важно, так как после него можно обращаться к переменным и функциям фильма, давать команды временной диаграмме — в обшем, взаимодействовать с фильмом при помощи ActionScript. Например, если фильм не должен начать проигрываться тотчас же после загрузки первого кадра, нужно набрать:

listener.onLoadInit=function(target_clip:MovieClip): Void {
target_clip.stop();
}

В качестве параметра обработчику события onLoadInit передается ссылка на клип, в который осуществляется загрузка.

Событие onLoadInit не имеет аналогов среди событий класса MovieClip.

• on Load Progress. Пожалуй, самое важное для практики событие класса MovieClipLoader. Возникает при поступлении из сети новой порции данных и успешном их сохранении на диске с подгруженной ранее информацией. Функции-обработчику передается при этом три параметра:

o ссылка на клип, в который осуществляется импортирование внешнего элемента;

o количество загруженных байтов на момент возникновения события (они соответствуют сжатому фильму);

o размер импортируемого элемента в байтах (если это сжатый swf-фильм, то параметру будет соответствовать его истинный размер, а не размер разар-хивированного фильма).

Событие onLoadProgress незаменимо при создании предзагрузчиков. Прeдзагрузчик — это обычно небольшой клип, который прокручивается до тех пор, пока фильм (или нужная его часть) не будет загружена. Чаше всего предзагрузчики показывают, какая часть импортируемого элемента уже закачалась. Чтобы определить ее в процентах, достаточно второй параметр обработчика события onLoadProgress поделить на третий и умножить на 100. Обновлять же вид предзагрузчика нужно всякий раз, когда регистрируется поступление данных.

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

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

До определенной степени с событием onLoadProgress класса MovieClipLoader схоже событие onData класса MovieClip. Правда, последнему не передается в качестве параметров количество уже загруженных байтов и размер фильма в байтах, так что для определения этих величин приходится использовать методы getBytesLoaded() и getBytesTotal(). Главное же отличие onData от onLoadProgress заключается в том, что оно фиксирует загрузку покадрово. Событие же onLoadProgress срабатывает при получении очередной порции данных независимо от того, завершена загрузка кадра или нет. Естественно, что onLoadProgress куда эффективнее при создании прелоадеров, чем onData.

• onLoadComplete. Событие происходит по окончании загрузки элемента. В качестве параметра его обработчику передается ссылка на клип-контейнер. Использовать событие onLoadComplete нужно, если при завершении закачки клипа или изображения в фильме должны произойти изменения (например, предзагрузчик должен исчезнуть, а импортированный фильм — начать проигрываться).

Событию onLoadComplete класса MovieClipLoader соответствует событие onLoad класса Movie-Clip. По умолчанию события класса MovieClipLoader не имеют листенеров. Поэтому, чтобы их использовать, необходимо создать объект и зарегистрировать в качестве листенера при помощи метода addListener(). Удалить листенер, как вы помните, позволяет метод removeListener().

Приведенный ниже несложный пример поможет вам отработать навыки использования класса MovieClipLoader.

/* Создайте фильм 1.swf размером 50-100 килобайт (например, поместив в него несколько изображений). Расположите его на доступном вам удаленном сервере. Это необходимо сделать, так как событие onLoadProgress не работает в режиме тестирования. Затем создайте новый фильм и поместите на его первый кадр следующий код:

*/
this.createEmptyMovieClip("clip", 0); // Клип-контейнер для импорта фильма
var loader:MovieClipLoader = new MovieClipLoader(); // Создаем загрузчик
var listener:Object = (); // Создаем листенер для событий загрузчика
loader.addListener(listener);
// Загружаем внешний фильм, указав его абсолютный адрес
loader.loadClip("http://www.myserver.ru/mysite/flash/l.swf", clip);
// При успешном начале загрузки необходимо создать текстовое поле,
// в котором будет отображаться процент уже закачанной части фильма
listener.onLoadStart = function():Void {
_root.createTextField("preloader", -1, 250, 200, 50, 30);
preloader.аutoSize=true, preloader.border=true, preloader.text="0 %";
};
// При возникновении сбоя сообщение о его причинах должно появиться в
// специальном текстовом поле
listener.onLoadError = function(clip:MovieClip, message:String):Void {
_root.createTextField("error", 1, 250, 100, 50, 30);
error.autoSize=true, error.border=true;
if (message = "URLNotFound") {
error.text="Hеверно указан адрес - загрузка невозможна";
}
if (message == "LoadNeverCornpleted") {
error.text="npи загрузке возник сбой";
// При получении новой порции санных информация об уже скачанной части
// фильма должна обновляться
listener.onLoadProgress = function(clip:MovieClip, bytesLoaded:Number,
bytesTotal:Number):Void {
preloader.text = Math.round(bytesLoaded/bytesTotal*100)+" %";
};
listener.onLoadComplete = function():Void {
preloader.removeTextField();
};

Одновременно при помощи одного объекта класса MovieClipLoader может подгружаться несколько элементов. Причем их загрузка будет протекать взаимно независимо. Это означает, что листенеры данного объекта будут получать сообщения о событиях нескольких загрузок сразу. Чтобы можно было определить, от какого клипа-контейнера пришло сообщение, в качестве первого параметра обработчикам всех событий класса MovieClipLoader передается ссылка на него.

Чтобы код обработчиков не стал слишком громоздким, при помощи одного объекта класса MovieClipLoader нужно подгружать не больше двух-трех элементов. Динамически остановить загрузку в клип фильма можно, применив к соответствующему загрузчику метод unloadClip(). При этом возникнет событие onLoadError с параметром LoadNeverCornpleted.

Создание предзагрузчиков
Прелоадеры или предварительные загрузчики помогают решать две важные задачи:

• По умолчанию проигрывание swf-фильма начинается тотчас же после загрузки первого кадра.

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

• Демонстрация пользователю, какая часть фильма уже загружена. Наблюдая за постоянно удлиняющейся шкалой или значением, неуклонно приближающимся к 100 %, человеку психологически гораздо проще дождаться конца загрузки ролика, чем если бы в окне браузера было просто пустое белое поле.

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

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

Простейший внутренний прелоадер может представлять собой просто надпись «Идет загрузка» на первом кадре. Его код будет также очень примитивен:

this.onData = function():Void {
if (this.getBytesLoaded() = this . getBytesTotal()) {
this.play();
delete this.onData;
}
};
this.onEnterFrame = function():Void {
this.stop();
delete this.onEnterFrame;
};

Алгоритм, реализуемый приведенным кодом, довольно очевиден. При помощи события onData отслеживается момент окончания загрузки очередного кадра. Когда он наступает, проверяется, весь ли фильм закачался. Делается это при помощи методов getBytesLoaded() (возвращает число уже закачанных байтов) и getBytesTotal() (возвращает полный размер фильма в байтах). Если значения, возвращаемые данными методами, сравниваются, запускается проигрывание фильма. Чтобы прокрутка временной диаграммы не началась до окончания загрузки фильма, при инициализации первого кадра (этому моменту соответствует первый вызов события onEnterFrame) активируется метод stop() и удаляется затем ненужный уже обработчик.

Чтобы тестировать предзагрузчик, совсем необязательно выкладывать фильм на удаленный сервер. Можно воспользоваться особым режимом среды тестирования, в котором моделируется потоковая загрузка фильма. Чтобы его активизировать, нужно задействовать команду View ► Simulate Download (Вид ► Имитировать загрузку) главного меню среды тестирования (или просто повторно нажать + ). Скорость подгрузки данных определяется в меню, открываемом командой Download Settings (Установки загрузки) меню View. Однако нужно помнить, что в среде тестирования не работает событие onLoadProgress класса MovieClipLoader. И если оно используется в вашем проекте, отлаживать его необходимо непосредственно в сети.

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

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

/* Создайте клип в виде прямоугольника с отношением высоты к ширине приблизительно 1:10. Назовите его polosa и обведите рамкой. Точка отсчета координат клипа polosa должна располагаться на его левой границе (это необходимо, чтобы он удлинялся в одном направлении). Данный клип исполняет роль шкалы, на которой отобразится степень загрузки фильма. Справа от него поместите небольшое динамическое текстовое поле и назовите pole. В нем будет отображаться, сколько в процентах от полного объема swf-файла уже скачалось */

// Обновлять вид предзагрузчика необходимо по событию onEnterFrame (так как
// onData срабатывает только по окончании загрузки кадра - а аналога события
// onLoadProgress класса MovieCHpLoader среди событий класса MovieClip нет)
this.onEnterFrarae = function():Void {
// При первом вызове события onEnterFrame (его мы определяем по
// незаполненности текстового поля pole) останавливается прокрутка фильма
if (pole.text = "") {
this.stop();
}
// С точностью до десятых определяется процент загрузки фильма
var percent:Number = Math.floor(this.getBytesLoaded() /
this.getBytesTotal()*1000)/10;
// Выводится процент загрузки в поле pole
pole.text = percent+" %";
// Длина шкалы должна равняться тому проценту от ее максимальной длины,
// на величину которого загрузка уже осуществлена
polosa._xscale = percent;
// При завершении загрузки фильм должен начать проигрываться, а обработчик
// onEnterFrame должен быть удален
if (this.getBytesLoaded (1 == this.getBytesTotal()) {
this.play();
delete this.onEnterFrame;
}
};

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

Конечно, качественно прогнозировать время загрузки фильма можно лишь в том случае, если она происходит достаточно равномерно. Но даже если это условие не соблюдается, то грубый прогноз — это лучше его полного отсутствия. Для примера приведем код, который нужно добавить в созданный выше скрипт, чтобы предзагрузчик отображал время, которое осталось ждать:

/* Создайте динамическое текстовое поле и назовите его time. В нем будет отображаться
прогнозируемое время (в минутах), которое осталось до конца загрузки */
// Функция getTimer() возвращает время в миллисекундах, прошедшее с начала
// проигрывания фильма
var prognoz:Number = ((100-percent)*getTimer()/percent)/60000;
time.text = Math.round(prognoz*10)/10;

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

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

Главным недостатком внутреннего предзагрузчика является то, что он представляет собой часть загружаемого фильма, располагаясь на его первом кадре (или первых кадрах). Результатом этого является ряд весьма неприятных следствий:

Загрузчик появится лишь тогда, когда будет закачан первый кадр. Вы можете возразить, что данный кадр можно сделать очень легким — и тогда он появится почти моментально. Увы, не все так просто. Перед первым кадром загружаются элементы, не связанные с каким-то конкретным кадром. Это клипы и звуки, экземпляры которых создаются программно, встроенные шрифты, внешние классы ActionScript 2.0, служебные данные. Зачастую объем загружаемой до первого кадра информации может составлять значительную часть объема фильма. В этом случае может пройти много времени до появления предзагрузчика, что сделает его наличие в принципе бессмысленным. Да и если первым отображенным в нем значением будет 78 %, то такой прелоадер вряд ли можно назвать хорошим.

• Внутренний прелоадер довольно проблематично создать, используя новый класс MovieClipLoader. А так как данный класс предоставляет чрезвычайно полезные и не имеющие аналогов инструменты, позволяющие следить за ходом загрузки, то невозможность его применения сказывается на качестве пред за грузчиков.

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

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

Имеется ли способ преодолеть перечисленные недостатки внутреннего предзагрузчика? Полностью — нет, отчасти — вполне. Основной причиной этих недостатков является то, что значительная часть объема фильма скачивается до появления первого кадра. Следовательно, необходимо уменьшить количество элементов, загружаемых по умолчанию до первого кадра. Для этого нужно убрать флажок из окошка настройки Export in first frame (Экспортировать в первый кадр) меню Linkage (Связывание) панели Linkage Properties (Свойства связывания) (она открывается командой Linkage контекстного меню строки символа в библиотеке). При этом клипы и звуки, экземпляры которых создаются программно (а также встроенные шрифты, изображения, видео), будут импортированы лишь по окончании загрузки последнего кадра. Внешние классы также могут быть подгружены на любой кадр благодаря наличию настройки Export frame for classes (Кадр экспорта для классов) окна ActionScript Settings (Установки ActionScript), открываемого кнопкой Settings закладки Flash панели Publish Settings. Однако часть данных (контуры символов статичного текста, служебная информация) подгружается исключительно перед первым кадром, так что у оптимизации фильма под предзагрузчик имеется предел.

Описанный способ оптимизации хорош, если фильм начинает проигрываться по окончании загрузки. Но на практике это не всегда достижимо. Многие эксперты в области Flash-usability считают, в частности, что элементы сайта должны появляться по мере его загрузки (как это происходит в случае его HTML-аналога). Это, во-первых, делает ожидание окончания закачки менее тягостным, а во-вторых, дает возможность пользователю проводить часть операций, не дожидаясь полной загрузки фильма. В общем, добиться того, чтобы элементы фильма закачивались в некоторой нужной очередности, совсем несложно. Для этого достаточно просто распределить их по кадрам в необходимой последовательности так, чтобы каждый следующий кадр содержал те же элементы, что и предыдущие, плюс некоторые другие. По завершении загрузки кадра (о нем говорит событие onData) на него нужно переводить проигрывающую головку. Вот и все. Однако если мы начнем проигрывать фильм до его полной загрузки, то часть элементов, создаваемых при помощи команд кода, в принципе не появится, так как соответствующие объекты попросту еще не будут скачаны. Можно, конечно, создать экземпляры соответствующих символов за границей фильма в нужных кадрах — тогда они будут закачаны к моменту инициализации обращающегося к ним кода. Но этот ход слишком искусственен, чтобы считать его удачным решением проблемы.

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

Для примера приведем код простого внешнего предзагрузчика, предназначенного для импорта в фильм небольшой анимации. Изучите его, а затем воссоздайте самостоятельно:

/* Создайте клип в виде прямоугольника тех же размеров, что и импортируемая анимация. Назовите его clip - он будет использоваться в качестве контейнера. Поместите его на подложку того цвета, какой в анимации применяется в качестве фона. Далее необходимо изготовить "тело" предзагрузчика. Для этого нарисуйте полосу, переведите ее в клип (точка отсчета координат должна располагаться на ее левой границе) и назовите polosa. Обведите клип рамкой. Справа от него создайте два небольших динамических текстовых поля и назовите их рrос и time — в них будет выводиться процент уже осуществленной загрузки и, соответственно, прогнозируемое время в минутах, оставшееся до конца загрузки. Объедините полосу и текстовые поля в один клип и назовите его preloader.

*/
// До успешного начала загрузки прелоадер должен быть невидимым
preloader._visible = false;
// Создаем загрузчик и листенер для него
var loader:MovieClipLoader = new MovieClipLoader();
var listener:Object = {};
loader.addListener(listener);
var timer:Number = null; // Переменная для хранения времени начала загрузки
// Начинаем закачку анимации (расположите произвольный swf-файл на любом
// доступном вам удаленном сервере и укажите абсолютный путь к нему)
loader.loadClip("http://www.myserver.com/flash/anim.swf", clip);
// Если плейеру удастся начать загрузку, делаем предэагрузчик видимым,
// выводим в его поля значения по умолчанию, а также фиксируем момент старта
// закачки.
listener.onLoadStart = function():Void {
preloader._visible = true;
preloader.proc.text="0 %", preloader.time.text="?";
timer = getTimer();
};
// При возникновении сбоя создаем текстовое поле и выводим в него об этом
// сообщение
listener.onLoadError = function():Void {
clip.createTextField("error", 0, 50, 50, 0, 0);
clip-error.autoSize = clip.error.border=true;
clip.error.text = "Анимация не может быть загружена";
};
// По завершении загрузки первого кадра нужно сделать клип с анимацией
// невидимым, а также остановить его дальнейшую прокрутку
listener.onLoadInit = function{):Void {
clip._visible = false;
clip.stop();
}
// При загрузке очередной порции данных меняем длину шкалы прелоадера,
// а также обновляем значения в полях рrос и time
listener.onLoadProgress = function(clip:MovieClip, l_b:Number, t_b:Number):
Void {
var percent:Number = l_b/t_b;
preloader.proc.text = Math.floor(percent*1000)/10;
preloader.polosa._xscale = percent*100;
preloader.time.text = Math.round((1-percent)*(getTimerО-timer) / percent
/ 6000) /10;
};
// При завершении загрузки удаляем предзагрузчик и ненужные более объекты.
// Затем делаем клип clip видимым и начинаем его прокрутку
listener.onLoadComplete = function():Void {
preloader.swapDepths(1000), preloader.removeMovieClip();
delete loader, delete listener, delete timer;
clip._visible=true, clip.play();
};

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

Делая вывод, можно сказать, что внешние предзагрузчики гораздо лучше внутренних. Кстати, среди встроенных компонентов Flash MX 2004 имеются простейший внешний предзагрузчик и шкала загрузки — Loader и ProgressBar. Особенности их применения мы обсудим в главе, посвященной компонентам.

Заканчивая разговор о создании предзагрузчиков, упомянем еще о двух инструментах, предназначенных для реализации прелоадеров. Это глобальные свойства _framesloaded (количество загруженных кадров) и _totalframes (полное число кадров в фильме). Соответственно, чтобы определить, какая часть фильма уже подгрузилась, нужно поделить величину _framesloaded на значение _totalframes.

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

Скорее, данные свойства имеют историческое значение (во Flash 5 они применялись довольно активно, так как тогда метод getBytesLoaded() возвращал вес загруженных кадров, а не число реально подкачанных байтов). Правда, _totalframes иногда применяется в коде, управляющем навигацией по временной шкале.

Кэширование swf-фильмов
Широко использующимся в сети способом повышения производительности является сохранение на жестком диске пользователя загруженных ранее страниц па случай их повторного запроса. Этот метод называется кэшированием, и его поддерживают все современные браузеры. Кэшируются все элементы, встроенные в страницу, в том числе и swf-фильмы. Более того, кэшированию подвергаются даже фильмы, закачанные автономным плейером или плейером среды тестирования Flash, а также фильмы, загруженные посредством функции loadMovie(). В этом можно убедиться двумя способами. Во-первых, можно обратиться к директории, я которой хранятся кэшированные файлы (в русифицированной Windows их принято называть временными файлами Интернета — Temporary Internet Files). В Windows 2000 путь к этой директории имеет приблизительно следующий вид: C:\Documenls and Settings\user\Local Settings\Temporary Internet Files. Во-вторых, можно сравнить время, которое требуется на то, чтобы была проведена первая и последующие загрузки фильма. Повторный импорт фильма потребует несопоставимо меньше времени.

То, что swf-фильмы кэшируются, очень хорошо. Это означает, что если в вашем проекте один элемент загружается несколько раз, то существенного времени потребует лишь его первая закачка.

Повторный же импорт будет осуществлен почти моментально.

Однако у кэширования есть и негативная сторона, связанная с отладкой. Например, представьте, что вы создаете предзагрузчик. Чтобы его тестировать, вы закачиваете некоторый swf-файл на сервер. Запустив фильм в первый раз, вы обнаруживаете ошибки и исправляете их. Однако проверить, корректно ли стал работать предзагрузчик, у вас не получится, так как реальная загрузка осуществляться не будет. Плейер просто возьмет копию фильма из папки Temporary Internet Files.

Как же в такой ситуации провести отладку? Можно, конечно, менять имя фильма на сервере, но этот способ неэффективен. Более технично использовать один из следующих подходов;

• удалять копию фильма из папки Temporary Internet Files. Подход хороший, если загружается немного фильмов;

• воспользоваться тем, что URL может содержать придаток сданными (метод GET), причем один URL, но с разными данными воспринимается как разные ссылки. Следовательно, присоединяя к адресующему фильм URL случайное число, можно заставить плейер осуществлять загрузку из сети даже тогда, когда в кэше имеется копия фильма. Важное условие успешности описанного подхода следующее: ссылка на swf-файл должна быть абсолютной, а не относительной. Например:

this.createEmptyMovieClip("clip", 0);
clip.loadMovie("http://www.mysite.ru/film.swf?"+Math.random());

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

Бороться с кэшированием фильмов, встроенных в HTML-страницу, также несложно. Для этого можно к URL, адресующему фильм, присоединять случайное число (посредством JavaScript).

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

<НЕАD>
<МЕТА HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">

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

Ее значение можно прочитать, используя метод getDepth(), и переопределить при помощи метода swapDepths():

trace(_root.getDepth()); // Выводит: -16384
_root.swapDepths(1000); // Пробуем изменить глубину _root
trace(_root.getDepth()); // Выводит: 1000 (глубина была успешно изменена)
trace(_root); // Выводит: _level0 (изменение глубины не
// повлекло изменения уровня)

Проанализировав приведенный код, можно сделать два важных вывода:

• Основная временная диаграмма загруженного первым фильма располагается на глубине, соответствующей максимально возможному ее значению в случае клипов. Как вы помните, у них глубина -16384 зарезервирована и никогда не занимается элементами внутренней структуры клипа. Самый нижний из объектов размещается на глубине -16383.

• Уровень, занимаемый фильмом, и глубина его основной временной диаграммы прямо никак не связаны.

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

trace(_root.getDepth());

Затем опубликуем новый фильм на Рабочий Стол, назвав 1 .swf.

Далее создадим еще один документ и на его первом кадре наберем:

loadMovieNum("1.swf", 1);

Чтобы при тестировании данного фильма файл 1.swf попал в его область видимости, сохраняем соответствующий ему fla-файл на Рабочем Столе. Нажимаем + . В панели Output при этом появляется -16383. Пробуем заменить уровень, на который подгружается 1.swf, на 100-й.

При этом в Output выводится число -16284.

Итак, оказывается, что уровень и глубина взаимозависимы. Более того, можно даже вывести однозначную формулу, связывающую глубину основной временной диаграммы с соответствующим ей уровнем: depths=-16384+level. Но почему тогда при изменении глубины не модифицируется уровень, по которому адресуется фильм (см. пример выше)?

А ответить на этот вопрос очень просто, предположив, что реально никаких уровней не существует. Имеются глубины, на которых действительно располагаются фильмы. Эти глубины полностью аналогичны виртуальным слоям обычных клипов. Их можно прочитать и даже изменить. Уровни же — это лишь удобная форма для именования проигрываемых в плейере фильмов и задания параметров для функций и методов семейства loadMovie (действительно, если бы отсчет велся от - 16384, работать с ними было бы гораздо сложнее). Итак _lеvel0 — это имя загруженного первым фильма, а не указатель на глубину, на которой он находится. Фильм с таким именем может располагаться и на миллионной глубине, отображаясь выше всех остальных фильмов.

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

but.onRelease=function():Void {
_root.swapDepths(1000);
}

Тут же расположим код, подгружающий второй фильм в первый:

loadMovieNum("2.swf", 1);

Сохраняем фильмы n одной директории как 1.swf и 2.swf. Далее запускаем проигрывание файла 1.swf. При этом в плейере содержимое второго фильма отобразится выше содержимого первого.

После нажатия кнопки but объекты первого фильма окажутся выше объектов второго. Таким образом, используя метод swapDepths(), можно менять порядок отображения фильмов в плейере точно так же, как и вложенных клипов. А это означает, что клипы являются по своей сути как бы маленькими swf-фильмами (точнее, что нет принципиальных отличий между объектами, которые мы называем клипами, и структурами, стоящими за термином «фильм»). В качестве дополнительного доказательства такого утверждения можно привести тот факт, что при помощи loadMovie() любой swf-файл может быть подгружен в клип.

Кстати, можно обменять два фильма глубинами, используя их имена. Для этого нужно использовать второй вариант синтаксиса метода swapDepths():

_level0.awapDepths(_level1);

Функции и методы семейства IoadMovie() не учитывают глубин фильмов. Поэтому запись loadMovieNum(«1.swf»,1) не всегда означает, что фильм будет помещен на глубину -16383. Если в плейере уже имелся фильм с именем _level1, то новый фильм просто заместит его. А располагаться он при этом может на любой глубине.

Доступ из одного фильма к объектам другого
Обратиться к одному из элементов внутренней структуры фильма из другого фильма ничуть не сложнее, чем прочитать из одного клипа свойство другого. Для этого достаточно указать уровень, на котором располагается фильм, и «адрес» нужного элемента. Например;

_level1.mov.start Drag(); // Клип mov фильма level1 становится протаскиваемым

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

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

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

Так как уровни — это просто имена подгруженных в плейер фильмов, то создать переменную на незаполненном уровне невозможно. Например:

_level1.prop = "Привет";
trace(_level1.prop); // Выводит: undefined

Все, что мы говорили выше о доступе одного из проигрываемых в плейере фильмов к элементам другого, по умолчанию справедливо лишь в том случае, если оба фильма были получены с одного домена или же соответствующий фильму, к которому происходит обращение, swf-файл хранится локально. Если же фильмы были закачаны с разных доменов, то начинают работать ограничения, связанные с политикой сетевой безопасности Flash MX 2004.

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

Чтобы атаки, вроде описанной выше, были невозможны, по умолчанию плейер запрещает фильму с домена А считывать значения переменных, вызывать функции или задавать новые переменные в коде фильма с домена В. Причем неважно, где располагается фильм с домена В: на уровне плейера или в клипе фильма с домена А. Чтобы фильмы с разных доменов могли взаимодействовать, фильм, к которому происходит обращение, должен знать, какие домены являются дружественными. Для этого в его коде должен быть вызов метода System.security.allowDomain(), в качестве параметров которому переданы строки с именами допустимых доменов (или их IP-адресами). Например:

System.security.allowDomain("www.mysite.ru", "www.macromedia.com");

Для импорта фильма можно использовать как протокол HTTP, так и HTTPS. HTTPS — это HTTP в комплексе с системой безопасности SSL, обеспечивающей секретность передаваемых данных. Если фильм был получен с применением HTTPS, то к нему не сможет обратиться фильм, закачанный по HTTP, даже если они оба относятся к одному домену. Снять это ограничение позволяет метод System.security.allowInsecureDomain(), принимающий в качестве параметров имена или IP доменов, фильмы с которых являются дружественными:

System.security.allowInsecureDomain("www.mysite.ru");

Если подгружаемый фильм поступает с иного домена или по иному протоколу, чем основной фильм, то по умолчанию они будут практически полностью изолированы друг от друга. Для этого для них не только будет запрещен доступ к переменным и функциям друг друга, но и для каждого фильма будет создан индивидуальный объект Global. Это означает, что глобальные переменные, классы или новые методы и свойства существующих классов, создаваемые одним фильмом, будут недоступны для другого фильма. Чтобы фильмы использовали один объект Global, нужно снять ограничения политики сетевой безопасности при помощи метода allowDomain() или allowInsecureDomain() объекта System.security.

Политика сетевой безопасности Flash MX 2004 — сложный и запуганный вопрос ActionScript-программирования. Чтобы хорошо в нем разобраться, недостаточно прочитать приведенное выше описание.

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

Справиться с описанным недостатком позволяют так называемые обобщенные библиотеки (shared libraries). Они представляют собой обычные swf-файлы, исполняющие роль хранилищ данных.

Любой другой swf-фильм может импортировать из удаленной библиотеки нужный ему символ, который будет помешен в его собственную библиотеку. Это позволяет централизованно хранить объекты, используемые одновременно несколькими фильмами проекта. Обобщенные библиотеки особенно полезны в случае таких объектов, как встроенные шрифты, которые нельзя импортировать в форме внешних файлов, в отличие от фильмов, текста, МР3-звуков и видео.

Чтобы создать обобщенную библиотеку, нужно выполнить последовательность действий.

• Открываем новый fla-документ и создаем в нем все элементы, которые должна хранить обобщенная библиотека.

• Каждый элемент следует связать с идент

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Action Script на эту тему:
Виртуальные слои клипов
Задание формулы цвета
Имена экземпляров клипов
Клипы как носители кода
Коллизии клипов. Метод hitTest()

Вернуться в раздел: Action Script / 10. Клипы