Внимание!
Для работы с этой лекцией необходимы учебные
файлы, которые Вы можете загрузить
здесь.
В этой
лекции мы рассмотрим еще один интерфейс просмотра
изображений. Мы научимся отображать динамическую
загрузку изображений, а также сохранять список
изображений отдельно от фильма Flash, чтобы изменять
сайт без повторного открытия FLA.
Для
динамической загрузки рисунков потребуется создать
предзагрузчик. Мы разберемся в том, как обрабатывать
рисунки различных размеров, как расширить прототип
slideTo, созданный
ранее, чтобы изменять размер области просмотра. Мы
также научимся применять объект
Stage для управления
интерфейсом и использовать общие объекты так, чтобы
интерфейс мог хранить имена файлов рисунков для
загрузки за определенный промежуток времени, а затем
создадим интерфейс просмотра фотографий.
Объект Stage
Объект
Stage является одним из наиболее мощных дополнений
во Flash MX. Он позволяет нам получать доступ к
пространству вокруг рабочего места, превращать его в
окно браузера или самостоятельного проигрывателя,
устанавливать его разрешение и использовать его
пространство. Мы можем присваивать события для
реакции на любое изменение в доступном месте и
соответствующим образом переорганизовывать элементы.
Для использования объекта
Stage необходимо настроить HTML определенным
образом. Как правило, при публикации фильма есть две
возможности.
Согласно
первой из них, фильм Flash отображается в реальном
размере, и размер определяется разрешением фильма,
образуя четкую границу на странице HTML. Для
установки этого режима откройте диалоговое окно
Publish Settings (File > Publish Settings) и
выберите вкладку HTML. Установите в ниспадающем меню
Dimensions значение Match Movie и в меню Scale
значение Default (Show All).
Вторая
возможность - растянуть фильм Flash так, чтобы он
заполнял все доступное место. Для этого установите
Dimensions на 100%, оставив для режима Scale
установки по умолчанию.
Изменение размера
рабочего места с помощью объекта Stage
Flash
MX имеет еще одну возможность настройки параметров
публикации Publishing Settings, которая разрешает
пользователю настраивать размер браузера или окна
проигрывателя Flash, причем объекты на рабочем месте
будут также изменять свои размеры. Разберем, как
реализовать эту возможность.
- Откройте
новый фильм Flash с разрешением по умолчанию
(550x400) и в диалоговом окне Publish Settings
установите Dimensions на 100% и режим
Scale на значение
No Scale.
- Теперь
нарисуйте что-нибудь вне рабочего места, т.е. в
рабочей области.
Открыв фильм в браузере или во Flash Player, вы
увидите, что можно просматривать не только
область на рабочем месте, но и определенную
часть содержимого рабочей области, в зависимости
от размеров окна вашего браузера.
Так
настраивается HTML, чтобы использовать объект
Stage.
Теперь посмотрим, как можно использовать это
дополнительное пространство. Объект
Stage довольно
примитивен с точки зрения доступных в нем
параметров и методов. Мы имеем доступ к ширине и
высоте рабочего места, а также можем присваивать
"приемники" для получения уведомлений об
изменении размера рабочего места. Очень важно
понимать, что Stage.Width и Stage.Height не
ссылаются на разрешение, установленное нами для
фильма, а связаны с доступным нам актуальным
размером: размером отдельного окна Player или
размером окна браузера. Сама по себе эта
информация бесполезна, если не знать, какого
размера было наше рабочее место.
- Чтобы узнать
эти данные, нужно временно переключить режим
размера рабочего места на значение
showAll. В этом
режиме разрешение рабочего места будет таким,
какое указано в диалоговом окне Properties.
Удалите все с рабочего места фильма, который мы
только что создали, и введите следующий код в
новый слой scripts.
fscommand("allowscale", "false");
// switch to showAll mode
Stage.scaleMode = "showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
// switch back to noScale
Stage.scaleMode = "noScale"
Мы
только что добавили два дополнительных параметра
в объект Stage:
originalWidth и
originalHeight. Мы также добавили команду
allowscale = false,
чтобы фильм не растягивался в режиме
предварительного просмотра.
Когда вокруг фильма имеется дополнительное место,
координаты левого верхнего угла больше не равны
(0,0). Теперь исходная область (рабочее место)
нашего фильма сохраняет начальные координаты и
находится в середине кадра, а координаты
дополнительного места вокруг него связаны с
ними. На следующем рисунке это показано
графически.
Серая область является рабочим местом размером
550x400 пикселей, с соответствующими
координатами каждого из углов. Больший
прямоугольник вокруг границы этой области
представляет собой окно, в котором находится
фильм, имеющее в данный момент размер 750x500
пикселей. Это на 200 пикселей шире нашего
исходного фильма, и на 100 пикселей выше.
Дополнительные пиксели добавляются равномерно на
каждой стороне рабочего места, поэтому левый
край видимой области находится на -100 (0-100) ,
правый край на 650 (550+100), верхний край на
-50 (0-50) и нижний край на 450 (400+50).
Именно эти границы важны, если мы собираемся
располагать фильмы относительно них, поэтому мы
сейчас напишем несколько новых методов объекта
Stage, чтобы возвращать эти значения. Прежде
всего, разберемся, как будут проходить
вычисления значений.
- Левый
край: 0 минус половина Stage.Width минус
исходная ширина.
- Верхний
край: 0 минус половина Stage.Height минус
исходная высота.
- Правый
край: исходная ширина плюс половина
Stage.Width минус исходная ширина.
- Нижний
край: исходная высота плюс половина
Stage.Height минус исходная высота.
- Теперь
реализуем это в виде ActionScript.
//define methods to retrieve boundaries
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)/2;
};
Stage.getTop = function() {
return -1*(this.height-this.originalHeight)/2;
};
Stage.getRight = function() {
return
this.originalWidth+(this.width-this.originalWidth)/2;
};
Stage.getBottom = function() {
return
this.originalHeight+(this.height-this.originalHeight)/2;
};
- Создадим
несколько фильмов для использования этих
значений. Будем использовать четыре скобки в
качестве углов изображения для каждого из углов
рабочего места. Мы назовем их
tl,
tr,
br и
bl, и рисунки
каждого из них будут размером 50х50 пикселей с
точкой закрепления в левом верхнем углу. Фильмы
скобок доступны в исходных файлах на
компакт-диске.
- Перетащите
четыре объекта на рабочее место, назовите
инстансы именами tl,
tr,
br и
bl с помощью
Property inspector, и расположите каждый из них
в соответствующих углах рабочего места. Мы также
ввели некоторый текст в центре рабочего места
для обозначения середины фильма. Ваше рабочее
место теперь будет выглядеть примерно так.
Чтобы перемещать эти фильмы на свои места при
изменении размеров рабочего места, нужно создать
для них приемники. Объект
Stage имеет список объектов и/или фильмов,
которые зарегистрированы вместе с ним для
получения уведомления о том, что размер рабочего
места изменен. Каждый раз при изменении рабочего
места объект Stage
будет вызывать событие onResize во всех
объектах, которые с ним зарегистрированы.
Сначала нужно зарегистрировать наши фильмы с
объектом Stage и
затем создать для каждого из них метод onResize
для расположения их на нужных местах.
- Для
добавления приемников вызовем метод
addListener
объекта Stage и
передадим ему имена фильмов в качестве
параметров. Добавьте следующий код в имеющийся
ActionScript.
//
register four movie clips to receive
notification of onResize
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
- Далее нужно
создать методы onResize.
Учтите, что точки регистрации (закрепления)
наших фильмов могут находиться не на той
стороне, поэтому правый нижний фильм, например,
нам нужно расположить по правому краю минус его
ширина и по нижнему краю минус его высота.
//
top left
tl.onResize = function() {
this._x = Stage.getLeft();
this._y = Stage.getTop();
};
// top right
tr.onResize = function() {
this._x =
Stage.getRight()-this._width;
this._y = Stage.getTop();
};
// bottom left
bl.onResize = f unction() {
this._x = Stage.getLeft();
this._y =
Stage.getBottom()-this._height;
};
// bottom right
br.onResize = function() {
this._x =
Stage.getRight()-this._width;
this._y =
Stage.getBottom()-this._height;
};
Итак, получаем следующий код.
fscommand("allowscale", "false") ;
Stage. scaleMode="showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
Stage.scaleMode="noScale"
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)/2;
};
Stage.getTop = function() {
return -1*(this.height-this.originalHeight)/2;
};
Stage. getRight = function() {
return
this.originalWidth+(this.width-this.originalWidth)/2;
};
Stage.getBottom = function() {
return
this.originalHeight+(this.height-this.originalHeight)/2;
};
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
// top left
tl.onResize = function() {
this._x = Stage.getLeft();
this._y = Stage.getTop();
};
// top right
tr.onResize = function() {
this._x =
Stage.getRight()-this._width;
this._y = Stage.getTop();
};
// bottom left
bl.onResize = function() {
this._x = Stage.getLeft();
this._y =
Stage.getBottorn()-this._height;
};
// bottom right
br.onResize = function() {
this._x =
Stage.getRight()-this._width;
this._y =
Stage.getBottom()-this._height;
};
Пример
5.1.
- Сохраните ваш
фильм в файле
stageObject.fla. Запустив его, вы увидите,
что фильмы располагаются в нужных углах, как и
предполагалось. Полученный результат лучше
наблюдать в отдельном окне проигрывателя, нежели
в браузере, так как вокруг фильма Flash в
браузере будет немного лишнего пространства.
Все
работает как нужно, но не оптимально. Каждый раз,
когда надо найти левую координату рабочего места,
мы вызываем функцию
Stage.getLeft, тогда как это значение
изменяется только при изменении размера рабочего
места.
Более эффективное
изменение размера
Намного
проще иметь дело со статическими значениями,
вычисляемыми только при изменении размера рабочего
места. Это удобнее всего реализовать внутри самого
объекта Stage, поэтому
мы будем устанавливать параметры
Stage.left,
Stage.top,
Stage.right и
Stage.bottom каждый
раз при изменении его размеров. Согласно словарю
ActionScript, объект Stage
имеет строенный управляющий элемент, которому можно
присваивать функцию. К сожалению, этот способ не
работает, и единственный способ заставить его
работать - это добавить объект
Stage самому себе в
качестве приемника (это подход может показаться
странным, но он имеет положительный результат).
Итак,
для функции Stage.onResize
есть код (он приведен ниже), вызывающий каждую из
четырех функций get и
помещающий результаты в параметры внутри объекта
Stage.
- Добавьте
следующий код над функциями
get.
Stage.onResize = function() {
this.left = this.getLeft();
this.top = this.getTop();
this.right = this.getRight();
this.bottom = this.getBottom();
};
- В процессе
работы мы внесем значительные изменения в
функцию get. Так
как мы сначала вычисляем левую верхнюю позицию,
мы можем приравнять правую позицию к левой плюс
ширина и нижнюю к верхней плюс высота.
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)/2;
};
Stage.getTop = function() {
return -1* (this.height-this.original-Height)
/2;
};
Stage.getRight = function() {
return this.left+this.width;
};
Stage.getBottom = function() {
return this.top+this.height;
};
- Затем
добавляем приемник рабочего места.
Stage.addListener(Stage);
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
Имейте в виду, что приемники принимают событие
изменения размера в том порядке, в котором они
добавлены, поэтому, в данном случае, Stage стоит
на первом месте, затем идут
tl,
tr,
bl и, наконец,
br. Здесь это
важно, так как нам нужно быть уверенными в том,
что значения для левой, правой и т.д позиций,
устанавливаются перед обращением к ним из
фильмов.
- Теперь нам
нужно пересоздать функции
onResize фильма для использования новых
значений.
//
top left
tl.onResize = function() {
this._x = Stage.left;
this._y = Stage.top;
};
// top right
tr.onResize = function() {
this._x = Stage.right-this._width;
this._y = Stage.top;
};
// bottom left
bl.onResize = function() {
this._x = Stage.left;
this._y = Stage.bottom-this._height;
};
// bottom right
br.onResize = function() {
this._x = Stage.right-this._width;
this._y = Stage.bottom-this._height;
};
- Сохраните
фильм в файле
stageObject2.fla. Приведем полный код,
функционирующий более "аккуратно".
fscommand("allowscale", "false");
Stage.scaleMode="showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
Stage.scaleMode="noScale";
Stage.onResize = function() {
this.left = this.getLeft();
this.top = this.getTop();
this.right = this.getRight();
this.bottom = this.getBottom();
};
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)
/2;
};
Stage.getTop = function() {
return -1* (this.height-this.original-Height)
/2;
};
Stage.getRight = function() {
return this.left+this.width;
};
Stage. getBottom = function() {
return this.top+this.height;
};
Stage.addListener(Stage);
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
// top left
tl.onResize = function() {
this._x = Stage.left;
this._y = Stage.top;
};
// top right
tr.onResize = function() {
this._x = Stage.right-this._width;
this._y = Stage.top;
};
// bottom left
bl.onResize = function() {
this._x = Stage.left;
this._y = Stage.bottom-this._height;
};
// bottom right
br.onResize = function() {
this._x = Stage.right-this._width;
this._y = Stage.bottom-this._height;
};
Пример
5.2.
Добавление
движения при изменении размера рабочего места
Сейчас
мы продемонстрируем, как можно добавить движение на
примере скольжения уголков на свои места. Для этого
нам понадобится скопировать созданную нами в
лекции 1
функцию slideTo.
- Добавьте
следующий код над функциями
onResize.
MovieClip.prototype.slideTo = function(x, y,
speed, callbackObj, callbackFunc) {
var noo
if (this.slideControl) {
noo =
this.slideControl;
} else {
noo =
this.createEmptyMovieClip(<slideControl>,this.depth++);
}
noo.tx = x;
noo.ty = y;
noo.speed = speed;
// store callback object / method
(not used yet)
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnterFrame = function() {
this._parent._x +=
(this.tx-this._parent._x)/this.speed;
this._parent._y +=
(this.ty-this._parent._y)/this.speed;
if
(Math.abs(this.tx-this._parent._x)<0.2 &&
Math.abs(this.ty
Кthis._parent
._y) <0.2) {
this._parent._x = this.tx;
this._parent._y = this.ty;
// execute callback (we're not using this
yet)
this.callBackObj
[this.calIBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Пример
5.3.
- Измените
вызовы onResize,
чтобы вызывать функцию
slideTo со значениями
X и
Y в качестве
конечных точек скольжения.
tl.onResize = function() {
var x = Stage.left;
var у = Stage.top;
this.slideTo(x, y, 5);
}
tr.onResize = function() {
var x = Stage.right-this._width;
var у = Stage.top;
this.slideTo(x, y, 5);
};
bl.onResize = function() {
var x = Stage.left;
var у = Stage.bottom-this._height;
this.slideTo(x, y, 5);
};
br.onResize = function() {
var x = Stage.right-this ._width;
var у = Stage.bottom-this._height;
this.slideTo(x, y, 5);
};
- Сохраните
фильм в файле
stageObject3.fla. При его запуске вы
увидите, что углы постепенно появляются и
скользят на свои позиции каждый раз при
изменении размеров окна браузера или
проигрывателя Flash. Приведем конечный код
программы.
fscommand("allowscale", "false");
Stage.scaleMode="showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
Stage.scaleMode="noScale";
Stage.onResize = function() {
this.left = this.getLeft();
this.top = this.getTop();
this.right = this.getRight();
this.bottom = this.getBottom();
};
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)/2;
};
Stage.get Top = function() {
return -1*(this.height-this.originalHeight)/2;
};
Stage.getRight = function() {
return this.left+this.width;
};
Stage.getBottom = function() {
return this.top+this.height;
};
Stage.addListener(Stage);
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
MovieClip.prototype.slideTo = function(x, y, speed,
callbackObj, callbackFunc) {
var noo
if (this.slideControl) {
noo =
this.slideControl;
} else {
noo =
this.createEmptyMovieClip("slideControl",this.depth++);
}
noo.tx = x;
noo.ty = y;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.calIBackFunc = callBackFunc;
noo.onEnterFrame = function() {
this._parent._x += (this.tx-this._parent._x)/this.speed;
this._parent._y += (this.ty-this._parent._y)/this.speed;
if (Math.abs(this.tx-this._parent._x)<0.2
&&Math.abs(this.ty
Кthis._parent._y)<0.2) {
this._parent._x = this.tx;
this._parent._y = this.ty;
this.calIBackObj
[this.calIBackFunc](this._parent);
this.removeMovieClip();
}
};
};
tl.onResize = function() {
var x = Stage.left;
var у = Stage.top;
this.slideTo(x, y, 5);
};
tr.onResize = function() {
var x = Stage.right-this._width;
var у = Stage.top;
this.slideTo(x, y, 5);
};
bl.onResize = function() {
var x = Stage.left;
var у = Stage.bottom-this._height;
this.slideTo(x, y, 5);
};
br.onResize = function() {
var x = Stage.right-this._width;
var у = Stage.bottom-this._height;
this.slideTo(x, y, 5);
};
Пример
5.4.
Если использовать этот подход к изменению
размеров рабочего места, то Flash Player
придется выполнять гораздо больший объем кода.
Если в проекте много движущихся объектов, в
особенности с эффектами прозрачности, Flash
Player может "тормозить", поэтому подумайте,
стоит ли его использовать.
Изменение размеров
объекта
Метод
slideTo перемещает
объект из одного места в другое. Теперь мы немного
изменим метод slideTo
для создания метода scaleTo,
который будем применять с объектом
Stage для создания
фоновой панели изменяемого размера, которая будет
изменяться с увеличением или уменьшением фильма и
находиться по центру под названием.
- Откройте файл
stageObject3.fla и
сохраните его в файле
resize001.fla. Теперь нарисуйте серый
квадрат размером 100х100 пикселей и преобразуйте
его в фильм с центральной точкой регистрации (закрепления).
- Удалите новый
фильм с рабочего места, но перетащите его
инстанс на новый слой с именем
background panel,
который размещается под слоем, содержащим
заголовок "center stage".
- С помощью
Property inspector назовите инстанс фильма
именем bg и
измените его размер так, чтобы он был размером
500 пикселей в ширину и 350 в высоту. С помощью
панели Align расположите фильм по центру по
горизонтали и по вертикали, нажав
соответствующие кнопки при выделенной кнопке To
Stage. Ваше рабочее место и временная диаграмма
будут выглядеть так.
- Создадим код,
который будет сохранять постоянное расстояние от
фильма до края в 25 пикселей, независимо от
размера фильма. Прежде всего, нужно изменить
размер. Добавьте в фильм приемник под другими
приемниками.
Stage.addListener(bg);
- Затем
добавьте в фильм приемник
onResize под другими приемниками
onResize. После
этого сохраните фильм и запустите его.
bg.onResize = function() {
this._xscale = Stage.width-50;
this._yscale = Stage.height-50;
};
Анимация изменения
размеров
Все
работает корректно. Уголки скользят на свои места, и
было бы неплохо, чтобы фон так же постепенно изменял
размер. Мы реализуем это в нашем следующем шаге.
Сохраните фильм в файле
resize002.fla.
- Создадим
метод scaleTo,
заменив _xscale и
_yscale на
_x и
_y в методе
slideTo. Введите
следующий код под функцией
slideTo.
MovieClip.prototype.scaleTo = function
(xsc, ysc, speed,callbackObj, callbackFunc) {
var noo;
if (this.scaleControl) {
noo = this.scaleControl;
} else {
noo =
this.createEmptyMovieClip
("scaleControl",
this. depth++);
}
noo.txsc = xsc;
noo.tysc = ysc;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.calIBackFunc = callBackFunc;
noo.onEnterFrame = function() {
this._parent._xscale +=
(this.txsc-this._parent._xscale)/this,
speed;
this._parent._yscale +=
(this.tysc-this._parent._yscale)/this
.speed;
if (Math.abs(this.txsc-this._parent._xscale)<0.2
&&
КMath.abs(this.tysc-this._parent._yscale)<0.2)
{
this._parent._xscale =
this.tx;
this._parent._yscale =
this.ty;
//execute callback (not
used yet)
this.callBackObj [this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
- Эта
конструкция работает так же, как и
slideTo, но размер
объекта может изменяться. Теперь мы просто
изменим функцию onResize
для вызова этого метода, с передачей ей конечных
значений.
bg.onResize = function() {
var xsc = Stage.width-50;
var ysc = Stage.height-50;
this.scaleTo(xsc, ysc, 5);
};
- При проверке
в браузере вы увидите, что серый фон постепенно
изменяет размер во время увеличения или
уменьшения окна браузера.
Все работает прекрасно, но необходимость
применять различные методы для изменения
различных параметров раздражает. Для изменения
интенсивности придется добавить еще один метод,
и т.д. Изменять набор значений внутри фильма на
набор конечных значений было бы лучше с помощью
одного метода.
Использование
простого метода изменения параметров
На
самом деле это можно сделать, передав методу объект
с набором всех значений, которые мы хотим изменить.
После этого нужно организовать цикл для каждого
значения и постепенно приближать соответствующее
значение к значению в конечном объекте. Когда
значения приблизятся друг к другу, значение фильма
переключится на конечное значение, и затем это
значение удалится из объекта targets. Для
перемещения объекта к точке нужно передать объект
примерно так:
var obj
= {_x:50, _y:100};
this.tweenTo (obj, 5);
Начало
метода будет таким же, как в случае с методом
slideTo, т.е. будет
создаваться управляющий фильм, если такового еще не
существует, и затем внутри него будут
устанавливаться значения.
- Замените
предыдущие функции
slideTo и scaleTo
следующим кодом.
MovieClip.prototype.tweenTo =
function(targetsObj, speed,
КcallbackObj, callbackFunc) {
var noo;
if (this.tweenControl) {
noo = this.tweenControl;
} else {
noo = this.createEmptyMovieClip("tweenControl", this.depth++);
}
// store the object containing target properties and values
noo.targetsObj = targetsObj;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnterFrame = function() {
var count;
// as its an object we can't check length.
}
}
- Затем нужно
настроить цикл на прохождение и изменение всех
значений. Для итерации через значения в объекте
мы используем цикл for:in следующим образом.
for
(var prop in this.targetsObj) {
this._parent[prop]+=(this.targetsObj
[prop]this._parent[prop]
)/this, speed;
}
По
мере работы цикла, prop будет обращаться к имени
каждого параметра, и в следующей строке кода мы
получим очередное значение этого параметра в
фильме, приближающееся к значению в параметре
объекта targets.
- Если фильм
достиг конечного параметра, нужно переключить
его на этот конечный параметр и затем удалить
его из объекта targets.
Новый код выделен жирным шрифтом.
for
(var prop in this.targetsObj) {
this._parent[prop] += (this.targetsObj
[prop] this ._parent[prop]) /this.speed;
// if property has reached target
value
if (Math.abs(this.targetsObj[prop]-
this._parent[prop])<0.4) {
this._parent[prop] = this.targetsObj[prop];
delete
this.targetsObj[prop];
}
}
- Добавим
проверку того, является ли объект
targets пустым. В
отличие от объекта массива, объект класса не
имеет параметра длины, поэтому нам нужно знать,
сколько параметров пройдено циклом. Так как
параметр каждого фильма достигает
соответствующего конечного параметра, конечный
параметр удаляется из
targetsObj. Когда все параметры фильма
достигают конечных параметров, в
targetsObj не
останется параметров, и значением
count будет ноль
вследствие отсутствия параметров для обработки
циклом. Когда count равен нулю, мы можем
уничтожить управляющий фильм и вызвать функцию
обратной связи.
MovieClip.prototype.tweenTo = function
(targetsObj, speed, callbackObj,
calIbackFunc) {
var noo;
if (this.tweenControl) {
noo =
this.tweenControl;
} else {
noo =
this.createEmptyMovieClip
("tweenControl",
this.depth++);
}
noo.targetsObj = targetsObj;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnterFrame = function() {
var count;
// as it's an
object we can't check length.
for (var prop
in this. targetsObj) {
this._parent[prop] += (this.targetsObj[prop]
-
Кthis._parent [prop]) /this.speed;
if (Math.abs (this.targetsObj
[prop] this._parent [prop])<0.4) {
this._parent[prop] = this.targetsObj[prop];
delete this.targetsObj[prop];
}
count++;
}
if (!count) {
this.callBackObj[this.calIBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Пример
5.5.
- Для
применения этого метода управления движением
нужно изменить приемники
onResize. Вот один из них для правого
угла, влияющий на _x
и _y.
br.onResize = function() {
// define targets
var obj = (_x:Stage.right-this._width,
_y:Stage.bottom
this ._height};
this.tweenTo(obj, 5);
};
:а вот фоновый приемник, влияющий на
значения _xscale и
_yscale.
bg.onResize = function() {
var obj = (_xscale:Stage.width-50,
_yscale:Stage.height-50};
this.tweenTo(obj, 5);
};
Ниже приведен целиком код, в котором также были
изменены функции onResize.
fscommand("allowscale", "false");
Stage.scaleMode="showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
Stage.scaleMode="noScale";
Stage.onResize = function() {
this.left = this.getLeft();
this.top = this.getTop();
this.right = this.getRight();
this.bottom = this.getBottom();
};
Stage.getLeft = function() {
return -1*(this.width-this.originalWidth)/2;
};
Stage.getTop = function() {
return -1* (this.height-this.originalHeight)/2;
};
Stage.getRight = function() {
return this.left+this.width;
};
Stage.getBottom = function() {
return this.top+this.height;
};
Stage.addListener(Stage);
Stage.addListener(tl);
Stage.addListener(tr);
Stage.addListener(bl);
Stage.addListener(br);
Stage.addListener(bg);
MovieClip.prototype.tweenTo = function(targetsObj, speed,
callbackObj, callbackFunc) {
var noo;
if (this.tweenControl) {
noo =
this.tweenControl;
} else {
noo =
this.createEmptyMovieClip("tweenControl",this.depth++);
}
noo.targetsObj = targetsObj;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnterFrame = function() {
var count;
// as its an
object we can't check length,
for (var prop
in this.targetsObj) {
this._parent[prop] += (this.targetsObj[prop]-
this._parent [prop] ) /
Кthis. speed;
if (Math.abs(this.targetsObj [prop]-
this._parent[prop])<0.4) {
this._parent [prop] = this.targetsObj[prop];
delete this.targetsObj [prop];
}
count++;
}
if (! count)
{
this.callBackObj
[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
tl.onResize = function() {
var obj = (_x:Stage.left,
_y:Stage.top};
this.tweenTo(obj, 5);
};
tr.onResize = function() {
var obj =
(_x:Stage.right-this._width, _y:Stage.top};
this.tweenTo(obj, 5);
};
bl.onResize = function() {
var obj = (_x:Stage.left,
_y:Stage.bottom-this._height};
this.tweenTo(ob j, 5);
};
br.onResize = function() {
var obj =
(_x:Stage.right-this._width, _y:Stage.bottom
thi s._height};
this.tweenTo(obj, 5);
};
bg.onResize = function() {
var obj = (_xscale:Stage.width-50,
_yscale:Stage.height-50};
thi s.tweenTo(obj, 5);
};
Пример
5.6.
- Сохраните
фильм в файле
resize003.fla и запустите его. Он
работает точно так же, как и в предыдущем файле,
но его код более компактен.
Можно сделать еще больше - например, поставить
интенсивность фона в зависимость от размера
рабочего места.
bg.onResize = function() {
var obj = {_alpha:100*
(Stage.Width/Stage.originalWidth),
К_xscale:Stage.width-50,
_yscale:Stage.height-50};
this.tweenTo(obj, 5);
};
Эффект будет лучше заметен, если изменять цвет
квадрата в фильме с серого на красный или синий.
Если вы сделаете рабочее место совсем небольшим,
фон практически полностью исчезнет
(resize004.fla).
Нужно иметь в виду, что вместо создания
tweenTo в виде
метода объекта MovieClip
можно было бы создать отдельный класс
tween, унаследовав
его из класса MovieClip.
Это удобно, если большое количество фильмов
требует доступ к этим методам. Реализовать это
можно так.
function tweenable() {
}
tweenable.prototype = new Movieclip();
Object.registerClass("tweenableMc", tweenable);
tweenable.tweenTo = function() {
var noo;
if (this.tweenControl) {
noo =
this.tweenControl;
} else {
noo =
this.createEmptyMovieClip("tweenControl",
this.depth++);
}
noo.targetsObj = targetsObj;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnter Frame = function() {
var count;
// as it's an
object we can't check length,
for (var prop
in this.targetsObj) {
this._parent [prop] += (this.targetsObj
[prop]
Кthis._parent [prop] ) /this. speed;
if (Math.abs (this.targetsObj [prop]
this._parent [prop] ) <0.4) {
this._parent[prop] = this.targetsObj [prop];
delete this.targetsObj [prop];
}
count++;
}
if (!count) {
this.callBackObj
[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Пример
5.7.
Можно использовать
Object.registerClass для связывания
любого отдельного символа фильма с данным
классом. Тогда вы могли бы добавить
дополнительные методы к этому классу для
добавления параметров к
tween.
tweenable.prototype.addToTween =
function(obj) {
//loop through and add properties to targetsObj
for (var prop in obj) {
this.tweenControl.targetsObj [prop] = obj [prop];
}
};
Также можно присвоить каждому параметру свою
скорость, свои обратные связи для использования
по достижении цели, или передавать аргумент с
именем параметра и достигнутым значением. Все
это зависит от того, как вы хотите использовать
эти параметры. Можно было бы вернуться к подходу,
с помощью которого мы установили точку
наблюдения для события
rollOver с компонентом всплывающей
подсказки, и придумать в данном случае нечто
аналогичное, чтобы обычные события
onEnterFrame
выполнялись в одно и то же время, что и функция
tweenTo, и чтобы
не нужно было добавлять управляющий фильм.
XML
Теперь
мы приступим к компиляции окончательной версии
нашего сайта. Если мы хотим сделать представление
изображений динамическим, имеет смысл сохранять
список рисунков отдельно от фильма Flash и загружать
рисунки по мере надобности.
XML
является самым удобным способом работы с параметрами
изображений, такими как ширина, высота и заголовок.
В разрабатываемом здесь приложении мы
сконцентрируемся на загрузке данных XML для
реализации наших целей. В
лекции 17
более подробно рассказывается об использовании XML
совместно с Flash MX; в ней вы также изучите
некоторые из правил и решений, важных при работе с
данными XML. Если вы впервые столкнулись с XML, вам
будет лучше ознакомиться с несколькими первыми
страницами
лекции 17
перед изучением данной темы.
Можно
очень просто описать список рисунков с помощью XML.
Каждое изображение будет иметь свой собственный узел,
содержащий атрибуты для своего имени файла и
названия. Каждый узел будет выглядеть примерно так.
<picture
file="denim001.jpg" name="whatever"/>
Приведенный выше код XML содержит информацию об
имени файла изображения, его ширине и высоте, а
также о назначенном изображению названии, если
таковое имеется. Знак " означает начало узла, так же,
как и в HTML, а его окончание обозначается знаком
/". picture означает тип данного узла, аналогично
тому, как b означает жирный шрифт в HTML.
Полный
код XML будет состоять из нескольких таких элементов,
объединенных в корневой элемент
pictures. Введите этот
текст в текстовый файл и сохраните его под именем
pictures.xml.
<pictures>
<picture file="denim001.jpg" name="whatever"/>
<picture fiie="denim002.jpg" name="whatever"/>
<picture file="denim003.jpg" name="whatever"/>
<picture file="denim004.jpg" name="whatever"/>
<picture file="denim005.jpg" name="whatever"/>
<picture file="denim006.jpg" name="whatever"/>
<picture file="denim007.jpg" name="whatever"/>
<picture file="denim008.jpg" name="whatever"/>
</pictures>
Вы
только что создали простой файл XML. Если вы
откроете файл pictures.xml
в браузере, вы увидите иерархическое описание
информации.
Заметьте, что здесь есть значок +/-, на котором вы
можете щелкать для открытия или закрытия структуры
документа и вывода связанной информации. Сам по себе
этот документ не представляет особого интереса.
Данные XML становятся полезными лишь тогда, когда
они обрабатываются каким-либо другим приложением.
Разберемся, как можно использовать эти данные во
Flash.
Добавление данных
XML во Flash
Структуру XML нужно импортировать во Flash и затем
преобразовать информацию в данные, которые можно
использовать, т.е. в массив объектов изображений.
- Создадим
новый объект, в который будем загружать документ
XML.
myXml = newXml();
myXml.load ("pictures.xml");
Это действие добавит документ XML, но после
полной загрузки нам необходимо вызывать функцию.
- Для вызова
функции по завершении загрузки документа XML мы
можем использовать управляющий элемент
onLoad объекта
XML. Как и в случае с управляющими элементами
событий фильма, мы можем определить функцию для
ее вызова при возникновении данного события.
myXml = new Xml();
// call parseMe once xml has loaded
myXml.onLoad = parseMe;
myXml.load("pictures.xml");
function parseMe() {
}
- Перед
созданием функции parseMe
нужно добавить одну или более строк кода для
обеспечения игнорирования всех пустых мест в
исходном файле XML (к примеру, возврат каретки
или табуляция).
myXml = new Xml();
myXml.ignoreWhite = true;
myXml.onLoad = parseMe;
myXml.load("pictures.xml");
function parseMe() {
}
- Функция
parseMe должна
раскрывать соответствующую информацию из каждого
узла изображения и сохранять его, поэтому мы
также создадим массив для хранения в нем данных.
pictureObjects = [];
myXml = new Xml();
myXml.ignoreWhite = true;
myXml.onLoad = parseMe;
myXml.load("pictures.xml");
function parseMe() {
}
- При вызове
функции parseMe
наша цель находится внутри объекта XML, поэтому,
если мы используем this,
то получим список всего документа. Введите этот
код под имеющимся кодом.
function parseMe() {
trace(this);
}
Если вы сохраните фильм в той же папке, где
сохранили pictures.xml,
и запустите его, то увидите строку в окне
Output, содержащую всю информацию внутри
pictures.xml.
Однако в объекте XML имеется гораздо больше
возможностей. При импортировании документа XML
во Flash последний обрабатывает документ и
создает из него структуру объекта, причем каждый
узел ссылается на свои дочерние узлы, на братьев
и на родителей, равно как и на атрибуты, которые
могут иметь узлы.
- Чтобы увидеть
все это, создадим цикл, проходящий все параметры
объекта.
function parseMe() {
}
function parseMe() {
for (var i in this) {
trace
(property - "+i+" : value - "+this[i]");
}
}
Этот цикл будет возвращать в окне Output
следующий результат.
Итак, с помощью функции
parseMe нам нужно перейти к узлу pictures
и пройти циклом через его дочерние узлы (узлы
picture), выбирая атрибуты каждого изображения и
сохраняя их в массиве.
При вызове функции
parseMe мы находимся внутри документа XML
в корне документа, т.е. на уровень выше узла
pictures.
Для обращения к узлу изображений вводим
следующее.
this.firstChild;
Это выражение ссылается на первый дочерний узел
главного узла. Теперь узлы каждого отдельного
изображения сохраняются внутри массива
childNodes узла
pictures - это
узлы, существующие в иерархии внутри узла
pictures. Для
обращения к этому массиву вводим следующее. Это
выражение ссылается на первый дочерний узел
главного узла. Теперь узлы каждого отдельного
изображения сохраняются внутри массива
childNodes узла
pictures - это
узлы, существующие в иерархии внутри узла
pictures. Для
обращения к этому массиву вводим следующее.
this.firstChild.childNodes;
- Это и есть
массив, который мы будем обрабатывать циклом,
поэтому нужно настроить сам цикл примерно так.
function parseMe() {
var pictures =
this.firstChild.childNodes;
// loop through child nodes
for (var i in pictures) {
populate the
array
}
}
- Внутри цикла
каждым отдельным узлом рисунка будет
pictures[i].
Информация, которую мы хотим получить,
содержится внутри объекта
attributes узла
picture. Этот объект является стандартным
объектом с параметром для каждого из атрибутов,
поэтому мы можем просто копировать его в массив
с использованием
Array.push, чтобы он добавлялся в конец
массива.
function parseMe() {
var pictures =
this.firstChild.childNodes;
for (var i in pictures) {
var obj =
pictures[i].attributes;
pictureObjects.push(obj);
}
}
- Можно
сократить.
function parseMe() {
var pictures =
this.firstChild.childNodes;
for (var i in pictures) {
pictureObjects.push(pictures[i].attributes);
}
}
Проблема заключается в том, что мы не создаем
новый объект в массиве, а просто создаем ссылку
на объект attributes
в объекте XML. Если объект
attributes в узле
XML изменится, то же самое произойдет и с
объектом в нашем массиве.
- Посмотрите на
этот код и приведите ваш код в соответствие.
function parseMe(){
var pictures =
this.firstChild.childNodes;
for (var i in pictures) {
pictureObjects.push(pictures[i]
.attributes);
}
// add an attribute "hello" to the
first childNode
this.firstChild.childNodes[0].attributes.hello
= true;
}
- Теперь
запустите код и выберите Debug > List Variables.
В нашем массиве отобразится значение
hello.
- Чтобы
предотвратить это, будем обрабатывать объект
attributes и
копировать все параметры.
function parseMe() {
// this is the entire document
// this.firstChild is the first node,
ie our pictures node
// this firstChiId.childNodes is the
list of
individual documents
var pictures =
this.firstChild.childNodes;
for (var i in pictures) {
var obj = {};
// loop
through and copy attributes
for (var j in
pictures[i].attributes) {
obj [j] = pictures [i] .attributes [j];
}
pictureObjects.push(obj);
}
this.firstChild.childNodes[0].attributes.hello
= true;
}
- Сохраните
файл под именем
bringInXML.fla и при его запуске выберите
List Variables. hello
больше не отображается в массиве.
В
данном случае это не очень важно, так как мы не
собираемся в дальнейшем осуществлять управление
документом XML, но нужно понимать, что
происходит, когда объекты определяются
посредством ссылки на другие объекты, а не в
своих собственных терминах. Наш исходный массив
содержал только указатель на объект атрибутов в
узле XML, так что мы просто скопировали в него
все параметры. Другой способ - удалить объект
myXml, чтобы разрушить исходный объект
attributes и
оставить значение в массиве в виде единственной
ссылки на этот объект.
Загрузчик XML
Интерфейс, создаваемый нами, будет составной частью
сайта, поэтому добавим загрузчик данных XML. Мы
создадим простой текстовый индикатор состояния,
который будет отображать процент загрузки данных, а
также выводить сообщение в случае неудавшейся
загрузки XML. Нам нужно добавить код для метода
tweenTo, установить
метод setBrightness из
предыдущей лекции и созданный нами объект
Stage.
Вместо
добавления всего этого кода вручную, сохраним код
отдельно в файлах .as и используем выражения include
для их включения.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
Хорошей практикой считается использование таких
фрагментов кода при работе с объектами. Когда Flash
доходит до выражения include
при компиляции SWF, то добавляет содержимое
указанного в include
файла. Последняя строка файла AS обязательно должна
заканчиваться точкой с запятой! Одной из новых
возможностей во Flash MX является глобальный путь
include. Обычно при
использовании выражения
include нужно убедиться в том, что файлы AS
находятся в той же директории, что и включающий их
файл Flash. Глобальный путь позволяет сохранять
любые файлы AS и предоставлять к ним доступ всем
фильмам. На компьютере этот путь выглядит так:
C:\Program Files\Macromedia\Flash MX\Configuration\
Include - папку include вы создаете сами. Когда
Flash дойдет до выражения
include, сначала будет осуществлен поиск в
локальной директории, и если файл AS не будет найден
в этой папке, то его поиск будет продолжен в
глобальной папке include.
Здесь
нужно включить три файла, для каждого из которых
ниже указан ActionScript. Если вы не хотите вводить
их заново или копировать из предыдущих файлов,
возьмите их из исходных файлов для этой лекции.
Приведем содержимое каждого из трех файлов.
Подключение внешнего кода:
tweenTo.as
MovieClip.prototype.tweenTo =
function(targetsObj, speed,
callbackObj,callbackFunc) {
var noo
if (this.tweenControl) {
noo =
this.tweenControl;
} else {
noo =
this.createEmptyMovieClip("tweenControl",this.depth++);
}
noo.targetsObj = targetsObj;
noo.speed = speed;
noo.callBackObj = callBackObj;
noo.callBackFunc = callBackFunc;
noo.onEnterFrame = function() {
var count;
// as its an
object we can't check length,
for (var prop
in this.targetsObj) {
this._parent[prop] += (this.targetsObj [prop]-
Кthis._parent [prop] ) /this. speed;
if (Math.abs(this.targetsObj [prop]-this._parent
[prop] ) <0.9) {
this._parent[prop] = this.targetsObj [prop];
delete this.targetsObj [prop];
}
count++;
}
if (!count) {
this.callBackObj
[this.callBackFunc](this._parent);
this.removeMovieClip();
}
};
};
Пример
5.8.
Подключение внешнего кода:
brightness.as
Color.prototype.setBrightness = function(bright)
{
var aNum = 100-Math.abs(bright);
var bNum = bright<0 ? 0 :
Math.ceil(255*bright/100);
this.setTransform({ra:aNum, ga:aNum,
ba:aNum, aa:100,
Кrb:bNum, bb:bNum, ab:0});
};
Подключение внешнего кода:
stage.as
fscommand("allowscale", "false");
Stage.scaleMode="showAll";
Stage.originalWidth = Stage.Width;
Stage.originalHeight = Stage.Height;
Stage.scaleMode="noScale"
Stage.onResize = function() {
this.left = this.getLeft();
this.top = this.getTop();
this.right = this.getRight();
this.bottom = this.getBottom();
};
Stage.getLeft = function() {
return -1*(Stage.width-this.originalWidth)/2;
};
Stage. getTop = function() {
return -1*(Stage.height-this.originalHeight)/2;
};
Stage. getRight = function() {
return this.left+this.width;
};
Stage. getBottom = function() {
return this.top+this.height;
};
Stage.addListener(Stage);
// ensure onResize is called at start
Stage.onResize();
Пример
5.9.
Создание
загрузчика XML
Теперь
внешние файлы ActionScript включены, и мы займемся
кодом для фильма.
- Добавим новый
слой scripts.
Прежде всего мы создадим функцию для текстового
отображения состояния фильма. Мы начнем с
объекта текста TextFormat,
который добавим под нашими выражениями
include.
statusTf = new TextFormat("Arial", 40,
0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
Текущий код будет выглядеть так.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
- На другом
слое добавим динамическое текстовое поле сразу
за верхней частью рабочего места. С помощью
ActionScript укажем его местоположение. Выделим
текстовое поле и добавим все символы Arial (или
любого другого шрифта), нажав кнопку Character:
в Property inspector. В диалоговом окне
Character Options выберем опцию All Characters и
нажмем кнопку Done.
- В Property
inspector назовем текстовое поле embedder и
введем следующий выделенный код, чтобы сделать
поле невидимым.
embedder._visible = 0;
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
- Мы также
добавим переменную
filename (выделено жирным шрифтом) для
указания файла XML, нужного для загрузки. Здесь
нам не понадобится использовать переменную,
однако это упростит изменение имени файла при
необходимости.
_root.filename = "pictures.xml";
embedder._visible = 0;
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
- Теперь мы
добавим функцию
createStatusMovie для создания текстового
поля, установим формат текста и затем установим
текст на значение, которое передается функции.
function createStatusMovie(tex) {
_root.createEmptyMovieClip("statusClip",
1);
statusClip.createTextField
("statusText", 1,
0, 0, 550, 100);
// set properties of textfield
statusClip.statusText.embedFonts =
true;
statusClip.statusText.selectable =
false;
statusClip.statusText.setNewTextFormat(statusTf);
statusClip.statusText.text = tex;
statusClip._x = 0;
statusClip._y =
(Stage.originalHeight-
statusClip.statusText.textHeight)/2-50;
}
Мы
используем объект Stage
для расположения текстового поля довольно далеко
от центра по вертикали, а для горизонтального
центрирования используем настройку выравнивания
текстового поля. Функция
createStatus будет вызываться при первом
импорте XML. Мы можем использовать такую же
функцию для создания загрузчика отдельных
изображений.
- Для создания
предзагрузчика XML мы используем методы
getBytesLoaded и
getBytesTotal
объекта XML. Если поделить
getBytesLoaded на
getBytesTotal и
затем умножить на 100, получим результат в
процентах, представляющий собой загруженный XML.
Функция updateXmlStatus
будет вычислять процент и отображать результат в
текстовом поле. Используем
setInterval для
регулярного вызова данной функции. Некоторая
часть следующего кода функции
importXml будет
аналогична коду предыдущего раздела, где мы
сначала импортировали наши данные XML во Flash.
Новый код выделен жирным шрифтом.
function importXml() {
pictureObjects = [];
myXml = new XML();
myXml. ignoreWhite = true;
// method to update display
myXml.updateXmlStatus = function() {
var percent =
Math.round(this.getBytesLoaded()/
Кthis.getBytesTotal()*100);
if
(percent<=0) {
percent = "0";
}
_root.statusClip.statusText.text = "PICTURE
DATA LOADING
К\n"
percent+"%";
};
// use setlnterval to call
updateXmlStatus every 20
//milliseconds
myXml.updater = setlnterval(myXml,
"updateXmlStatus", 20);
myXml.onLoad = parseMe;
myXml.load(_root.filename);
_root.createStatusMovie("PICTURE DATA
LOADING");
}
Рассмотрим функцию
updateXmlStatus.
myXml.updateXmlStatus = function() {
var percent = Math.round
(this.getBytesLoaded()/this.getBytesTotal()*100);
if (percent<=0) {
percent =
"0";
}
_root.statusClip.statusText.text =
"PICTURE DATA
LOADING \n"+percent+"%";
};
Прежде всего, мы вычисляем процентное значение и
округляем его, чтобы избавиться от десятичной
части. Когда вызов загрузки XML происходит в
первый раз (перед тем, как Flash свяжется с
сервером или примет любую информацию о файле),
getBytesTotal
возвращает значение
undefined. Тогда процентное значение
будет равно нулю, деленному на
undefined, что,
естественно, не даст никакого результата, и
будет возвращено значение
NaN (не число).
Чтобы предотвратить отображение этого значения в
виде процентов, мы выполняем проверку на
percent. Так как
NaN воспринимается
Flash как значение, меньшее нуля, мы проверяем,
является ли процентное значение меньшим, чем
ноль и, если это так, просто устанавливаем
percent на значение ноль. В последней строке мы
обновляем отображаемый текст, добавляя новую
строку после PICTURE DATA
LOADING.
- Теперь
рассмотрим, как отображать сообщение, если
что-то происходит неправильно. Это можно
реализовать, значительно изменив функцию
parseMe. Когда
функция присваивается управляющему элементу
onLoad объекта XML, ей передается один параметр
при происхождении события
onLoad. Если XML загружен успешно,
передается значение "истина", в противном случае
- "ложь". Итак, мы изменяем нашу предыдущую
функцию parseMe
для проверки этого значения и выполнения
действий с его использованием. Новый код выделен
жирным шрифтом.
function parseMe(success) {
// if the xml has loaded successfully
if (success) {
var pictures
= this.firstChild.childNodes;
for (var i =
0; i<pictures.length; i++) {
var obj = {};
for (var j in pictures[i] .attributes) {
obj [j] = pictures [i] .attributes [j];
}
pictureObjects.push(obj);
}
// stop
calling the display update function
clearInterval(this.updater);
_root.pictureDataComplete();
this.updateXmlStatus();
//statusClip.removeMovieClip()
} else {
_root.statusClip.statusText.text =
"FAILED TO LOAD DATA";
clearInterval(this.updater);
}
}
Если загрузка не удалась, мы изменяем
statusText для
чтения FAILED TO LOAD
DATA. Независимо от того, была загрузка
успешной или нет, вызывается интервал
updateXmlStatus.
При непосредственном использовании кода мы будем
удалять клип состояния сразу по завершении
загрузки, однако сейчас мы оставим его, чтобы
убедиться в правильности функционирования вызова
статуса, т.к. в противном случае сообщение будет
исчезать практически мгновенно.
- Добавим
функцию
pictureDataComplete, которая будет
вызываться после загрузки XML, и функцию
init для начала
загрузки XML. Приведем окончательный код. Жирным
шрифтом выделены функции
pictureDataComplete и
init.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.filename = "pictures.xml";
embedder._visible = 0;
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
function createStatusMovie(tex) {
_root.createEmptyMovieClip("statusClip",
1);
statusClip.createTextField("statusText", 1,
0, 0, 550, 100);
statusClip.statusText.embedFonts =
true;
statusClip.statusText.selectable =
false;
statusClip.statusText.setNewTextFormat(statusTf);
statusClip.statusText.text = tex;
statusClip._x = 0;
statusClip._y = (Stage.originalHeight-
statusClip.statusText.textHeight)/2-50;
}
function importXml() {
pictureObjects - [];
myXml = new XML();
myXml.ignoreWhite = true;
myXml.updateXmlStatus = functionO {
var percent =
Math.round(this.getBytesLoaded() /
this.getBytesTotal()*100);
if
(percent<=0) {
percent = "0";
}
_root.statusClip.statusText.text
= "PICTURE DATA LOADING \n"+percent+"%";
};
myXml.updater = setInterval(myXml, "updateXmlStatus",
20);
myXml.onLoad = parseMe;
myXml.load(_root.filename);
_root.createStatusMovie("PICTURE DATA
LOADING");
}
function parseMe(success) {
if (success) {
var pictures
= this.firstChild.childNodes;
for (var i =
0; i<pictures.length; i++) {
var obj = {};
for (var j in pictures[i].attributes) {
obj [j] = pictures [i] .attributes [j];
}
pictureObjects.push(obj);
}
clearlnterval(this.updater);
_root.pictureDataComplete();
this.updateXmlStatus();
//statusClip.removeMovieClip()
} else {
_root.statusClip.statusText.text = "FAILED
TO LOAD DATA";
clearlnterval(this.updater);
}
}
function pictureDataComplete() {
trace("picture data loaded");
}
function init() {
importXml () ;
}
init();
Пример
5.10.
Если вы запустите файл без его сохранения, вы
получите сообщение FAILED
TO LOAD DATA, т.к.
FLA и pictures.xml
не сохранены в той же папке. Сохраните фильм в
файле xmlLoader.fla
и запустите его снова. На этот раз, как мы и
планировали, будет выведено сообщение
PICTURE DATA LOADING 100%:
Локальное
сохранение XML
Одной
из прекрасных возможностей, связанных с общими
объектами, является возможность сохранять внутри них
любые данные. Благодаря этому можно не импортировать
каждый раз XML, содержащий имена файлов изображений,
а сохранить массив рисунков локально. Мы также можем
установить срок обновления для общего объекта, после
которого будет выполняться повторный импорт XML в
случае его изменения.
Методология этого подхода очень проста. Перед
импортом XML с помощью
sharedObject.getLocal мы проверяем, создан ли
общий объект для фильма. Если не создан, или общий
объект является более старшим, чем определенная дата,
мы продолжим работу и импортируем XML. После импорта
и обработки XML мы будем сохранять массив
pictureObjects и текущее время в общем объекте. Если
у нас уже есть общий объект, мы получим массив
pictureObjects из
общего объекта, а не из XML.
- Сначала
создадим функцию для сохранения массива в общем
объекте. Мы добавим вызов этой функции после
успешной загрузки XML, а также уберем
комментарий со строки кода для удаления
statusClip.
function parseMe(success) {
if (success) {
var pictures
= this.firstChild.childNodes;
for (var i =
0; i<piсtures.length; i++) {
var obj = {};
for (var j in pictures[i].attributes) {
obj [j] = pictures [i] .attributes [j];
}
pictureObjects.push(obj);
}
_root.refreshSharedObj();
clearlnterval
(this.updater);
statusClip.removeMovieClip();
_root.pictureDataComplete
();
} else {
_root.statusClip.statusText.text
= "FAILED TO LOAD DATA";
clearlnterval(this.updater);
}
}
- В функции
refreshSharedObj
нам нужно сначала получить общий объект с
помощью
sharedObject.getLocal. Затем мы поместим
наш массив pictureObjects
в общий объект.
mySharedObj =
sharedobject.getLocal("denim");
mySharedObj.data.pictureObjects = pictureObjects;
- Затем нам
нужно создать дату. Для этого можно использовать
метод getTime
объекта Date. Этот
метод возвращает число миллисекунд, прошедших с
1 января 1970 года, что дает значение, с которым
мы будем работать. Для этого сначала создаем
новый инстанс объекта
Date, а затем вызываем его метод
getTime, сохраняя
значение в качестве
timeStamp в общем объекте.
var
dat = new Date();
mySharedObj.data.timeStamp = dat.getTime();
- Окончательная
функция включает весь код из шагов 2 и 3 плюс
метод flush общего
объекта для сохранения информации. Расположите
функцию refreshSharedObj
под функцией parseMe.
function refreshsharedobj() {
mySharedObj =
sharedobject.getLocal("denim");
mySharedObj.data.pictureObjects =
pictureObjects;
var dat = new Date();
// store the timestamp in the shared
object
mySharedObj.data.timeStamp =
dat.getTime();
mySharedObj.flush();
}
- Следующая
функция - checkShared.
Мы будем вызывать эту функцию из функции init и
использовать возвращаемое значение функции для
определения того, вызываем или нет функцию
importXml. Если
функция возвращает значение "истина", это будет
означать, что данные были найдены в общем
объекте, и нам не требуется импортировать XML;
если же возвращается значение "ложь", приступаем
к импорту XML. Наша функция лишь возвращает
значение "истина", если она находит общий объект
с массивом рисунков внутри него, а разница между
текущим временем и временем его создания меньше
определенного значения. Вот как сейчас примерно
будет выглядеть наша функция
init.
function init() {
// if the checkshared function
returns true
if (!checkshared()) {
importXml();
}
}
- Первое, что
нужно сделать в
checkShared (это будет под функцией
refreshSharedObj),
это получить общий объект.
function checkshared() {
mySharedObj =
sharedobject.getLocal("denim");
}
- Затем мы
проверяем, содержит ли
mySharedObj массив с именем
pictureObjects в
объекте data.
function checkshared() {
mySharedObj =
sharedobject.getLocal("denim");
if (mySharedObj.data.pictureObjects)
{
. . .
}
}
- Если это так,
то мы сразу получаем нужные нам данные, и нам
требуется просто проверить даты, поэтому мы
создаем объект new Date.
function checkshared() {
mySharedObj =
sharedobject.getLocal("denim");
if (mySharedObj.data.pictureObjects)
{
var dat = new
Date();
}
}
- Для сравнения
текущей даты с датой сохранения общего объекта
мы можем сделать следующее.
dat.getTime ()
-mysharedObj.data.timeStamp;
Это определит количество миллисекунд между
текущим моментом и моментом установки общего
объекта. Наилучший период для новой загрузки XML
- примерно раз в неделю, поэтому мы проверяем,
что значение меньше, чем
1000*60*60*27*7 = 604800000.
Если оно меньше, возвращается "истина", если нет,
ничего не будет возвращено. Мы так же вызываем
функцию pictureDataComplete, которую создали к
моменту окончания загрузки XML. Новый код
выделен жирным шрифтом.
function checkShared() {
mySharedObj =
sharedobject.getLocal("denim");
if (mySharedObj.data.pictureObjects)
{
var dat = new
Date();
if (dat.getTime()-mysharedObj.data.timeStamp<604800000)
{
// retrieve the array
from the sharedObject
_root.pictureObjects =
mySharedObj.data.pictureObjects;
pictureDataComplete();
return true;
}
}
}
Приведем окончательный код.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.filename = "pictures .xml";
embedder._visible = 0;
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
function createStatusMovie(tex) {
_root.createEmptyMovieClip("statusClip",
1);
statusClip.createTextField("statusText", 1,
0, 0, 550, 100);
statusClip.statusText.embedFonts =
true;
statusClip. statusText. selectable =
false;
statusClip.statusText.setNewTextFormat(statusTf);
statusClip.statusText.text = tex;
statusClip._x = 0;
statusClip._y = (Stage. original
Height- statusClip.statusText.textHeight)/2-50;
}
function importXml() {
trace("data imported from xml");
pictureObjects = [];
myXml = new XML();
myXml.ignoreWhite = true;
myXml.updateXmlStatus = function() {
var percent =
Math.round(this.getBytesLoaded()/
this.getBytesTotal()*100);
if
(percent<=0) {
percent = "0";
}
_root.statusClip.statusText.text
= "PICTURE DATA LOADING \n"+percent+"%" ;
};
myXml.updater = setInterval(myXml, "updateXmlStatus",
20);
myXml.onLoad = parseMe;
myXml.load(_root.filename);
_root.createStatusMovie("PICTURE
DATA LOADING");
}
function parseMe(success) {
if (success) {
var pictures
= this.firstChild.childNodes;
for (var i =
0; i<pictures. length; i++) {
var obj = {};
for (var j in pictures[i] .attributes) {
obj[j] = pictures [i] .attributes [j];
}
pictureObjects.push(obj);
}
_root.refreshSharedObj();
clearInterval(this.updater);
statusClip.removeMovieClip();
_root.pictureDataComplete();
} else {
_root.statusClip.statusText.text
= "FAILED TO LOAD DATA";
clearInterval(this.updater);
}
}
function refreshSharedObj() {
mySharedObj =
sharedobject.getLocal("denim");
mySharedObj.data.pictureObjects =
pictureObjects;
var dat = new Date();
mySharedObj.data.timestamp =
dat.getTime();
mySharedObj.flush();
}
function checkShared() {
mySharedObj =
sharedobject.getLocal("denim" );
if (mySharedObj.data.pictureObjects)
{
var dat = new
Date();
if
(dat.getTime()-mysharedObj.data.timeStamp<604800000)
{
trace("data found in shared object");
_root.pictureObjects =
mySharedObj.data.pictureObj ects;
pictureDataComplete();
return true;
}
}
}
function pictureDataComplete() {
trace("picture data loaded");
}
function init() {
importXml();
}
function init() {
if (!checkShared()) {
importXml();
}
}
init();
Пример
5.11.
- Сохраните ваш
файл под именем
xmlInShared.fla и запустите его. Вот
результат, который вы получите в окне Output.
Мы
добавили два выражения
trace (выделены жирным шрифтом) для
проверки того, откуда загружаются данные - из
XML или из общего объекта. Можно
поэкспериментировать с ними, чтобы понять, как
они работают. Попробуйте изменить число
миллисекунд даты окончания общего объекта. Таким
же способом можно полностью удалить общий объект.
function checkShared() {
mySharedObj =
sharedobject.getLocal("denim");
// loop through properties and delete
them
for (var i in mySharedObj .data) {
delete
mySharedObj.data[i];
}
}
В
этом случае в окне Output отобразится, что
данные были импортированы из XML.
Этот способ сохранения XML в виде объектной
структуры может быть особенно полезен при работе
с большими структурами или использовании сложных
систем анализа. В этих обстоятельствах
сохранение результатов посредством анализа XML
позволяет нам сэкономить время и пропустить
загрузку данных и ее представление посредством
локального сохранения XML и представления его в
виде данных, которые в обычном порядке
распознаются Flash.
Предварительная
загрузка изображений
После
загрузки данных для рисунков нужно выполнить
предварительную загрузку изображений, одно за другим.
Мы реализуем это в функции
pictureDataComplete, которая будет
выполняться посредством поочередной загрузки
рисунков в пустой фильм, расположенный вне рабочего
места. Лучше делать это именно так, нежели загружать
рисунки все сразу, так как множественные вызовы
loadMovie в один и тот
же момент времени вызывают проблемы в некоторых
браузерах.
- В
pictureDataComplete
создадим новый фильм состояния, а также фильм, в
который будут загружаться изображения. Установим
enterFrame, принадлежащий
_root, на значение
checkPreload, которое будет обновлять
состояние и проверять загрузку каждого
изображения. Наконец, мы вызовем
loadNext для
начала загрузки первого изображения. Переменная
picLoading служит
для выяснения того, какое изображение
загружается в данный момент. Мы заменим
выражение trace,
располагавшееся прежде в функции
pictureDataComplete,
следующим кодом.
function pictureDataComplete() {
_root.createStatusMovie("PICTURES
LOADING");
_root.createEmptyMovieClip("picturePlacebo",
2);
picturePlacebo._x = 1000;
picLoading = 0;
// check how much is loaded on
enterFrame
this.onEnterFrame = checkPreload;
loadNext();
}
- loadNext
будет проверять, имеются ли изображения, которые
еще должны быть загружены, в массиве
pictureObjects (если
picLoading меньше,
чем длина массива). Если еще остался рисунок для
загрузки, он будет загружен в фильм,
расположенный вне рабочего места; в противном
случае этот фильм будет удален, а
statusClip
постепенно исчезнет с использованием
tweenTo.
После плавного исчезновения фильма будет вызвана
setUpStage -
функция, устанавливающая интерфейс для просмотра
рисунков. Приведем код, который мы ввели под
нашей функцией
pictureDataComplete.
function loadNext() {
if (picLoading<pictureObjects.length)
{
// Load the next picture using the
file property
picturePlacebo.loadMovie(pictureObjects[picLoading].file);
} else {
delete
this.onEnterFrame;
var obj =
{_alpha:0};
picturePlacebo.removeMovieClip();
_root.statusClip.tweenTo(obj,
9, _root, "setUpStage");
}
}
- Функция
checkPreload
работает во многом так же, как и загрузчик XML.
Она создает переменную
percent и устанавливает ее по
getBytesLoaded
внешнего (находящегося вне рабочего места)
фильма, а затем отображает процентное значение в
фильме status.
После полной загрузки рисунка (когда процент
равен 100) она сначала сохраняет ширину и высоту
фильма (которые равны ширине и высоте рисунка
внутри фильма) в нашем массиве
pictureObjects.
Затем она увеличивает переменную
picLoading и
вызывает loadNext.
Мы расположили эту функцию под
loadNext.
function checkPreload() {
var percent =
Math.round(picturePlacebo.getBytesLoaded()
К/picturePlacebo.getBytesTotal()*100);
if (percent<=0) {
percent =
"0";
}
_root.statusClip.statusText.text =
"PICTURE"+(picLoading+1)+"
КLOADING \n"+percent+"%";
if (percent == 100) {
// store picture's width and height
_root.pictureObjects[picLoading].width
=
_root.picturePlacebo._width;
_root.pictureObjects[picLoading].height
=
_root.picturePlacebo._height;
picLoading++;
loadNext();
}
}
Приведем окончательный код.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.filename = "pictures.xml";
embedder._visible = 0;
statusTf = new TextFormat("Arial", 40, 0x4375A6);
statusTf.align = "center";
statusTf.leading = -5;
function createStatusMovie(tex) {
_root.createEmptyMovieClip("statusClip",
1);
statusClip.createTextField("statusText", 1,
0, 0, 550, 100);
statusClip.statusText.embedFonts =
true;
statusClip.statusText.selectable =
false;
statusClip.statusText.setNewTextFormat(statusTf);
statusClip.statusText.text = tex;
statusClip._x = 0;
statusClip._y = (Stage.originalHeight
statusClip.statusText.textHeight)/2-50;
}
function importXml() {
trace("data imported from xml");
pictureObjects = [];
myXml = new Xml();
myXml. ignoreWhite = true;
myXml.updateXmlStatus = function() {
var percent =
Math.round(this.getBytesLoaded()/this.getBytesTotal()*100);
if
(percent<=0) {
percent = "0";
}
_root.StatusClip.statusText.text
= "PICTURE DATA LOADING \n"+percent+"%";
};
myXml.updater = setInterval(myXml, "updateXmlStatus",
20);
myXml.onLoad = parseMe;
myXml.load(_root.filename);
_root.createStatusMovie("PICTURE DATA
LOADING");
}
function parseMe(success) {
if (success) {
var pictures
= this.firstChild.childNodes;
for (var i =
0; i<pictures.length; i++) {
var obj = {};
for (var j in pictures[i].attributes) {
obj[j] = pictures [i].attributes [j];
}
pictureObjects.push(obj);
}
_root.refreshSharedObj();
clearlnterval(this.updater);
statusClip.removeMovieClip();
_root.pictureDataComplete();
} else {
_root.StatusClip.statusText.text
= "FAILED TO LOAD DATA";
clearlnterval(this.updater);
}
}
function refreshSharedObj() {
mySharedObj =
sharedobject.getLocal("denim");
mySharedObj.data.pictureObjects =
pictureObject s;
var dat = new Date();
mySharedObj.data.timeStamp =
dat.getTime();
mySharedObj.flush();
}
function checkShared() {
mySharedObj -
sharedobject.getLocal("denim");
if (mySharedObj.data.pictureObjects)
{
var dat = new
Date();
if
(dat.getTime()-mysharedObj.data.timeStamp<604800000)
{
trace("data found in shared object");
_root.pictureObjects =
mySharedObj.data.pictureObjects;
pictureDataComplete();
return true;
}
}
}
function pictureDataComplete() {
_root.createStatusMovie("PICTURES
LOADING");
_root.createEmptyMovieClip("picturePlacebo",
2);
picturePlacebo._x = 1000;
picLoading = 0;
this.onEnterFrame = checkPreload;
loadNext();
}
function loadNext() {
if (picLoading<pictureObjects.length)
{
picturePlacebo.loadMovie(pictureObjects[picLoading].file);
} else {
delete
this.onEnterFrame;
var obj =
{_alpha:0};
picturePlacebo.removeMovieClip();
_root.statusClip.tweenTo(obj , 9, _root,
"setUpStage");
}
}
function checkPreload() {
var percent =
Math.round(picturePlacebo.getBytesLoaded()/
КpicturePlacebo.getBytesTotal()*100);
if (percent<=0) {
percent =
"0";
}
_root.statusClip.statusText.text =
"PICTURE "+(picLoading+1)+"
КLOADING \n"+percent+"%";
if (percent == 100) {
_root.pictureObjects[picLoading].width =
_root.picturePlacebo._width;
_root.pictureObjects[picLoading].height =
_root.picturePlacebo._height ;
picLoading++;
load Next();
}
}
function init() {
if (!checkShared()) {
importXml();
}
}
init();
Пример
5.12.
- Сохраните ваш
файл под именем
preloadPictures.fla. При его запуске вы
увидите следующее сообщение.
В
дополнение к этому, если вы выберете Debug >
List Variables, в окне Output также отобразится,
что все ваши рисунки загружены.
Внесем последние добавления к нашему интерфейсу
- настроим рабочее место таким образом, чтобы
могли отображаться фотографии, а потом определим,
как именно они будут отображаться.
Настройка рабочего
места
Мы
реализуем отображение каждой фотографии по центру
рабочего места, при этом маска будет перемещаться
слева направо, постепенно показывая изображение. Как
только маска одной фотографии переместится и откроет
ее, маска для предыдущей фотографии также
переместится и в тот же момент скроет эту фотографию,
что создаст эффект двигающейся пленки.
- Первое, что
нужно сделать при вызове
setUpStage - это удалить
statusClip. После
этого мы создадим строку кнопок для вызова
нужных фотографий.
function setUpStage() {
_root.statusClip.removeMovieClip();
// create the buttons
createButtons();
// trigger the first photo
triggerPhoto(0);
}
- Для создания
кнопок мы будем использовать компонент
nooButton,
созданный в предыдущей лекции, поэтому убедитесь,
что в Library имеется его копия (вы можете
заимствовать ее из полной версии этого
упражнения в файле с именем
finalVersion.fla).
Мы добавим объект
textFormat для кнопок в верхней части
нашего кода. Приведем новую строку кода,
выделенную жирным шрифтом.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.filename = "pictures.xml";
embedder._visible = 0;
buttonTf = new TextFormat("Arial", 20, 0x4375A6);
statusTf = new TextFormat("Arial", 40, 0x4375A6);
- Функция
createButtons (расположенная
нами под setUpStage)
знакома вам из предыдущей лекции. Мы создаем
объект для установки параметров новой кнопки, ее
внешнего вида и действия. На этот раз мы
присваиваем функцию
triggerPhoto управляющему элементу
события press,
которая будет передавать функции номер
фотографии.
function createButtons(imageArr, tF) {
_root.createEmptyMovieClip("buttonHolder",
100000);
var currx;
for (var i = 0; i<pictureObjects.length;
i++) {
_root.buttonHolder.depth
= i + 1;
var caption =
(i+1).toString().length<2 ? "00"+(i +1) :
"0"+(i +1);
var obj = {myText:caption,
_x:currx, obj:_root, defaultBright:0,
rollOverBright:30,
КvisitedBright:60, activeBright:-30,
pressFunction:"triggerPhoto",
КpressArgs:i,
tf:buttonTf, embedFonts:true};
var nooButton
= buttonHolder.attachMovie("nooButtonMc", "noob"+i,
i, obj);
currX +=
nooButton.width+5;
}
// set the first button to active
buttonHolder.noob().setActive();
buttonHolder._x = (Stage.originalWidth-buttonHolder._width)/2;
buttonHolder._y =
Math.round(Stage.bottom)-buttonHolder._height;
// move to the bottom of the stage
onResize
buttonHolder.onResize = function() {
var у =
Math.round(Stage.bottom)-30;
this.tweenTo({_y:y}, 3);
};
Stage.addListener(buttonHolder);
Stage.onResize();
buttonHolder.onResize();
}
Пример
5.13.
Если на данном этапе запустить фильм, кнопки
загрузятся в нижней части рабочего места.
Мы
использовали несколько новых элементов во второй
части функции
createButtons и сейчас рассмотрим их
поподробнее.
Прежде всего, мы вызываем метод
setActive первой
кнопки. Это кнопка, соответствующая первой
загруженной фотографии, поэтому она должна быть
отключена в начале.
buttonHolder.noob0.setActive();
Затем мы используем объект
Stage для
установки позиции фильма
buttonHolder таким образом, что он
отцентрирован по горизонтали и расположен прямо
над нижнем краем.
buttonHolder._x = (Stage.originalWidth-buttonHolder._width)/2;
buttonHolder._y = Math.round (Stage.bottom)-buttonHolder._height;
Мы
хотим, чтобы кнопки оставались внизу окна
браузера или проигрывателя, поэтому добавляем
функцию onResize
для перемещения кнопки на нужное место.
buttonHolder.onResize = function() {
var у = Math.round(Stage.bottom)-30;
this.tweenTo({_y:у}, 3);
};
Stage.addListener(buttonHolder);
Наконец, добавляем вызовы
Stage.onResize и
buttonHolder.onResize для проверки того,
что даты были корректны в момент создания кнопок.
Это необязательно, но без обеспечения этой
возможности значения
Stage не будут корректно обновляться, и
позиционирование будет работать неправильно.
Теперь мы готовы приступить к созданию последней
части нашего интерфейса и настроить его так,
чтобы можно было просматривать фотографии на
рабочем месте, а также выбирать способ их
отображения.
Отображение
фотографий
Для
применения механизма маски при отображении
фотографий создадим пары фильмов. При вызове новой
фотографии мы создаем фильм, в котором будет
содержаться фотография, а также маску, которая будет
на ней. При вызове фотографии маска будет полностью
открывать фотографию. В тот же момент маска, которая
была расположена над верхней границей предыдущей
фотографии, переместится через рабочее место для ее
сокрытия. Когда маска будет целиком вне рабочего
места, а фотография полностью скрыта, старые маска и
фотография будут удалены.
- Каждая пара
фотографий будет создаваться под предыдущей
парой, поэтому мы добавляем переменную
depth после
выражений include,
которая будет начинаться со значения 1000 и
будет постепенно уменьшаться.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.depth = 1000;
_root.filename = "pictures.xml";
- Теперь мы
создаем фильм, который будет использоваться для
маски. Дайте ему имя mask и внутри него
нарисуйте квадрат 100х100 пикселей.
- Щелкните
правой кнопкой мыши на mask в Library и выберите
Linkage: из меню для отображения диалогового
окна Linkage Properties. Выберите опцию Export
for ActionScript и в поле
Identifier введите mask.
- Функция
triggerPhoto обеспечит несколько возможностей.
function triggerPhoto
(currentPicture) {
Во-первых, она будет определять объект рисунка,
который будет использоваться, путем поиска в
массиве pictureObjects значения, переданного
функции.
function triggerPhoto(currentPicture) {
var nextPicObj =
pictureObjects[currentPicture];
Затем она создаст пустой фильм для содержания в
нем рисунка. Далее внутри него будет создан
другой фильм, в который будет непосредственно
загружаться рисунок. Это делается потому, что
когда фильм содержит загруженный рисунок или
иной SWF, удаляются многие элементы, такие как
элементы управления
onEnterFrame и (что более подходит к
нашему случаю) маскирование данных.
Располагаем фильм по центру рабочего места с
использованием данных о ширине и высоте при
предварительной загрузке изображений, и далее
значительно изменяем размер фильма для
обеспечения развертывания изображения.
_root.depth--;
var noopic = createEmptyMovieClip("picClip"+depth, depth);
noopic.createEmptyMovieClip("inner", 1);
noopic.inner.loadMovie(nextPicObj.file);
nooPic._xscale = nooPic._yscale = 101;
nooPic.inner._x = (Stage.originalWidth-nextPicObj.width)/2;
nooPic.inner._y = (Stage.originalHeight-nextPicObj.height)/2;
После этого создаем максу, которая будет
располагаться по левому краю рабочего стола и
растягиваться по высоте фильма. Внутри маски мы
устанавливаем ссылку на соответствующий фильм с
рисунком, чтобы при удалении маски изображение
также могло быть удалено.
_root.depth--;
var nooMask = _root.attachMovie("mask", "mask"+depth, depth);
nooMask._x = 0;
nooMask._yscale = Stage.originalHeight;
nooMask._xscale = 1;
nooMask.myPic = nooPic;
nooPic.setMask(nooMask);
Мы
будем постепенно изменять размер маски для
покрытия всего рабочего места.
noMask.tweenTo
({_xscale:Stage.originalWidth+200}, 15);
Следующим шагом выясним, находится ли уже
рисунок на рабочем месте, и вызовем функцию
maskOut, если это
так.
if
(_root.latestMask) {
maskOut();
}
После этого мы устанавливаем
latestMask на
текущую маску, указывая, что она будет удалена
следующей.
_root.latestMask =
nooMask;
Наконец, проверяем, опустилось ли значение
depth ниже
величины 900. Если да, то возвращаем значение на
1000 для предотвращения перезаписи каких-либо
элементов на рабочем столе.
if
(_root.depth<900) {
_root.depth =
1000;
}
Приведем полный код функции.
function triggerPhoto(currentPicture) {
var nextPicObj =
pictureObjects[currentPicture];
_root.depth--;
var noopic =
createEmptyMovieClip("picClip"+depth,
depth);
noopic.createEmptyMovieClip("inner",
1);
noopic.inner.loadMovie(nextPicObj.file);
nooPic._xscale = nooPic._yscale=101;
nooPic.inner._x = (Stage.originalWidth-nextPicObj.width)/2;
nooPic.inner._y = (Stage.originalHeight-nextPicObj.height)/2;
_root.depth--;
var nooMask = _root.attachMovie("mask",
"mask"+depth, depth);
nooMask._x = 0;
nooMask._yscale =
Stage.originalHeight;
nooMask._xscale = 1;
nooMask.rayPic = nooPic;
nooPic.setMask(nooMask);
nooMask.tweenTo((_xscale:Stage.originalWidth+200},
15);
if (_root.latestMask) {
maskOut();
}
_root.latestMask = nooMask;
if (_root.depth<900) {
_root.depth =
1000;
}
}
- Теперь нам
нужно создать функцию
maskOut, которая будет перемещать
предыдущую маску за пределы рабочего места.
Имейте в виду, что мы перемещаем этот фильм на
ту же самую позицию (Stage.originalWidth+200),
на которую была перемещена первая маска. При
этом открывающая и скрывающая маски появляются и
исчезают примерно с одинаковой скоростью.
function maskOut() {
_root.latestMask.tweenTo({_x:Stage.originalWidth+200},
15, _root,
"remove");
}
- По достижении
фильмом конечной точки вызывается функция
remove, которая удаляет его вместе с
соответствующим рисунком.
function remove(maskMc) {
maskMc.myPic.removeMovieClip();
maskMc.removeMovieClip();
}
Создание кода завершено, однако он слишком велик,
чтобы приводить его целиком в этой книге,
поэтому ниже мы приведем только основные
дополнения, сделанные в двух последних
параграфах. Они выделены жирным шрифтом.
#include "tweento.as"
#include "brightness.as"
#include "stage.as"
_root.depth = 1000;
_root.filename = "pictures.xml";
embedder._visible = 0;
buttonTf = new textFormat("Arial", 20, 0x4375A6);
statusTf = new TextFormat("Arial", 40, 0x4375A6);
//rest of code goes here
function createButtons(imageArr, tF) {
_root.createEmptyMovieClip("buttonHolder",
100000);
var currx;
for (var i = 0; i<pictureObjects.length;
i++) {
_root.buttonHolder.depth
= i+1;
var caption =
(i+1) .toString().length<2 ? "00"+(i+1) :
"0"+(i+1);
var obj = {myText:caption,
_x:currx, obj:_root,
КdefaultBright:0, rollOverBright:30,
visitedBright:60,
КactiveBright:-30,
pressFunction:"triggerPhoto",
К-pressArgs:i,
tf:buttonTf, embedFonts:true};
var nooButton
= buttonHolder.attachMovie ("nooButtonMc",
К"noob"+i, i,
obj);
currX +=
nooButton.width+5;
}
buttonHolder.noob().setActive();
buttonHolder._x = (Stage.originalWidth-buttonHolder._width)/2;
buttonHolder._y =
Math.round(Stage.bottorn)-buttonHolder._height;
buttonHolder.onResize = function() {
var у =
Math.round (Stage.bottom) -30;
this.tweenTo({_y:y}, 3);
};
Stage.addListener(buttonHolder);
Stage.onResize();
buttonHolder.onResize();
}
function triggerPhoto(currentPicture) {
var nextPicObj =
pictureObjects[currentPicture];
_root.depth--;
var noopic =
createEmptyMovieClip("picClip"+depth,
depth);
noopic.createEmptyMovieClip("inner",
1);
noopic.inner.loadMovie(nextPicObj.file);
nooPic._xscale = nooPic._yscale=101;
nooPic.inner._x = (Stage.originalWidth-nextPicObj.width)/2;
nooPic.inner._y = (Stage.originalHeight-nextPicObj.height)/2;
_root.depth--;
var nooMask = _root.attachMovie("mask",
"mask"+depth, depth);
nooMask._x = 0;
nooMask._yscale =
Stage.originalHeight;
nooMask._xscale = 1;
nooMask.myPic = nooPic;
nooPic.setMask(nooMask);
nooMask.tweenTo((_xscale:Stage.originalWidth+200},
15);
if (_root.latestMask) {
maskOut();
}
_root.latestMask = nooMask;
if (_root.depth<900) {
_root.depth =
1000;
}
}
function maskOut() {
_root.latestMask.tweenTo({_x:Stage.originalWidth+200},
15, _root,
К"remove");
}
function remove(maskMc) {
maskMc.myPic.removeMovieClip();
maskMc.removeMovieClip();
}
function init() (
if (!checkShared()) {
importXml(};
}
}
init();
Пример
5.14.
- Сохраните
файл с именем
finalVersion.fla и запустите его. Вы
сначала увидите сообщение о загрузке рисунков.
Затем оно исчезнет перед появлением кнопок при
отображении первой картинки.
Загрузка рисунка завершена
|
Рисунок 1 появляется во время
перемещения кнопок на их места
|
Настройка рабочего места почти
завершена
|
Рисунок 1 исчезает, а рисунок 3
появляется
|
Таким образом, при создании этого интерфейса вы
изучили намного больше, чем при работе с двумя
предыдущими версиями. Здесь рисунки загружаются
динамически, а с использованием XML можно
выбирать рисунки для загрузки без изменения FLA.
Мы также минимизировали время инициализации,
использовав sharedObjects
для кэширования созданного при обработке XML
массива. Интерфейс может работать с массивами
различного размера, хотя массивы, с которыми мы
работали, были одинаковы по величине. Даже если
вы будете быстро щелкать на разных кнопках,
фотографии по-прежнему будут плавно скользить
через экран. При помощи системы обратных связей
можно изменить работу масок так, чтобы
фотографии появлялись только после исчезновения
других. Данные заголовка, которые мы включили в
XML, можно использовать для добавления названий
рисунков.
|