Если при разработке проекта нам доступен для редактирования таймлайн основного SWF, например, мы делаем проект во Flash IDE, всё просто - всё делаем как и раньше - а точнее - как нам советует в своей статье "
Экспорт классов во второй кадр и создание прелоадера во Flash CS3"
__etc.
А что делать, если наш проект компилируется Flex SDK?
Можно сделать маленький модулек, который будет загружать и мониторить загрузку основного SWF. Ну а если требуется наличие только одного SWF?
Люди советуют почитать
статью Preloaders in AS3. Читаем.
Основную мысль автору статьи, подсказал
Ted Patrick, намекнув на мета-тег [Frame] и поглядеть исходник mx.core.Application.
Поглядим-ка
"...\Flex2SDK\frameworks\source\mx\core\Application.as ".
Как отметил 101 (тот что автор статьи) там и правда есть тег
[Frame(factoryClass="mx.managers.SystemManager")]
. А в комментарии, по-моему, ключевой является фраза "
Все фреймворки инициализируются SystemManager-ом".
Идем смотреть
"...\Flex2SDK\frameworks\source\mx\managers\SystemManager.as".
И правда - видим интересные фразы типа
"ВНИМАНИЕ: Минимизируйте не флэшовые классы, которые импортируете здесь, всё что связано с SystemManager будет загружено в 1й фрейм до того, как загрузится прелоадер, и вообще, что-либо отображающееся." Кроме того, несколько ниже, говорится, что
"SystemManager - первый визуальный класс, который создается в приложении. Он так же отвечает за создание mx.preloaders.Preloader, который отображает mx.preloaders.DownloadProgressBar до конца загрузки приложения, после чего SystemManager создает mx.core.Application instance". Собственно, вызывается метод SystemManager.create, код которого приводит 101 (автор).
Он пришел к следующим мыслям:
- Сначала создаем основной класс MainClass как обычно и указываем компилятору чтобы он его компилил.
- Затем в этом классе вставляем тег [Frame(factoryClass="MyFactoryClass")], который указывает на некий другой класс MyFactoryClass.
Это должно привести к тому, что скомпилируется SWF с двумя фреймами! Всё барахло, которое мы имбедим, наш основной класс, другие классы которые он пользует - всё это будет импортироваться во второй фрейм. Единственное, что будет импортировано в первый фрейм - наш класс
MyFactoryClass и всё что он пользует. Шикарно!
Итак, нам остается всего-навсего создать класс обычного прелоадера, который:
- Делает stop();
- Мониторит загрузку любым удобным для нас способом.
- После полной загрузки делает nextFrame();
Теперь мы можем инстанцировать основной класс используя getDefinitionByName() {А откуда?}. Это дает нам возможность контролировать, какой класс мы хотим сделать основным. Кроме того, теперь мы можем выгрузить прелоадер.
Еще одна любопытная особенность этого решения - основной таймлайн представляет
MainClass, но в качестве "Document Class" теперь выступает не
MainClass, а
MyFactoryClass. Из этого следует:
- MyFactoryClass должен наследоваться от MovieClip. И не в коем случае не от Sprite.
- При инстанцировании MainClass, необходимо добавить (addChild) его в дисплей-лист MyFactoryClass.
- MainClass не будет являться корнем дисплей-листа. Он будет чайлдом MyFactoryClass.
- В своем конструкторе, MainClass не должен ссылаться на "stage", так как он будет доступен только после добавления (addChild) MainClass в дисплей-лист.
Теперь, всё, что осталось для нас неясным, развеем примером:
код примера.
В основном классе FrameTest - это то что выше называлось
MainClass - всё просто. Имбедится и выводится картинка "big_asset.jpg", желательно очень большого размера. Единственное необычное - это фраза
[Frame(factoryClass="MyFactory")]
.
Класс MyFactory - это наш вышеописываемый
MyFactoryClass.- В конструкторе производится остановка тайм-лайна stop(), выставляются параметры stage и добавляется обработчик "ENTER_FRAME".
- Обработчик onEnterFrame() рисует полосу состояния загрузки вычисляя процент загрузки:
var percent:Number = root.loaderInfo.bytesLoaded / root.loaderInfo.bytesTotal;
- При выполнении условия framesLoaded == totalFrames, объект отписывается от события "ENTER_FRAME", осуществляет переход на следующий фрейм nextFrame() и производит инициализацию init().
- Метод инициализации init() получает определение основного класса
var mainClass:Class = Class(getDefinitionByName("FrameTest"));
и, если такой класс существует if (mainClass)
, инстанцирует его и добавляет в дисплей-лист: var app:Object = new mainClass(); addChild(app as DisplayObject);
.
Всё. Переходим к практике.
* * *
Не забудем положить картинку "big_asset.jpg" в папку с классами.
Первая коррекция - getDefinitionByName вызывает исключение, в случае если класс не найден. Поэтому проверки if(mainClass)
недостаточно. Нужна обработка исключения try ... catch.
* * *
Пробуем trace(this.currentFrame);
. Он выдает правильно - 1 из конструктора MyFactory
, 2 - из init();.
Попробуем, как это будет работать в интернете. Хм. Прелоадер отрабатывает, но как-то поздно. Мой Naviscope показывает, что SWF загружается больше чем на половину, когда только начинает происходить отрисовка полосы загрузки. Попробую еще утяжелить SWF-ку. Для этого добавляю картинок.
Интересное наблюдение - если накопировать одну и ту же картинку и имбедить копии, размер SWF не изменится - как будто внедрена одна картинка! Вот это сжатие!
Итак, утяжелил до 20 мБ. Теперь всё встало на свои места. Прелоадер аккуратно отрабатывает объем и затем отображается картинка! Всё дело было в слишком быстром интернете.
Итак, подход прост и практичен. Спокойно делаем правильные и эффектные флэшки любого размера!