В этой лекции мы рассмотрим еще один интерфейс просмотра изображений. Мы научимся отображать динамическую загрузку изображений, а также сохранять список изображений отдельно от фильма Flash, чтобы изменять сайт без повторного открытия FLA.
Для динамической загрузки рисунков потребуется создать slideTo, созданный ранее, чтобы изменять размер области просмотра. Мы также научимся применять объект 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 установки по умолчанию.

Flash MX имеет еще одну возможность настройки параметров публикации Publishing Settings, которая разрешает пользователю настраивать размер браузера или окна проигрывателя Flash, причем объекты на рабочем месте будут также изменять свои размеры. Разберем, как реализовать эту возможность.
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, чтобы возвращать эти значения. Прежде всего, разберемся, как будут проходить вычисления значений.
//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 пикселей с точкой закрепления в левом верхнем углу. Фильмы скобок доступны в исходных файлах на компакт-диске.Чтобы перемещать эти фильмы на свои места при изменении размеров рабочего места, нужно создать для них приемники. Объект 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 = function() {
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;
};

Все работает как нужно, но не оптимально. Каждый раз, когда надо найти левую координату рабочего места, мы вызываем функцию 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;
};
Сейчас мы продемонстрируем, как можно добавить движение на примере скольжения уголков на свои места. Для этого нам понадобится скопировать созданную нами в лекции 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();
}
};
};
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);
};
Если использовать этот подход к изменению размеров рабочего места, то Flash Player придется выполнять гораздо больший объем кода. Если в проекте много движущихся объектов, в особенности с эффектами прозрачности, Flash Player может "тормозить", поэтому подумайте, стоит ли его использовать.
Метод slideTo перемещает объект из одного места в другое. Теперь мы немного изменим метод slideTo для создания метода scaleTo, который будем применять с объектом Stage для создания фоновой панели изменяемого размера, которая будет изменяться с увеличением или уменьшением фильма и находиться по центру под названием.
stageObject3.fla и сохраните его в файле resize001.fla. Теперь нарисуйте серый квадрат размером 100х100 пикселей и преобразуйте его в фильм с центральной точкой регистрации (закрепления).background panel, который размещается под слоем, содержащим заголовок "center stage".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 (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();
}
};
};
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);
};
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 можно было бы создать отдельный класс , унаследовав его из класса 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();
}
};
};
Можно использовать Object.registerClass для связывания любого отдельного символа фильма с данным классом. Тогда вы могли бы добавить дополнительные методы к этому классу для добавления параметров к .
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, и чтобы не нужно было добавлять управляющий фильм.
Теперь мы приступим к компиляции окончательной версии нашего сайта. Если мы хотим сделать представление изображений динамическим, имеет смысл сохранять список рисунков отдельно от фильма 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.xm l в браузере, вы увидите иерархическое описание информации.

Заметьте, что здесь есть значок +/-, на котором вы можете щелкать для открытия или закрытия структуры документа и вывода связанной информации. Сам по себе этот документ не представляет особого интереса. Данные XML становятся полезными лишь тогда, когда они обрабатываются каким-либо другим приложением.
Разберемся, как можно использовать эти данные во Flash.
Структуру XML нужно импортировать во Flash и затем преобразовать информацию в данные, которые можно использовать, т.е. в массив объектов изображений.
myXml = newXml();
myXml.load ("pictures.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;
}
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;
}
В данном случае это не очень важно, так как мы не собираемся в дальнейшем осуществлять управление документом XML, но нужно понимать, что происходит, когда объекты определяются посредством ссылки на другие объекты, а не в своих собственных терминах. Наш исходный массив содержал только указатель на объект атрибутов в узле XML, так что мы просто скопировали в него все параметры. Другой способ - удалить объект myXml, чтобы разрушить исходный объект attributes и оставить значение в массиве в виде единственной ссылки на этот объект.
Интерфейс, создаваемый нами, будет составной частью сайта, поэтому добавим загрузчик данных 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();
}
};
};
Подключение внешнего кода: 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();
Теперь внешние файлы 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;

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. Мы можем использовать такую же функцию для создания загрузчика отдельных изображений.
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 .
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();
Если вы запустите файл без его сохранения, вы получите сообщение FAILED TO LOAD DATA, т.к. FLA и pictures.xml не сохранены в той же папке. Сохраните фильм в файле xmlLoader.fla и запустите его снова. На этот раз, как мы и планировали, будет выведено сообщение PICTURE :

Одной из прекрасных возможностей, связанных с общими объектами, является возможность сохранять внутри них любые данные. Благодаря этому можно не импортировать каждый раз XML, содержащий имена файлов изображений, а сохранить массив рисунков локально. Мы также можем установить срок обновления для общего объекта, после которого будет выполняться повторный импорт XML в случае его изменения.
Методология этого подхода очень проста. Перед импортом XML с помощью sharedObject.getLocal мы проверяем, создан ли общий объект для фильма. Если не создан, или общий объект является более старшим, чем определенная дата, мы продолжим работу и импортируем XML. После импорта и обработки XML мы будем сохранять массив pictureObjects и текущее время в общем объекте. Если у нас уже есть общий объект, мы получим массив pictureObjects из общего объекта, а не из 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();
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();
Мы добавили два выражения 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();
}
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();
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();
}
Если на данном этапе запустить фильм, кнопки загрузятся в нижней части рабочего места.

Мы использовали несколько новых элементов во второй части функции 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";
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");
}
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();



Таким образом, при создании этого интерфейса вы изучили намного больше, чем при работе с двумя предыдущими версиями. Здесь рисунки загружаются динамически, а с использованием XML можно выбирать рисунки для загрузки без изменения FLA. Мы также минимизировали время инициализации, использовав sharedObjects для кэширования созданного при обработке XML массива. Интерфейс может работать с массивами различного размера, хотя массивы, с которыми мы работали, были одинаковы по величине. Даже если вы будете быстро щелкать на разных кнопках, фотографии по-прежнему будут плавно скользить через экран. При помощи системы обратных связей можно изменить работу масок так, чтобы фотографии появлялись только после исчезновения других. Данные заголовка, которые мы включили в XML, можно использовать для добавления названий рисунков.
В этой лекции мы рассмотрим еще один интерфейс просмотра изображений. Мы научимся отображать динамическую загрузку изображений, а также сохранять список изображений отдельно от фильма Flash, чтобы изменять сайт без повторного открытия FLA.
Для динамической загрузки рисунков потребуется создать slideTo, созданный ранее, чтобы изменять размер области просмотра. Мы также научимся применять объект 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 установки по умолчанию.

Flash MX имеет еще одну возможность настройки параметров публикации Publishing Settings, которая разрешает пользователю настраивать размер браузера или окна проигрывателя Flash, причем объекты на рабочем месте будут также изменять свои размеры. Разберем, как реализовать эту возможность.
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, чтобы возвращать эти значения. Прежде всего, разберемся, как будут проходить вычисления значений.
//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 пикселей с точкой закрепления в левом верхнем углу. Фильмы скобок доступны в исходных файлах на компакт-диске.Чтобы перемещать эти фильмы на свои места при изменении размеров рабочего места, нужно создать для них приемники. Объект 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 = function() {
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;
};

Все работает как нужно, но не оптимально. Каждый раз, когда надо найти левую координату рабочего места, мы вызываем функцию 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;
};
Сейчас мы продемонстрируем, как можно добавить движение на примере скольжения уголков на свои места. Для этого нам понадобится скопировать созданную нами в лекции 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();
}
};
};
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);
};
Если использовать этот подход к изменению размеров рабочего места, то Flash Player придется выполнять гораздо больший объем кода. Если в проекте много движущихся объектов, в особенности с эффектами прозрачности, Flash Player может "тормозить", поэтому подумайте, стоит ли его использовать.
Метод slideTo перемещает объект из одного места в другое. Теперь мы немного изменим метод slideTo для создания метода scaleTo, который будем применять с объектом Stage для создания фоновой панели изменяемого размера, которая будет изменяться с увеличением или уменьшением фильма и находиться по центру под названием.
stageObject3.fla и сохраните его в файле resize001.fla. Теперь нарисуйте серый квадрат размером 100х100 пикселей и преобразуйте его в фильм с центральной точкой регистрации (закрепления).background panel, который размещается под слоем, содержащим заголовок "center stage".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 (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();
}
};
};
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);
};
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 можно было бы создать отдельный класс , унаследовав его из класса 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();
}
};
};
Можно использовать Object.registerClass для связывания любого отдельного символа фильма с данным классом. Тогда вы могли бы добавить дополнительные методы к этому классу для добавления параметров к .
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, и чтобы не нужно было добавлять управляющий фильм.
Теперь мы приступим к компиляции окончательной версии нашего сайта. Если мы хотим сделать представление изображений динамическим, имеет смысл сохранять список рисунков отдельно от фильма 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.xm l в браузере, вы увидите иерархическое описание информации.

Заметьте, что здесь есть значок +/-, на котором вы можете щелкать для открытия или закрытия структуры документа и вывода связанной информации. Сам по себе этот документ не представляет особого интереса. Данные XML становятся полезными лишь тогда, когда они обрабатываются каким-либо другим приложением.
Разберемся, как можно использовать эти данные во Flash.
Структуру XML нужно импортировать во Flash и затем преобразовать информацию в данные, которые можно использовать, т.е. в массив объектов изображений.
myXml = newXml();
myXml.load ("pictures.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;
}
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;
}
В данном случае это не очень важно, так как мы не собираемся в дальнейшем осуществлять управление документом XML, но нужно понимать, что происходит, когда объекты определяются посредством ссылки на другие объекты, а не в своих собственных терминах. Наш исходный массив содержал только указатель на объект атрибутов в узле XML, так что мы просто скопировали в него все параметры. Другой способ - удалить объект myXml, чтобы разрушить исходный объект attributes и оставить значение в массиве в виде единственной ссылки на этот объект.
Интерфейс, создаваемый нами, будет составной частью сайта, поэтому добавим загрузчик данных 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();
}
};
};
Подключение внешнего кода: 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();
Теперь внешние файлы 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;

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. Мы можем использовать такую же функцию для создания загрузчика отдельных изображений.
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 .
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();
Если вы запустите файл без его сохранения, вы получите сообщение FAILED TO LOAD DATA, т.к. FLA и pictures.xml не сохранены в той же папке. Сохраните фильм в файле xmlLoader.fla и запустите его снова. На этот раз, как мы и планировали, будет выведено сообщение PICTURE :

Одной из прекрасных возможностей, связанных с общими объектами, является возможность сохранять внутри них любые данные. Благодаря этому можно не импортировать каждый раз XML, содержащий имена файлов изображений, а сохранить массив рисунков локально. Мы также можем установить срок обновления для общего объекта, после которого будет выполняться повторный импорт XML в случае его изменения.
Методология этого подхода очень проста. Перед импортом XML с помощью sharedObject.getLocal мы проверяем, создан ли общий объект для фильма. Если не создан, или общий объект является более старшим, чем определенная дата, мы продолжим работу и импортируем XML. После импорта и обработки XML мы будем сохранять массив pictureObjects и текущее время в общем объекте. Если у нас уже есть общий объект, мы получим массив pictureObjects из общего объекта, а не из 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();
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();
Мы добавили два выражения 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();
}
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();
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();
}
Если на данном этапе запустить фильм, кнопки загрузятся в нижней части рабочего места.

Мы использовали несколько новых элементов во второй части функции 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";
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");
}
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();



Таким образом, при создании этого интерфейса вы изучили намного больше, чем при работе с двумя предыдущими версиями. Здесь рисунки загружаются динамически, а с использованием XML можно выбирать рисунки для загрузки без изменения FLA. Мы также минимизировали время инициализации, использовав sharedObjects для кэширования созданного при обработке XML массива. Интерфейс может работать с массивами различного размера, хотя массивы, с которыми мы работали, были одинаковы по величине. Даже если вы будете быстро щелкать на разных кнопках, фотографии по-прежнему будут плавно скользить через экран. При помощи системы обратных связей можно изменить работу масок так, чтобы фотографии появлялись только после исчезновения других. Данные заголовка, которые мы включили в XML, можно использовать для добавления названий рисунков.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.