Программное рисование во Flash MX. Управление кривыми.

Вместо вступления.

Наконец-то!!! Теперь во Flash MX мы можем рисовать по средствам программного кода: создавать и удалять клипы, делать различные градиентные заливки, свободно управлять размером, местоположением и прочими характеристиками текста и, конечно же, рисовать прямые и кривые линии.

Но вслед за восторгом от новшеств вскоре приходят некоторые проблемы. Кривая, заданая тремя точками (начальная является концом предыдущей линии или может быть задана с помощью метода moveTo), ведёт себя совершенно непонятно - через точку (ControlX, ControlY) она даже не проходит, и может возникнуть проблема с рисованием даже простой окружности.

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

Что рисует curveTo().

По сути данная кривая всего-навсего CV NURBS из трёх точек, каждая с весом 1, кривая степени 2. Для тех, кто изучал 3D-Studio MAX R2 или выше или какую-либо другую программу создания 3D-графики и анимации этого достаточно (Poser и Bryce3D в расчёт не беруться, т.к. Я их не видел и наличие там NURBS гарантировать не могу). Для всех остальных потребуются пояснения. Control Vertex Non-Uniform Rational Basic Splines (сокращённо CV NURBS) - это линии, состоящие из частей кривых различного порядка (т.е. заданых математическими уравнениями некоторой степени), форма которых определяется направляющими векторами, концы которых находятся в задаваемых пользователем точках. В Flash MX эти составляющие имеют степень 2, т.е. это могут быть параболы, гиперболы и эллипсы. Но так как вес точки (коэффициэнт, влияющий на то, как близко к этой точке подходит кривая) постоянен и равен единице, то линия, создаваемая curveTo() является частью параболы. Из этого следует, что просто нарисовать обычную окружность или эллипс нельзя, а можно лишь нарисовать кривую, которая будет на них похожа. Ввиду ограниченности кол-ва задаваемых точек, сложную кривую нужно создавать по частям, рассчитывая точки так, чтобы не было изломов. Однако есть и свои плюсы. Направляющие векторы в нашем случае - это касательные к параболе в двух точках её графика, а точка (ControlX, ControlY) - пересечение этих касательных.

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

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

Метод рисование сложной кривой multicurveTo().

Данный метод позволит нам рисовать почти полноценный CV NURBS. Почти, потому что мы не сможем менять вес точки (всегда равный единице) и устанавливать степень кривой больше 2. Будет ещё одна оговорка, но о ней чуть позднее. Итак, программный код метода:

function Multicurve(Xargs, Yargs, closed)
{
	var Xmid = Xargs.slice(0);
	var Ymid = Yargs.slice(0);
	if ((Xmid.length != Ymid.length) || (Xmid.length < 2))
	{
		trace('Wrong Arguments');
		return this
	};

	if (Xmid.length == 2)
	{
		this.moveTo(Xmid[0], Ymid[0]);
		this.lineTo(Xmid[1], Ymid[1]);
		delete Xmid;
		delete Ymid;
		return this;
	};

	var Xpoint = new Array();
	var Ypoint = new Array();
	for (var i = 1; i < Xmid.length-2; i++)
	{
		Xpoint[i] = 0.5*(Xmid[i+1]+Xmid[i]);
		Ypoint[i] = 0.5*(Ymid[i+1]+Ymid[i]);
	};

	if (closed)
	{
		Xpoint[0] = 0.5*(Xmid[1]+Xmid[0]);
		Ypoint[0] = 0.5*(Ymid[1]+Ymid[0]);
		Xpoint[i] = 0.5*(Xmid[i+1]+Xmid[i]);
		Ypoint[i] = 0.5*(Ymid[i+1]+Ymid[i]);
		Xpoint[i+1] = 0.5*(Xmid[i+1]+Xmid[0]);
		Ypoint[i+1] = 0.5*(Ymid[i+1]+Ymid[0]);
		Xmid[i+2] = Xmid[0];
		Ymid[i+2] = Ymid[0];
		Xpoint[i+2] = Xpoint[0];
		Ypoint[i+2] = Ypoint[0];
	}
	else
	{
		Xpoint[0] = Xmid[0];
		Ypoint[0] = Ymid[0];
		Xpoint[i] = Xmid[i+1];
		Ypoint[i] = Ymid[i+1];
		Xmid.pop();
		Ymid.pop();
	};

	this.moveTo (Xpoint[0], Ypoint[0]);
	for (var i = 1; i < Xmid.length; i++)
	{
		this.curveTo(Xmid[i], Ymid[i], Xpoint[i], Ypoint[i]);
	};

	delete Xmid;
	delete Ymid;
	delete Xpoint;
	delete Ypoint;
	return this;
}

Object.prototype.multicurveTo = Multicurve;

Теперь разберём всё по порядку:
function Multicurve(Xargs, Yargs, closed){

В качестве аргументов передаём два массива - координаты контрольных точек кривой по х и по у, а также параметр closed, который показывает, какую мы хотим получить кривую. Если кривая замкнута, то линия не имеет начала и конца, все заданные точки используются как контрольные (значение closed равно true), в противном случае нулевая (исходя из индекса в массиве) и последняя заданные точки являются началом и концом кривой(значение closed равно false). Можно было бы сделать проверку на замкнутость автоматической, но иногда бывают случаи, когда нужно, чтобы линия имела совпадающие начало и конец.
var Xmid = Xargs.slice(0);
var Ymid = Yargs.slice(0);

Возможно, Вам в дальнейшем потребуются массивы, которые вы задали как параметры функции. Если производить с аргументами Xargs и Yargs какие-нибудь преобразования, то изменятся и сами передаваемые массивы. Поэтому создаются их локальные копии (на локальность указывает слово var). Если Вы уверены, что передаваемые массивы нигде больше использоваться не будут, можно удалить эти две строчки, а первую строку записать как function Multicurve(Xmid, Ymid, closed){.
if ((Xmid.length != Ymid.length) || (Xmid.length < 2)){
trace('Wrong Arguments');
return this
};

Проверка на отсутствие ошибок. Если задано меньше двух точек, или количество координат по X не равно количеству координат по Y, то метод заканчивается с передачей ссылки на себя и выводит в режиме теста соответствующее сообщение. Если проверка наличия хотя-бы двух точек - простая формальность, то проверка различного числа координат может быть весьма полезной. При желании строчку trace('Wrong Arguments'); можно заменить на что-нибудь вроде trace('Wrong Arguments: '+(Xmid.length-Ymid.length)), чтобы знать, сколько и каких координат не достаёт. if (Xmid.length == 2){ this.moveTo(Xmid[0], Ymid[0]); this.lineTo(Xmid[1], Ymid[1]); delete Xmid; delete Ymid; return this; };

Ещё одна проверка. Если задано всего две точки, то мы просто рисуем соединяющий их отрезок и заканчиваем выполнение метода. Данная проверка обязательна, т.к. иначе используемый далее метод curveTo() просто не получит достаточное количество параметров. Слово this используется для того, чтобы данный метод, применённый к любому клипу перенимал все его свойства и действовал в его пределах. Обращаю Ваше внимание на следующие строки:
delete Xmid;
delete Ymid;

В них удаляются из памяти локальные массивы. В ActionScript эти строчки не обязательны, т.к. "сборщик мусора" сам с ними разберётся.

Теперь вся подготовительная работа выполнена и можно начинать создание массивов данных для построения кривой:
var Xpoint = new Array();
var Ypoint = new Array();
for (var i = 1; i < Xmid.length-2; i++){
Xpoint[i] = 0.5*(Xmid[i+1]+Xmid[i]);
Ypoint[i] = 0.5*(Ymid[i+1]+Ymid[i]);
};

Создаём ещё два локальных массива, Xpoint и Ypoint, которые будут содержать координаты всех якорных точек. Далее в цикле происходит рассчёт координат этих якорных точек. Здесь и появляется та самая оговорка. В оригинале в CV NURBS координаты таких точек зависят от того, насколько далеко от концов кривой они находятся. Но подобный способ получения координат во Flash себя не оправдывает, и вот почему. В нашем методе мы создаём якорные точки на середине отрезка, соединяющего соседние контрольные точки. Максимальное отклонение "реального" местоположения якорных точек от середины отрезка меньше 0,58 % длины самого отрезка. Так что этим вполне можно пренебречь. Отмечу, что рассчёт координат происходит для точек с первой по предпоследнюю, так как координаты первой и последней рассчитываются в зависимости от того, замкнута кривая или нет.
if (closed){
Xpoint[0] = 0.5*(Xmid[1]+Xmid[0]);
Ypoint[0] = 0.5*(Ymid[1]+Ymid[0]);
Xpoint[i] = 0.5*(Xmid[i+1]+Xmid[i]);
Ypoint[i] = 0.5*(Ymid[i+1]+Ymid[i]);
Xpoint[i+1] = 0.5*(Xmid[i+1]+Xmid[0]);
Ypoint[i+1] = 0.5*(Ymid[i+1]+Ymid[0]);
Xmid[i+2] = Xmid[0];
Ymid[i+2] = Ymid[0];
Xpoint[i+2] = Xpoint[0];
Ypoint[i+2] = Ypoint[0];
}

Если кривая замкнута, то мы создаём нулевую якорную точку на середине отрезка между нулевой и первой контрольными точками, последнюю - между предпоследней и последней. И нам потребуются координаты ещё нескольких якорных точек: точки, находящейся на середине отрезка, соединяющего последнюю контрольную точку с первой, и копии координат нулевых якорной и контрольной точек. Это делается для того, чтобы объединить в один код рисование замкнутой и не замкнутой кривых. Используемая в индексах переменная i после окончания цикла имела значение Xmid.length-2, поэтому, следуя нашей организации массивов, Xpoint, Ypoint[i] - это координаты якорной точки, находящейся между предпоследней и последней контрольными (если здесь вообще уместно говорить, какая точка первая, а какая - последняя), Xpoint[i+1], Ypoint[i+1] - между последней и первой, Xpoint[i+2], Ypoint[i+2] - копии координат. [AS3]} else { Xpoint[0] = Xmid[0]; Ypoint[0] = Ymid[0]; Xpoint[i] = Xmid[i+1]; Ypoint[i] = Ymid[i+1]; Xmid.pop(); Ymid.pop(); };
Если же кривая не замкнута, то мы просто делаем нулевую и последнюю контрольные точки нулевой и последней якорной. Снова используем переменную i: Xmid[i+1], Ymid[i+1] являются поледними элементами массивов, т.к. i+1 = Xmid.length-1. Далее мы удаляем последний элемент в массивах координат контрольных точек, но оставляем первый затем, чтобы каждой паре якорных точек [i-1], соответствовала [i]-ая контрольная точка, как и в случае замкнутой кривой. Нахождение координат точек завершено и начинается процесс рисования кривой: [AS3]this.moveTo (Xpoint[0], Ypoint[0]); for (var i = 1; i < Xmid.length; i++){ this.curveTo(Xmid[i], Ymid[i], Xpoint[i], Ypoint[i]); };
Сначала указываем начальную точку.

Затем начинаем рисовать кривые, где для каждой i-ой кривой начальная точка имеет координаты (Xpoint[i-1], Ypoint[i-1]), контрольная - (Xmid, Ymid[i]), а конечная - (Xpoint[i], Ypoint[i]). Искомая линия построена!!! [AS3]delete Xmid; delete Ymid; delete Xpoint; delete Ypoint; return this; }
Удаление массивов и окончание метода. Без комментариев.

В последней строчке Object.prototype.multicurveTo = Multicurve; мы добавляем наш метод к набору имеющихся методов класса Object.

В завершении...

Метод описан, можете смело его использовать. Для простоты рекомендую поместить весь программный код в текстовый файл с расширением .as и в каждом новом клипе, где этот метод может пригодиться, в корне (_root) написать строку
#include path

где path - это абсолютный или относительный путь к файлу с кодом. Заметьте, что после #include точка с запятой НЕ ставится. Лично у меня это выглядит так:
#include "D:/Flash/aibdraw.as"

Если для написания программного кода вы используете встроенный во Flash редактор, то, возможно, Вам пригодиться следущее:

Заходите в корневой каталог Flash, далее \First Run\ActionsPanel
Открываете файл ActionsPanel.xml, находите <ifdef mode="FEATURE_DRAWING_API">
Перед </folder> прописываете подобную строчку: <string name="multicurveTo" tiptext="Draws a multipoint curve from the first to the last point" object="MovieClip" text=".multicurveTo(%
, [pointsY], Closed? %)" type="procedure" version="6" />
После этого в левом столбике редактора ActionScript и всплывающих подсказках появится multicurveTo().

"Кривые, кривые, кривые... Это, конечно, хорошо, но как насчёт чего-го конкретного? К примеру, как нарисовать самую элементарную окружность?" Вопрос действительно хороший. Если каждый раз рисовать окружность вручную, задавая поэтапно якорные и контрольные точки curveTo() (а для хорошей точности нужно от 6 до 18 кривых!), то очень скоро можно прийти к выводу, что делается это как-то по другому. И первое, что приходит на ум, написать метод, который бы рисовал окружность по заданному радиусу и координатам центра, говоря другими словами, создать прототип (это математический термин, не надо его путать с понятием прототипа объекта в ООП!).

В двух словах о создании прототипа. Вообще говоря, можно выделить две группы. Представители первой не зависят от параметров. Вызывая метод рисования, Мы всегда получаем один и тот же объект в одном и том же месте клипа. Затем все преобразования делаются по средствам перемещения, маштабирования и поворота клипа, который содержит этот объект. Это может быть полезно, к примеру, при создании кнопок, чекбоксов и т.п. Для создания такого прототипа достаточно просто скопировать код рисования понравившегося объекта в метод, добавив перед вызовами методов lineTo(), curveTo() и т.д. ключевое слово this. Всё просто. Вторая группа поинтереснее. В неё входят прототипы объектов, которым необходимо передавать параметры. К примеру, спираль имеет определённое количество витков, дуга - определённый угол, прямоугольник - радиус закригления углов, звезда - количество лучей и т.д. Если Вам нужна спираль, которая используется несколько раз с различным количеством витков, то бессмысленно создавать отдельные прототипы для каждой спирали, и эффективнее создать прототип, отвечающий всему классу данных спиралей (данных, потому что спирали бывают разные по способу задания и, соответственно, по внешнему виду). Но в подобных случаях простым копированием блока программного кода не обойтись. Потребуется придумать некий алгоритм рисования. При создании алгоритма очень полезной может оказаться математическая формула объекта, если таковая существует. Иногда проще бывает создать прототипы отдельных частей, а затем соединить их вместе.

Есть ещё одна деталь. Иногда (на практике - довольно часто) бывает нужно передавать в качестве параметров угол наклона или маштаб. Это нужно тогда, когда необходимо, чтобы внутри одного клипа было нарисовано несколько объктов разного размера и под разным углом.

Но слова это только слова, поэтому далее Я хотел бы проэллюстрировать всё вышесказанное на конкретном примере.

[B]Метод рисования эллипса ellipseTo().


Итак, окружность или дуга, а лучше эллипс, и хорошо бы, чтоб его можно было его рисовать под углом к осям координат. Немного о самом алгоритме. Если бы у Вас была нарисована окружность, то чтобы получить эллипс Вы бы растянули клип, в котором находится окружность, чтобы повернуть его изменили свойство _rotation, чтобы подвинуть задали новые координаты клипа. Если мы поместим внутрь клипа с координатами (0;0) вместе с окружностью необходимые для её построения контрольные(control) и якорные(anchor) точки, затем растянем этот клип, повернём и переместим, чтобы получить искомый объект, глобальные координаты контрольных и якорных точек и будут искомыми (т.е., если рисовать по точкам с этими координатами кривую, то мы опять же получим искомый объект). Именно таким образом мы будем находить координаты контрольной или якорной точки: сначала для окружности, потом, после растяжения, для эллипса, потом для повёрнутого эллипса, и, наконец, для перемещённого. Теперь, после определения алгоритма осталось самая малость - написать программный код. Ну как, приступаем?

Для начала опишем метод:
movieClip.prototype.ellipseTo = function(){
var t = arguments[0][0] == undefined ? 1 : 0;
var CenterX = arguments[0+t][0];
var CenterY = arguments[0+t][1];
var Dir = 1;
var ARadius;
var BRadius;
var StartAngle = 0;
var EndAngle = 0;
var ARadAngle = 0;

Кому-то это может показаться непонятным: почему в круглых скобках после имени метода ничего нет, откуда взялся массив arguments, да ещё и двумерный, и как при таком описании этот метод вызывать??? Сейчас всё разъясню.

Наш метод - это достаточно мощный инструмент. Как уже было выше сказано, с его помощью можно будет рисовать эллипс (или его часть) под заданным углом к осям координат. А для этого нам нужно передать функции координаты центра, длины радиусов эллипса, начальную и конечную точки дуги (в данном примере эти параметры задаются углами между прямой, проходящей через начало (конец) дуги и центр эллипса, и горизонтальной осью координат) и угол между главной осью эллипса и горизонтальной осью координат. Кроме того, в некоторых случаях бывает нужно не перемещать начальную точку для рисования дуги с помощью moveTo(), а начать рисовать с того места, где закончилась предыдущая линия. Стало быть нужен флаг. Всего восемь параметров. Но, допустим, нам нужна просто окружность, для которой достаточно трёх параметров (координаты центра и радиус). И что тогда делать с остальными параметрами? Поэтому было бы хорошо сделать наш метод таким, чтобы он мог работать с разным количеством параметров.

Наш метод будет иметь 8 аргументов: Dir(флаг, показывающий, используется ли moveTo() в начале рисования и в каком направлении нужно рисовать эллипс) [CenterX, CenterY] (координаты центра), [ARadius, BRadius] (радиусы эллипса), [StartAngle, EndAngle] (начало и конец дуги в градусах) и ARadAngle (угол между главной осью эллипса, длина которой задаётся параметром ARadius, и горизонтальной осью координат). Именно в таком порадке. Из них параметры Dir, [StartAngle, EndAngle] и ARadAngle могут быть не заданы, кроме того, если нужна окружность, вместо массива параметров [ARadius, BRadius] можно передать один параметр Radius. К примеру, следующий набор аргументов
(1, [100, 200], 50, [45, 270])
будет означать, что нужно нарисовать дугу от 45 до 270 градусов, с центром в точке (100, 200) и радиусом 50, moveTo() не требуется и строить её нужно в направлении отсчёта углов. А этот набор
([200, 150], [100, 80], 70)
будет означать, что нужен эллипс с радиусами (100, 80), с центром в точке (200, 150) и наклонённый под углом 70 градусов.

Теперь о массиве arguments. Этот массив создаётся автоматически для каждого метода и содержит все его аргументы. Двумерным он получился потому, что некоторые из передаваемых аргументов в свою очередь являются массивами (к примеру, [CenterX, CenterY]). Я использовал массив arguments, а не описал все эти аргументы в круглых скобках, потому что в программе будет происходить изменение переменных StartAngle и EndAngle, и если бы мы описали их в круглых скобках (..., [StartAngle, EndAngle],...), то изменения отразились бы и на передаваемом массиве. Если же отказаться от использования массива, то нельзя будет сделать автоопределение того, хотим ли мы целую фигуру или только дугу. Кроме того, в любом случае потребуется переход из градусной меры в радианную.

Переменная t принимает значение 1, если был задан флаг Dir и 0, если нет. Если НЕ был, то считается, что moveTo() используется. Эта переменная также показывает, с какого места в массиве arguments следуют все остальные данные. К примеру, если флаг не задан, то массив [CenterX, CenterY] находится в ячейке arguments[0], а если задан - то arguments[1]. Поэтому мы просто прибавляем переменную к номеру ячейки (в этом же примере arguments[0+t]).

И ещё немного о смысле флага Dir и его применении в нашем методе. Зачем он вообще нужен? Дело в том, что если при использовании заливки применить метод moveTo(), то предыдущая линия автоматически замыкается. Этот эффект не всегда желателен, и в этих случаях moveTo() не используется (как правило, оно и не нужно, так как при обресовке границы заливки дуга начинается там, где закончилась предыдущая линия). С другой стороны, когда заливка не используется, и нужно просто нарисовать пару отдельных окружностей, нет никакой необходимости вручную указывать место начала рисования линии. Теперь о значениях, которые в данном примере может принимать флаг. В процессе рисования границы заливки может также оказаться важным, в каком направлении происходит рисование дуги. На практике это проявляется в том, какой конец дуги должен примыкать к уже имеющейся части контура. Поэтому Я решил использовать флаг и для определения направления: значение 1 по направлению отсчёта углов, -1 - против. Значение по умолчанию равно 1, т.е. если moveTo() используется, то рисуем в направлении отсчёта углов.

Итак, мы определили все параметры. Но значения присвоили только пяти, да и то три из них обнулили. Дело в том, что координаты центра [CenterX, CenterY] существуют независимо от того, что мы хотим получить: окружность, эллипс или дугу. Параметрам StartAngle, EndAngle и ARadAngle присваиваются значения "по умолчанию". Почему именно такие, будет видно далее.

В процессе вычислений нам потребуются ещё две переменные, которым при описании также задаём некоторые значения по умолчанию:
var div = 12;
var delta = Math.PI/6;

Далее в программе будет много операций с использованием методов класса Math. Чтобы каждый раз при их вызове не писать конструкцию типа Math.method(), воспользуемся функцией with():
with (Math){
...............
............... //программный код 
...............
}

Функция with здесь действует совершенно также, как и с клипами. Если вызывается метод или переменная без указания родителя, то Flash сначала просматривает все методы и переменные, описанные внутри блока, а затем, если не находит, ищет их в указанном классе (в данном случае Math).

ВАЖНО!!! Использование with облегчает написание кода, но сильно замедляет выполнение программы компьютером. В статье Я использовал with для облегчения восприятия и повышения читабельности кода. Если для Вас высокая скорость не является важной, то можете использовать with, но для повышения производительности лучше пару минут понажимать Ctrl+v и установить везде прямые ссылки. В исходнике, прилагающемся к статье, метод описан без использования with.

Начнём определять значение остальных параметров. Итак, сначала выясним, в каком направлении нужно рисовать, если направление было задано:
if (t == 1 && (arguments[0] == -1 || arguments[0] == 1)){
Dir = arguments[0];
}

Напомню, что если при вызове метода мы задаём флаг Dir, то считается, что начальная точка дуги задаётся концом предыдущей линии и использовать moveTo() для её получения не нужно. Здесь же проверяем, правильно ли задан флаг.

Теперь определим, что мы рисуем - эллипс или окружность:
if (arguments[1+t][0] == undefined){
ARadius = BRadius = arguments[1+t];
} else {
ARadius = arguments[1+t][0];
BRadius = arguments[1+t][1];
};

Проверяем, является ли второй аргумент массивом. Если нет, то значение первого элемента массива, которого нет, естественно равно undefined, задан один радиус и тогда оба радиуса эллипса принимают одно значение. Если да, то переменной ARadius присваиваем значение первого элемента массива, BRadius - значение второго.

Теперь разберёмся с параметрами StartAngle, EndAngle и ARadAngle:
with (Math){ 
if (arguments.length > 2+t){
if (arguments[2+t][0] == undefined){
ARadAngle = PI*arguments[2+t]/180;
} else {
StartAngle = PI*arguments[2+t][0]/180;
EndAngle = PI*arguments[2+t][1]/180;

Первая строчка проверяет, заданы ли ещё какие-нибудь аргументы, кроме первых двух/трёх (массив координат центра и массив радиусов эллипса/радиус окружности, плю учитываем флаг Dir). Если условие выполнено, то проверяем, является ли третий аргумент массивом. Если нет, то это угол между главной осью эллипса и горизонтальной осью координат. Присваиваем его значение переменной ARadAngle, переведённое из градусной меры в радианную, и процесс передачи аргументов метода его локальным переменным на этом закончен. А вот если третий аргумент - массив, то это значения углов начала и конца дуги. Присваиваем их переменным StartAngle и EndAngle (снова предварительно переводя их из градусной меры в радианную) и затем...

Затем следует фрагмент кода, в котором происходит преобразование значений углов начала и конца дуги.
if (ARadius != BRadius){
if (cos(StartAngle) < 0){
StartAngle = PI+atan(ARadius*tan(StartAngle)/BRadius);
} else if (cos(StartAngle) > 0){
StartAngle = atan(ARadius*tan(StartAngle)/BRadius);
};
if (cos(EndAngle) < 0){
EndAngle = PI+atan(ARadius*tan(EndAngle)/BRadius);
} else if (cos(EndAngle) > 0) {
EndAngle = atan(ARadius*tan(EndAngle)/BRadius);
};
};

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

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

Итак, пусть задан угол U. Если произвести обратное преобразование эллипса в окружность, то получится некоторый угол V. Значит, если в начале задать вместо угла U угол V, то после растягивания мы снова получим угол U. Вычислим угол V. tan(V) = A/B; tan(U) = A/C; C = B*k, где k - коэффициэнт растяжения, равный отношению горизонтального радиуса к вертикальному (k = ARadius/BRadius). Отсюда tan(V) = tan(U)*C/B = tan(U)*B*k/B = tan(U)*k. Именно из этого соотношения берётся формула StartAngle = atan(ARadius*tan(StartAngle)/BRadius) для начального угла и аналогичная для конечного. Теперь о проверках, связанных со знаком cos. tan(PI/2) и tan (3*PI/2) не существуют (равны бесконечности), cos(PI/2)=cos(3*PI/2)=0. Однако, если угол U=PI/2 или 3*PI/2, при растяжении окружности вдоль горизонтальной оси его значение не изменится. Поэтому случай, когда cos(StartAngle) = 0 мы сразу убираем. Проверка формальна, так как скорее всего, если попросить Flash вывести значение tan(PI/2), вы получите что-то вроде 1.63317787283838e+16, большое, но всё же конечное число. Если cos(StartAngle) Ну, со сложными математическими преобразованиями пока всё. Возвращаемся к программному коду:
while (!(EndAngle > StartAngle)){
StartAngle -= 2*PI;
};
while (EndAngle-StartAngle > 2*PI){
EndAngle -= 2*PI;
}; 

Дуга строится всегда от начального угла к конечному, поэтому если значение начального угла не меньше конечного, то мы уменьшаем StartAngle на 2*PI радиан за шаг, пока оно не станет меньше EndAngle (забудем пока о том, что построение может идти и в противоположном напрпвлении - от большего угла к меньшему). Второй цикл следит за тем, чтобы разница между углами не была больше 2*PI радиан.

Теперь вспомним про описанные вначале переменные div и delta:
div = ceil(6*(EndAngle-StartAngle)/PI);
delta = (EndAngle-StartAngle)/div;

Переменная div показывает, какое количество дуг нам нужно взять при условии, что каждая из них не больше PI/6 радиан (в случае, когда углы не заданы как аргументы метода, div = (2*PI-0)/(PI/6) = 12, что является её значением по умолчанию). А переменная delta - сколько радиан точно составляет каждая дуга (по умолчанию delta = (PI*2-0)/12 = PI/6). Как Я уже говорил в первой части статьи, из параболы эллипс можно получить только с определённой долей приближения. Чем больше кривых curveTo() мы используем, тем более точное получаем приближение. Если для создания дуги в PI/6 радиан будет использована одна curveTo(), то точность приближения будет 927/1000, что Я считаю вполне достаточным. Если вам этого мало (много), просто разделите в строке div = ceil(6*(EndAngle-StartAngle)/PI) выражение (EndAngle-StartAngle) на меньший (больший) угол, а также измените значения по умолчанию div и delta. Скажу лишь, что если на PI/36 радиан отводить одну кривую curveTo(), то точность будет составлять 9997/10000. Так что брать ещё меньший угол вряд ли имеет смысл.

И в завершении передачи аргументов для случая рисования дуги:
if (arguments.length == 4){
ARadAngle = PI*arguments[3+t]/180;
};
};
};

Проверка, задан ли четвёртый аргумент - угол наклона. И закрывающие фигурные скобки.

Итак, на данный момент код программы выглядит так:
movieClip.prototype.ellipseTo = function(){
var t = arguments[0][0] == undefined ? 1 : 0;
var CenterX = arguments[0+t][0];
var CenterY = arguments[0+t][1];
var Dir = 1;
var ARadius;
var BRadius;
var StartAngle = 0;
var EndAngle = 0;
var ARadAngle = 0;
var div = 12;
var delta = Math.PI/6;
if (t == 1 && (arguments[0] == -1 || arguments[0] == 1)){
Dir = arguments[0];
}
if (arguments[1+t][0] == undefined){
ARadius = BRadius = arguments[1+t];
} else {
ARadius = arguments[1+t][0];
BRadius = arguments[1+t][1];
};
with (Math){ 
if (arguments.length > 2+t){
if (arguments[2+t][0] == undefined){
ARadAngle = PI*arguments[2+t]/180;
} else {
StartAngle = PI*arguments[2+t][0]/180;
EndAngle = PI*arguments[2+t][1]/180;
if (ARadius != BRadius){
if (cos(StartAngle)< 0){
StartAngle = PI+atan(ARadius*tan(StartAngle)/BRadius);
} else if (cos(StartAngle)> 0){
StartAngle = atan(ARadius*tan(StartAngle)/BRadius);
};
if (cos(EndAngle)< 0){
EndAngle = PI+atan(ARadius*tan(EndAngle)/BRadius);
} else if (cos(EndAngle)> 0) {
EndAngle = atan(ARadius*tan(EndAngle)/BRadius);
};
};
while (!(EndAngle > StartAngle)){
StartAngle -= 2*PI;
};
while (EndAngle-StartAngle > 2*PI){
EndAngle -= 2*PI;
}; 
div = ceil(6*(EndAngle-StartAngle)/PI);
delta = (EndAngle-StartAngle)/div;
if (arguments.length == 4){
ARadAngle = PI*arguments[3+t]/180;
};
};
}; 

Подготовка данных окончена. Приступаем к нахождению координат контрольных и якорных точек. Нам потребуются ещё две константы, радиусы эллипса, которому принадлежат все контрольные точки, участвующие в построении:
var ABRadius = ARadius/cos(delta/2);
var BBRadius = BRadius/cos(delta/2);

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

Угол U - это наша delta. После задания параметров методу выяснилось, что для построение дуги потребуется две curveTo(). В общем случае их может быть от одной до двенадцати (полная окружность или эллипс), но на данном этапе нам потребуется всего одна, так что их общее количество пока не важно. Для начала найдём угол V. Отрезки A2_C1 и A1_C1 являются направляющими векторами для curveTo, поэтому они являются касательными, в данном случае к заданной окружности. А значит они перпендекулярны радиусам O_A2 и O_A1 соответственно по свойству касательных к окружности. Значит, треугольники O_A2_C1 и O_A1_C1 прямоугольные. Они имеют одну общую сторону O_C1, а стороны O_A2 и O_A1 равны как радиусы окружности. Значит, эти треугольники равны, и угол V = V1. В сумме они дают угол U, а значит угол V = U-V1 = U-V = U/2. Теперь найдём O_C1. По определению cos(V) = A1_C1/O_C1. Отсюда O_C1 = A1_C1/cos(U/2). Отсюда получается формула BBRadius = BRadius/cos(delta/2). Как видно, при постоянных BRadius и delta, BBRadius также будет постоянен. BBRadius - расстояние от центра окружности до любой участвующей в построении контрольной точки. Значит, все контрольные точки равноудалены от центра заданной окружности, т.е. сами лежат на окружности с тем же центром и радиусом BBRadius. Теперь, если мы рассмотрим соседнюю дугу, то для неё направляющий вектор curveTo A2_C2 также будет перпендекулярен O_A2. Тем самым выполняется условие отсутствия изломов: контрольная и якорная точки первой кривой и контрольная точка второй кривой находятся на одной прямой.

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

Поэтому после растяжения они также будут находится на некотором эллипсе. Его горизонтальный радиус ABRadius = BBRadius*k = (BRadius/cos(delta/2))*(ARadius/BRadius) = ARadius/cos(delta/2), так как коэффициэнт растяжения у них одинаковый. Так как при растяжении прямые остаются прямыми (меняются лишь углы между ними), то условие отсутствия изломов по-прежнему сохраняется.

Итак, начинаем нахождение координат точек! Чтобы не делать два цикла и не заводить новые массивы с данными, мы будем находить координаты контрольной и якорной точек и сразу вызывать curveTo(). Но сначала, если флаг Dir не был задан, нужно найти координаты стартовой точки, к которой мы перейдём через moveTo():
if (t==0){
var teX = ARadius*cos(StartAngle);
var teY = BRadius*sin(StartAngle);
var X1 = CenterX+teX*cos(ARadAngle)-teY*sin(ARadAngle);
var Y1 = CenterY+teX*sin(ARadAngle)+teY*cos(ARadAngle);
this.moveTo (X1, Y1);
} 

Если бы точка лежала на окружности с центром в начале координат, то её координаты были бы (BRadius*cos(StartAngle), BRadius*sin(StartAngle)). Так как в общем случае мы производим растяжение, то координату X нужно домножить на ARadius/BRadius. Итак, переменные teX и teY содержат координаты стартовой точки на неповёрнутом эллипсе с центром в начале координат.

Теперь нужно выполнить поворот. Для этого используется формула:
// X' = X*cos(u)-Y*sin(u)
// Y' = X*sin(u)+Y*cos(u)

где X, Y - старые координаты точки, X', Y' - новые, u - угол поворота. Желающие могут посмотреть коротенький вывод этих формул:

Итак, координаты точки А (X, Y). Пусть длина отрезка О_А равна L. Тогда X=L*cos(u); Y=L*sin(u). Теперь производим поворот на угол V. Точка A перешла в A' с координатами X'=L*cos(u+v); Y'=L*sin(u+v). Теперь раскрываем скобки по формулам синуса и косинуса суммы: X'=L*cos(u+v)=L*cos(u)*cos(v)-L*sin(u)*sin(v)=X*cos(v)-Y*sin(v), аналогично X'=L*sin(u+v)=L*sin(u)*cos(v)+L*cos(u)*sin(v)=Y*cos(v)+X*sin(v).

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

Теперь чуть-чуть опртимизируем код.
if (t==0){
var te = ARadius*cos(StartAngle);
var Y1 = BRadius*sin(StartAngle);
var X1 = CenterX+teX*cos(ARadAngle)-teY*sin(ARadAngle);
Y1 = CenterY+teX*sin(ARadAngle)+teY*cos(ARadAngle);
this.moveTo (X1, Y1);
}

В качестве второй временной переменной используем Y1, значение которой изменяется лишь в конце этой части кода. Ещё одна вещь: если Вы привыкли, что отсчёт углов происходит против часовой стрелки, а не по часовой, то нужно как-бы перевернуть ось Y, и пятая строчка тогда будет выглядеть так:
Y1 = CenterY-te*sin(ARadAngle)-Y1*cos(ARadAngle);

И, наконец, учитывая, что переменнные X1, Y1 и te потребуются в цикле независемо от того, используем мы moveTo() или нет, делаем окончательный вариант:
var X1;
var Y1;
var te; 
if (t==0){
te = ARadius*cos(StartAngle);
Y1 = BRadius*sin(StartAngle);
X1 = CenterX+teX*cos(ARadAngle)-teY*sin(ARadAngle);
Y1 = CenterY+teX*sin(ARadAngle)+teY*cos(ARadAngle);
this.moveTo (X1, Y1);
}

Теперь, если флаг Dir всё же был задан и равен -1, то нужно поменять местами значения начального и конечного углов:
else if (Dir==-1){
te = StartAngle;
StartAngle = EndAngle;
EndAngle = te;
}

В цикле потребуются ещё две переменные для хранения координат контрольных точек. Пока просто опишем их:
var X2;
var Y2;

Ну, все преготовления закончились. Запускаем цикл рисования эллипса:
for (var i = 1; i<=div; i++){
te = ARadius*cos(StartAngle+Dir*delta*i);
Y1 = BRadius*sin(StartAngle+Dir*delta*i);
X1 = CenterX+te*cos(ARadAngle)-Y1*sin(ARadAngle);
Y1 = CenterY+te*sin(ARadAngle)+Y1*cos(ARadAngle);
te = ABRadius*cos(StartAngle+Dir*delta*(i-0.5));
Y2 = BBRadius*sin(StartAngle+Dir*delta*(i-0.5));
X2 = CenterX+te*cos(ARadAngle)-Y2*sin(ARadAngle);
Y2 = CenterY+te*sin(ARadAngle)+Y2*cos(ARadAngle);
this.curveTo (X2, Y2, X1, Y1);
};
};
}

Цикл пройдёт div раз и создаст div дуг. Сначала находим координаты для якорных точки. Всё происходит по тому же принципу, что и для начальной точки . На каждом этапе работы цикла мы смещаемся на угол delta относительно предыдущего положения, в конце концов достигая конечного угла EndAngle = StartAngle+Dir*delta*div. Переменная Dir указывает, нужно прибавлять угол или вычитать, в завсимости от выбранного направления построения.

Координаты контрольных точек находятся аналогично якорным, так как тоже находятся на эллипсе. Угол берётся на delta/2 меньше, чем для якорной точки (это следует из проведённых ранее вычислений). Опять же, если нужно другое направление отсчёта,
Y1 = CenterY-te*sin(ARadAngle)-Y1*cos(ARadAngle)
Y2 = CenterY-te*sin(ARadAngle)-Y2*cos(ARadAngle); 

И, наконец, вызов метода curveTo(), ради которого были все наши труды.

ВСЕ!!! Создание окончено. Целиком код метода выглядит так:
movieClip.prototype.ellipseTo = function(){
var t = arguments[0][0] == undefined ? 1 : 0;
var CenterX = arguments[0+t][0];
var CenterY = arguments[0+t][1];
var Dir = 1;
var ARadius;
var BRadius;
var StartAngle = 0;
var EndAngle = 0;
var ARadAngle = 0;
var div = 12;
var delta = Math.PI/6;
with (Math){ 
if (arguments[1+t][0] == undefined){
ARadius = BRadius = arguments[1+t];
} else {
ARadius = arguments[1+t][0];
BRadius = arguments[1+t][1];
};
if (arguments.length > 2+t){
if (arguments[2+t][0] == undefined){
ARadAngle = PI*arguments[2+t]/180;
} else {
StartAngle = PI*arguments[2+t][0]/180;
EndAngle = PI*arguments[2+t][1]/180;
if (ARadius != BRadius){
if (cos(StartAngle)< 0){
StartAngle = PI+atan(ARadius*tan(StartAngle)/BRadius);
} else if (cos(StartAngle)> 0){
StartAngle = atan(ARadius*tan(StartAngle)/BRadius);
};
if (cos(EndAngle)< 0){
EndAngle = PI+atan(ARadius*tan(EndAngle)/BRadius);
} else if (cos(EndAngle)> 0) {
EndAngle = atan(ARadius*tan(EndAngle)/BRadius);
};
};
while (!(EndAngle > StartAngle)){
StartAngle -= 2*PI;
};
while (EndAngle-StartAngle > 2*PI){
EndAngle -= 2*PI;
}; 
div = ceil(6*(EndAngle-StartAngle)/PI);
delta = (EndAngle-StartAngle)/div;
if (arguments.length 

Добавлено: 18 Сентября 2013 03:05:28 Добавил: Андрей Ковальчук

Рисование с помощью ActionScript во Flash MX

Для рисования примитивов нам потребуются следующии функции объекта MovieClip:

beginFill ([rgb[, alpha]])
beginGradientFill (fillType, colors, alphas, ratios, matrix)
clear()
curveTo (controlX, controlY, anchorX, anchorY)
endFill()
lineStyle ([thickness[, rgb[, alpha]]])
lineTo (x, y)
moveTo (x, y)

Начнем с функций moveTo() и lineTo(). Они используются для рисования линий. moveTo() устанавливает позицию "карандаша" (курсора) в позицию x, y. lineTo() проводит линию от позиции, установленной moveTo(), до позиции указанной параметрами x, y в самой функции lineTo().

Но, прежде чем рисовать линии этими функциями, нужно установить свойства линии функцией lineStyle(). У неё три необязательных параметра: thickness - указывает толщину линии; rgb - цвет (а-ля 0x56FFCC); alpha - значение прозрачности. Если параметр не указан, его значение считается равным нулю.

Вот пример кода, который рисует синий квадрат в текущем объекте MovieClip:
lineStyle(1, 0x0000FF);
moveTo(100, 100);
lineTo(200, 100);
lineTo(200, 200);
lineTo(100, 200);
lineTo(100, 100); 




Координатная решетка любого объекта типа MovieClip начинается с верхнего левого угла и растет вправо вниз, как видно на рисунке выше.

Для закраски нарисованных фигур используются функции beginFill() и endFill(). Функцию beginGradientFill() для создания градиентной закраски мы рассмотрим позже.

beginFill() включает режим закраски. После её вызова, все нарисованные контуры будут закрашиваться, образую замкнутые фигуры. beginFill() имеет два необязательных параметра: rgb указывает цвет закраски, alpha - прозрачность.

endFill() отключает режим закраски.

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

Вот пример c использованием функций beginFill() и endFill():
_root.createEmptyMovieClip("myClip", 1);

myClip.lineStyle(2,0x234567);
myClip.beginFill(0x7878FF);
myClip.moveTo(70,20);
myClip.lineTo(20,100);
myClip.lineTo(120,100);
myClip.lineTo(70,20);
myClip.endFill();

myClip.lineStyle(4, 0x0078DD);
myClip.moveTo(140,20);
myClip.lineTo(190,20);
myClip.lineTo(190,70);
myClip.lineTo(140,70);
myClip.lineTo(140,20);

myClip.beginFill(0x00FF00, 30);
myClip.lineStyle(1, 0xDC2323);
myClip.moveTo(230, 20);
myClip.lineTo(350, 100);
myClip.lineTo(350, 20);
myClip.lineTo(230, 100);
myClip.lineTo(230, 20); 

Рассмотрим функцию, которая рисует кривые: curveTo(controlX, controlY, anchorX, anchorY). Функция имеет четыре обязательных параметра. При отсутствии хотя бы одного из них она не срабатывает. Началом кривой считается текущая позиция курсора ("карандаша"), которая устанавливается при помощи moveTo(), или же позицией, в которой закончили черчение функции lineTo() или curveTo(). Конец кривой указывается параметрами anchorX и anchorY. Параметры controlX и controlY указывают точку, к которой направлены начало и конец линии. Для наглядности на рисунке ниже обозначены все точки.


Вот пример кода, и рисунка, который получается в результате его исполнения:
lineStyle(1);
beginFill(0xFF9921, 20);
moveTo(70,20);
curveTo(120,20,120,70);
curveTo(120,120,70,120);
curveTo(20,120,20,70);
curveTo(20,20,70,20);
endFill();

moveTo(140,120);
curveTo(140,20,160,20);
curveTo(180,20,180,120); 

Наконец, рассмотрим самую сложную функцию beginGradientFill (fillType, colors, alphas, ratios, matrix), которая служит для градиентной закраски.

Параметр fillType указывает тип закраски может быть равен одной из следующих строк: "linear" - линейная, "radial" - радиальная. Не забудьте, что это строки, поэтому параметры нужно указывать в кавычках.

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

colors - массив, содержащий цвета градиента. alphas содержит alphа-канал (прозрачность) каждого цвета.
ratios содержит значения распределения цветов. Возможные значения: 0-255. Это значение указывает место в процентном соотношении, где значение цвета достигает максимума.
matrix является матрицей преобразования, которая может описываться двумя наборами значений:
a, b, c, d, e, f, g, h, i, которые представляют из себя матрицу типа
a b c
d e f
g h i
или matrixType, x, y, w, h, r, где

matrixType - строка "box";
x и y - смещение центра градиента, относительно точки регистрации объекта;
w и h - ширина и высота градиента соответственно;
r - угол поворота градиента в радианах.
Для обращения к свойствам объекта можно использовать инструкцию with(). Вот пример, в котором мы создаём классическую "хромовую" закраску, повёрнутую на 45 градусов.
_root.createEmptyMovieClip( "myClip", 1 );
with ( _root.myClip )
{ 

colors = [ 0x0066FD, 0xFFFFFF, 0xFFFFFF, 0x996600, 0xFFCC00, 0xFFFFFF];
alphas = [ 100, 100, 100, 100, 100, 100 ];
ratios = [ 0, 100, 120, 125, 165, 255];
matrix = { matrixType:"box", x:20, y:20, w:130, h:100, r:(45/180)*Math.PI }; 
beginGradientFill( "linear", colors, alphas, ratios, matrix );
moveto(20,20);
lineto(150,20);
lineto(150,120);
lineto(20,120);
lineto(20,20);
endFill(); 

} 

Вот и всё! На самом деле, после небольшого количества практики, всё становится понятно и легко.

Добавлено: 18 Сентября 2013 02:59:50 Добавил: Андрей Ковальчук

Flash-навигация при отключенном Flash

Последнее время технология Flash на сайтах становится все более популярной. Красивые анимированные меню, баннеры, и даже целые сайты выполнены на Flash. Не так давно я с интересом узнал, что изначально Flash создавался для просмотра анимированных порнушных мультиков и совершенно не предназначался для сайтостроительства. Предположим, вы относитесь к тому редкому виду дизайнеров, которые используют Flash "редко, но метко", и сделали на нем красивую навигацию для сайта. У большинства пользователей все работает как надо, ссылки, прописанные в Flash-кнопках открываются, в общем все замечательно. А что увидит пользователь сайта, если у него в браузере отключена поддержка Flash, например в целях безопасности или экономии трафика? Правильно, не увидит НИЧЕГО. Для решения проблемы предлагаю использовать следующий код:

<object type="application/x-shockwave-flash"
  data="images/contact_button.swf" width="100" height="30">
  <param name="menu" value="false" />
  <param name="quality" value="high" />
  <param name="wmode" value="transparent" />
  <param name="bgcolor" value="#ffffff" />
  <param name="movie" value="images/contact_button.swf" />
  <a href="contact.html"><img src="images/contact_button.gif" width="100"
  height="30" border="0" alt="Контакты" /></a>
</object>

Если все нормально, то посетитель увидит красивую кнопку contact_button.swf, если в браузере отключен Flash, будет показана ссылка на страницу контактов и графическая кнопка contact_button.gif. Если отключены и картинки, то будет показан текст-заместитель "Контакты". Как видите, в любом случае посетитель не потеряется на вашем сайте и попадет куда надо.

Добавлено: 12 Сентября 2013 04:26:11 Добавил: Андрей Ковальчук

Настройки приложения в контакте (битовая маска настроек)

В FlashVars приложения хранится api_settings - это битовая маска настроек приложения у пользователя.
Значение, полученное методом getUserSettings обрабатывается так же.
http://vkontakte.ru/pages.php?o=-1&p=getUserSettings

Этот код только для примера того как можно узнавать настройки приложения, поэтому привожу код, который можно писать прямо в кадр.


Код

// получаем api_settings из FlashVars
var flags = LoaderInfo(root.loaderInfo).parameters.api_settings;
 
// создаём текстовое поле для отображения результатов
var result_tf:TextField = new TextField();
result_tf.autoSize=TextFieldAutoSize.LEFT;
result_tf.x=10;
result_tf.y=10;
addChild(result_tf);
 
result_tf.text='Результаты:';
result_tf.appendText('\nУведомления: '   + Boolean(flags & 1));
result_tf.appendText('\nДрузья: '        + Boolean(flags & 2));
result_tf.appendText('\nФотографии: '    + Boolean(flags & 4));
result_tf.appendText('\nАудиозаписи: '   + Boolean(flags & 8));
result_tf.appendText('\nВидеозаписи: '   + Boolean(flags & 16));
result_tf.appendText('\nПредложения: '   + Boolean(flags & 32));
result_tf.appendText('\nВопросы: '       + Boolean(flags & 64));
result_tf.appendText('\nWiki-страницы: ' + Boolean(flags & 128));
result_tf.appendText('\nВ меню слева: '  + Boolean(flags & 256));
result_tf.appendText('\nБыстрая публикация на стенах пользователей: ' + Boolean(flags & 512));
result_tf.appendText('\nСтатусы: '       + Boolean(flags & 1024));
 
// пример проверки доступа к друзьям
result_tf.appendText('\n\nПроверка доступа к друзьям');
if (Boolean(flags&2)) {
    result_tf.appendText('\nДоступ к друзьям разрешен');
} else {
    result_tf.appendText('\nДоступ к друзьям запрещён');
    // здесь можно вывести сообщение о том, что нужно разрешить доступ к друзьям
    // а при использовании контейнера, можно вызвать метод showSettingsBox(2)
}
 
// пример проверки доступа к друзьям и фотографиям одновременно
result_tf.appendText('\n\nПроверка доступа к друзьям и фотографиям');
if (Boolean(flags&2)&&Boolean(flags&4)) {
    result_tf.appendText('\nДоступ к друзьям и фотографиям разрешен');
} else {
    result_tf.appendText('\nДоступ к друзьям и/или фотографиям запрещён');
}

Добавлено: 16 Августа 2013 03:41:00 Добавил: Андрей Ковальчук

Урок создания flash видео плеера

Рассмотрим пример создания флеш проигрывателя для видео файлов, который можно установить на свой сайт, позволив посетителям сайта смотреть видео на сайте без скачивания при помощи технологии Adobe Flash, на языке actionscript 2. Обратите внимание, что flash плеер может воспроизводить видео только одно формата –flv.
Плеер будет состоять из одного кадра. На сцене должны быть:
1) Видео-символ (myVideo). В свойствах нужно указать Video (actionscript controlled).
2) Кнопка или мувиклип, который будет останавливать воспроизведение (кнопка пауза). Instance name - p_btn.
3) Над этой кнопкой нужно разместить клипы – значки воспроизведения и паузы (play_btn и pause_btn).
4) Мувиклип vol, состоящий из двух мувиклипов – полосок, индикатор/регулятор громкости. На первом слое (нижнем) должен быть мувиклип fon, над ним – bar, идентичный по размеру, но другого цвета.
5) Клип progress_bar, похожий на клип vol. На нижнем слое клип fon, на среднем - flv_load, на самом высшем – bar.
После того, как вы сделали оболочку плеера, вам остаётся только добавить в кадр код:

stop(); // останавливаем воспроизведение
// ставим ширину равной нулю (скрываем) индикаторы загрузки и позиции воспроизведения
progress_bar.flv_load._width = 0;
progress_bar.bar._width = 0;
pause_btn._visible = false; // скрываем кнопку пауза
//isPlaying - булева величина, которая показывает, идёт ли сейчас подгрузка видео
isPlaying = false;

link = '1.flv';// ссылка на видео файл для воспроизведения (flv)

// нажатие на кноку play/pause
p_btn.onRelease = function() {
    if (!isPlaying) {// если подгрузка вижео ещё не начиналась
        _root.nc = new NetConnection(); // экземпляр класса NetConnection
        _root.nc.connect(null);
        _root.my_ns = new NetStream(_root.nc);// экземляр класса NetStream
        setPlayBtn(false); // скрываем кнопку play, показываем кнопку pause
        // "прикрепляем" поток видео (класс NetStream) к видео-символу
        myVideo.attachVideo(_root.my_ns);
        _root.my_ns.play(link);
        // при получении данных о длительности видео
            _root.my_ns.onMetaData = function(infoObject:Object) {
            totalTime = infoObject.duration;//сохраняем в отдельную переменную
            };
            // при завершении загрузки видео
            _root.my_ns.onStatus = function(info) {
            if (info.code == "NetStream.Play.Stop") {
                isPlaying = false;
                setPlayBtn(true);
                progress_bar.bar._width = 0;
                }
            }
        isPlaying = true; // значит, что видео начало подгружаться
    } else // если видео уже грузится
    if (pause_btn._visible) { // и если сейчас плеер не на паузе
    // ставим видео на паузу
        _root.my_ns.pause(true);
        // запоминаем позицию воспроизведения
        current = _root.my_ns.time;
        // скрываем кнопку pause, показываем кнопку play
        setPlayBtn(true);
    } else { // если плеер на паузе
    // перематываем видео на место, где остановились
        current = _root.my_ns.time;
        // и продолжаем воспроизведение
        _root.my_ns.pause(false);
        // скрываем кнопку play, показываем кнопку pause
        setPlayBtn(false);
    }
    
}


onEnterFrame = function() {
    if (isPlaying) {
        setBar();
    }
    //считаем проценты, меняем ширину полоски - индикатора загрузки видео во flash плеер
    current = _root.my_ns.time;
    total = _root.my_ns.bytesTotal; // "вес" видео в байтах
    loaded = _root.my_ns.bytesLoaded; // количество загрженых байт
    percent = (loaded/total); // соотношение полного веса к загруженой части
    // в зависимости от загруженой части, меняем длину зелёной полоски - индикатора
    progress_bar.flv_load._width = (percent * progress_bar.fon._width);
    
}
// setBar - функция, которая будет отображать прогресс вопроизведения видео в плеере
// при вызове позиция передаётся как процент (от 0 до 100)
function setBar(percent:Number) {
    if (percent == undefined) percent = Math.floor(_root.my_ns.time/totalTime*100);
    // меняем ширинц полоски-индикатора
    progress_bar.bar._width = (percent/100 * progress_bar.fon._width);
}

// нажатие на полоску - индикатор загрузки
// именно на этот индикатор, чтобы нельзы было перепрыгвать на незагруженные участки
progress_bar.flv_load.onRelease = function() {
    // вызов функции scrubbing, которая отвечает за перемотку на нужный моммент времени
    scrubbing(Math.floor((progress_bar._xmouse*100)/progress_bar._width));
}

function scrubbing(p) { // функция для перемтоки видео
        isEnd = false;
        setPlayBtn(false);
_root.my_ns.seek(p*totalTime/100);
_root.my_ns.pause(false);
}

// функция, которая показывает/прячет кнопки play и pause
function setPlayBtn(b) {
    play_btn._visible = b;
    pause_btn._visible = !b;
}

// создаём обьект класса Sound, который будет охватывать все звуки в фильме
sound = new Sound();
// регулировка громкости
vol.onRelease = function() { // клик по индикатору громкости
    vol.bar._width = vol._xmouse;// меняем ширину индикатора
    // меняем громость звука
    sound.setVolume(vol._xmouse/vol._width * 100);
}

Ниже вы можете увидеть мой пример флеш видео плеера, который легко можно поставить себе на сайт.

Добавлено: 15 Августа 2013 10:27:05 Добавил: Андрей Ковальчук

Создание видео плеера с плейлистом (xml)



Раннее я публиковал урок изготовления flash проигрывателя для mp3 файлов, который считывал список файлов для воспроизведения (плейлист, playlist) из внешнего xml файла, также я выкладывал урок изготовления флеш видео плеера, но без плейлиста (actionscript 2). Сейчас, исходя из этих уроков, мы сделаем flash плеер для видео со списком воспроизведения при помощи среды Adobe Flash и языка actionscript 2. Конечно, для начала желательно почитать эти уроки, так как я не буду подробно описывать создания плейлиста, работу с xml и потоковым видео (всё описано в вышеупомянутых уроках).
Первый кадр, в котором будет грузиться сама флешка и xml список воспроизведения, оставим пустым, работать же будем во втором кадре. Сам список (плейлист) можно скопировать (или почитать как его сделать) в статье о mp3 плеере. Для «копирования» плейлиста нужно скопировать мувиклипы pl, scroll и item. Из урока с видео плеером необходимо скопировать компонент для проигрывания видео (myVideo), клип-индикатор воспроизведения и загрузки (position) и клип-регулятор громкости (vol). Ещё на сцене необходимо разместить кнопку с instance name p_btn (кнопка смены паузы/воспроизведения), над ней разместить клип-иконку паузы (pause_btn) и клип-иконку воспроизведения (play_btn).
Пример структуры xml файла, который описывает плейлист для нашего flash видео плеера:

<data>
<item url="1.flv" label="Первое видео" time ="4:02" />
<item url="2.flv" label="Второе видео" time ="5:11" />
</data>

Атрибут url – относительный либо абсолютный путь к .flv видео файлу. Рекомендую использовать относительные адреса, потому что с точки зрения flash плеера, http://flash-world.ru и http://www.flash-world.ru – разные домены, и если вы пропишете адрес без www, то, скорее всего, пользователь, который зашёл на www.flash-world.ru не сможет посмотреть видео, так как будут проблемы с политикой безопасности flash плеера.
В атрибуте label хранится название видео, в атрибуте time – продолжительность видео, которое отображется в плейлисте рядом с названием.
Итак, создайте файл playlist.xml, заполните его нужным образом и сохраните его в кодировке UTF-8 в ту же папку, располагается или будет располагаться флешка – видео проигрыватель. Для загрузки xml плейлиста в наш flash видеопроигрыватель в первый кадр флешки вставьте код
Stage.scaleMode = 'noScale';
// для того, чтобы флешка могла работать с кириллическими символами
System.useCodepage = false;
// останавливаем флешку на первом кадре,
// и ждём пока загрузится xml 
// после загрузки переводим нашу флешку на второй кадр,
// с нашим видео плеером
stop();
playlist = new XML();
playlist.ignoreWhite = true;
playlist.onLoad = function() {
    _root.gotoAndStop(2);
}

playlist.load("playlist.xml");

Далее переходим на второй кадр, вставляем код:
// инициализация
yy = 5; // размещать элементы плейлиста будем от 5
dif = 3; // расстояние по высоте между элементами в списке
url = []; // массив для хранения ссылок на видео-клипы
// перебираем все элементы xml файла плей-листа нашего mp3 плеера
for (i=0; i<playlist.firstChild.childNodes.length; i++) {
    mc = _root.pl.container.attachMovie('item', 'item'+i, i);
    // в список воспрозведения из библиотеки добавляем мувиклип item 
    mc._x = 5; // немного сдвигаем его по оси _x
    mc._y = yy; // назначаем новую координату по _y
    yy += mc._height+dif; // считаем координаты для следующего элемента плей-листа
    dataHolder = playlist.firstChild.childNodes[i];
    // в каждый элемет плейлиста (списка) пишем несколько переменных,
    // которые хранятся в xml файле плейлиста
    mc.label = dataHolder.attributes.label; // название видео
    mc.time = dataHolder.attributes.time; // продожительность видео
    url[i] = dataHolder.attributes.url; // массив путей к flv видео файлам
    mc.i = i; // в каждый пункт списка пишем его номер
    mc.playing = false; // булева величина
    // false значит, что текущее данное видео не проигрывается в данный момент
}
// кодим скроллер
// для того, чтобы текст в динамических полях отображался, маску нужно применять программно.
pl.container.setMask(pl.m);// что мы и делаем =)
// если список умещается в контейнер, то полоса прокрутки (скроллер) нам не нужен.
if (pl.container._height < pl.m._height) { // проверяем
    scroll._visible = false; // если это так, скрываем скроллер
}

// если скроллер нам всё-таки нужен
// при нажатии на ползунок
scroll.bar.onPress = function() {
// начинаем его перетаскивать от самого верху (0) до низа-выоста ползунка
    scroll.bar.startDrag(false, 4, 0, 4, scroll._height-scroll.bar._height);
        scroll.onMouseMove = function () { // при движении курсора
        // двигаем контейнер со списком воспроизведения на нужное расстояние
        pl.container._y = 0-((pl.container._height-pl.m._height+10) * scroll.bar._y / (scroll._height-scroll.bar._height));
    }
}
// когда пользователь отпускает кнопку мыши, нужно остановить движение ползунка
scroll.bar.onRelease = scroll.bar.onReleaseOutside = function() {
    scroll.bar.stopDrag();
}
// конец формирования плейлиста и скроллера

// часть кода, которая отвечает за проигрывание видео 
// инициализация
mediaPlaying = false; // изначально при заходе на страницу видео не воспроизводится
isPlaying = null; // номер видео, которое играет плеер в данный момент
// скрываем (делаем ширину равной 0) все индикаторы
progress_bar.flv_load._width = 0;// индикатор процесса загрузки видео файла
progress_bar.bar._width = 0;// индикатор прогресса воспроизведения flv файла
pause_btn._visible = false;// скрываем икноку пауза ||

function playVideo(numb) { // на вход принимается номер мувика, видео в котором нужно проиграть
// переводим активный элемент плейлиста в неактивное состояние
_root.pl.container['item'+isPlaying].fon.gotoAndStop(1);
isPlaying = numb;// переменной isPlaying присваиваем номер текущего видео
// переводим в активное состояние элемент плейлиста, по которому кликнули,
// который ссылается на видео, которое плеер воспроизводит в данный момент
_root.pl.container['item'+isPlaying].fon.gotoAndStop(2);

// создаём поток для воспроизведения видео (классы NetConnection и NetStream)
_root.nc = new NetConnection();
_root.nc.connect(null);
_root.my_ns = new NetStream(_root.nc);
myVideo.attachVideo(_root.my_ns);
// воспроизводим видео под номером numb,
// который передаётся при вызове функции playVideo
// берём ссылку на flv файл из массива url по индексу numb
_root.my_ns.play(url[numb]);

// т.к. видео воспроизводится, нужно присвоить переменной mediaPlaying значение true
mediaPlaying = true;
setPlayBtn(false); // скрываем кнопку play, показываем кнопку pause

// функция выполнится при поступлении мета-данных о видео файле
_root.my_ns.onMetaData = function(infoObject:Object) {
    // сохраняем продолжительность видео (в секундах) в переменную totalTime
    totalTime = infoObject.duration;
};

_root.my_ns.onStatus = function(info) {
    if (info.code == 'NetStream.Play.Stop') {
// когда воспроизведние закончится
// присваиваем перменной mediaPlaying значение false
        mediaPlaying = false;
// скрываем кнопку pause, делаем видимой кнопку воспроизведние
        setPlayBtn(true);
    }
}

onEnterFrame = function() {
/* в функции onEnterFrame мы будем считать соотношение размера 
видео к его загруженой части, а также прогресс воспроизвдения
и изменять ширину соотвествующих индикаторов */
    if (mediaPlaying) {
// вызов функции setBar, которая меняет ширину индикатора воспроизвдения
        setBar();
    }
    //считаем проценты, меняем ширину полоски - индикатора загрузки видео во flash плеер
    total = _root.my_ns.bytesTotal;// полный размер flv файла 
    loaded = _root.my_ns.bytesLoaded;// размер загруженой части видео
    percent = int(loaded/total);// округляем до целого числа отношение между ними
// меняем ширину полоски-индикатора загрузки по нехитрой формуле
    progress_bar.flv_load._width = (percent * progress_bar.fon._width);
// в переменной current хранится секунда, на которой сейчас воспроизведение
    current = _root.my_ns.time;
}

function setBar() {
// функция, которая отвечает за отображение прогресса воспроизведения
    percent = Math.floor(_root.my_ns.time/totalTime*100);
    progress_bar.bar._width = (percent/100 * progress_bar.fon._width);
}

// функция scrubbing отвечает за перемеотк видео на нужное время.
// как параметр передаётся процент времени, на который нужно 
// перемотать видео (число от 1 до 100)
function scrubbing(p) {
        isEnd = false;
        setPlayBtn(false);
// "перескакиваем" на нужное место
_root.my_ns.seek(p*totalTime/100);
_root.my_ns.pause(false);
}

// нажатие на индикатор загрузки - перемтока видео на загруженый фрагмент видео
progress_bar.flv_load.onRelease = function() {
// вызываем функцию scrubbing с параметром - процент, на который нужно перемотать видео
    scrubbing(Math.floor((progress_bar._xmouse)/progress_bar._width*100));
}

}// конец onEnterFrame()

// функция setPlayBtn скрывает/прячет кнопки play и pause (пауза и воспроизведение)
// если она будет вызвана с параметром true, кнопка play станет видимой, pause - невидимой
// с параметром false - наоборот 
function setPlayBtn(bool) {
    play_btn._visible = bool;
    pause_btn._visible = !bool;
}

// нажтие на кнопку вопроизведение/пауза
p_btn.onRelease = function() {
    if (pause_btn._visible) { // если сейчас плеер не на паузе
// ставим воспроизведение на паузу
        _root.my_ns.pause(true);
        setPlayBtn(true);
    } else { // если плеер на паузе
// "снимаем" плеер с паузы
        _root.my_ns.pause(false);
        setPlayBtn(false);
    }
    
}

// регулировка громкости
// создаём обьект класса Sound для управления всеми звуками в фильме
sound = new Sound();
// нажатие на регулятор громкости
vol.onRelease = function() {
// меняем ширину индикатора громкости
    vol.bar._width = vol._xmouse;
// устанавливаем громкость звука
    sound.setVolume(vol._xmouse/vol._width * 100);
}

Собственно всё, по идее должно работать.

Добавлено: 15 Августа 2013 10:25:48 Добавил: Андрей Ковальчук

Изготовление флеш рисовалки как вконтакте граффити

В этом уроке я постараюсь как можно подробнее рассказать о том, как написать флеш «рисовалку» на actionscript 2, наподобии той, которая на vk.com и даже получше :) Наша рисовалка будет иметь три инструмента: кисть, линия и прямоугольник. Также будут пара опций: смена диаметра кисти и регулировка интенсивности цвета. И, конечно, выбор цвета.
Начнём, естественно, с изготовления «тела» :)Дадим имена каждому элементу. Инструменты:
Кисть - i1
Линия – i2
Прямоугольник – i3
Холст для рисования назовём pole
Регулятор диаметра – diametr
Регулятор жёсткости – opacity
Индикатор диаметра и цвета (красный кружок) – smp
Палитра – palitra
Кнопка отмена – otm
Кнопка повтора – povt
Кнопка очистки – clДля каждой кнопки прописываем useHandCursor = false;

pole.useHandCursor = false;
palitra.useHandCursor = false;
otm.useHandCursor = false;
povt.useHandCursor = false;
cl.useHandCursor = false;
i1.useHandCursor = false;
i2.useHandCursor = false;
i3.useHandCursor = false;

Инициализируем переменные:
undo = false; //значит, что отменять сейчас нечего
rep = false; //поторять изначально тоже нечего 

kolor = "0xFF0000";//дефолтный цвет
pole.instrument = 1;//дефолтный инструмент (кисть)

diam = 10; //размер кисти
smp.k._height = _root.diam;
smp.k._width = _root.diam;

al = 100; // жёсткость кисти

Приступаем к оформлению кнопок для выбора инструмента. Внутри каждого мувика делаем два кадра: первый – неактивная кнопка, второй – активная, на первый кадр каждой вешаем
stop();

Инструментом по умолчанию будет кисть, поэтому на кадр во временной диаграмме добавляем
i1.gotoAndStop(2);
pole.instrument = 1; // переменная, хранящая номер выбранного инструмента

Сделаем переключение инструментов. Не знаю кто как, а я привык вешать скрипты – обработчики нажатий на сами кнопки, так можно намного быстрее их находить. На кнопку кисть вешаем:
on (rollOver) {
    if (_root.pole.instrument != 1) {
        gotoAndStop(2);
    }
}

on (press) {
    if (_root.pole.instrument != 1) {
        gotoAndStop(3);
    }
}

on (rollOut, releaseOutside) {
    if (_root.pole.instrument != 1) {
    gotoAndStop(1);
    }
}

on (release) {

    _root.i1.gotoAndStop(1);
    _root.i2.gotoAndStop(1);
    _root.i3.gotoAndStop(1);
    _root.pole.instrument = 1;
    gotoAndStop(3);
}

Аналогично на кнопки линия и прямоугольник:
on (rollOver) {
    if (_root.pole.instrument != 2) {
        gotoAndStop(2);
    }
}

on (press) {
    if (_root.pole.instrument != 2) {
        gotoAndStop(3);
    }
}

on (rollOut, releaseOutside) {
    if (_root.pole.instrument != 2) {
    gotoAndStop(1);
    }
}

on (release) {

    _root.i1.gotoAndStop(1);
    _root.i2.gotoAndStop(1);
    _root.i3.gotoAndStop(1);
    _root.pole.instrument = 2;
    gotoAndStop(3);
}

и
on (rollOver) {
    if (_root.pole.instrument != 3) {
        gotoAndStop(2);
    }
}

on (press) {
    if (_root.pole.instrument != 3) {
        gotoAndStop(3);
    }
}

on (rollOut, releaseOutside) {
    if (_root.pole.instrument != 3) {
    gotoAndStop(1);
    }
}

on (release) {
    _root.i1.gotoAndStop(1);
    _root.i2.gotoAndStop(1);
    _root.i3.gotoAndStop(1);
    _root.pole.instrument = 3;
    gotoAndStop(3);
}
Кнопки Отмена и Повтор будут иметь три кадра: первый – активная кнопка, которую можно нажать, второй кадр – кнопка при наведении на неё курсора, третий – неактивная кнопка. Изначально останавливаем кнопки на третьем кадре.
Далее приступим к созданию самого «движка» - скриптов для рисования. Вешать их будем на клип pole. Для начала внутри клипа pole инициализируем две переменные для хранения кол-ва клипов и глубин.
m = 0;
n = 0;

Теперь начинаем писать обработчик нажатия кнопки мыши на мувике pole.
on (press) {

    if (instrument == 1) { //если выбрана кисть
m++; // наращиваем счётчик глубин
_root.otm.gotoAndStop(1); //переводим кнопку Отмена в активное положение
_root.undo = true; // означает, что сейчас можно отменить
_root.norep(); // функция, которую мы напишем позже. 

aa = this.createEmptyMovieClip("ff"+m, m); // создаём новый клип
// пермещаем его в начало координат
aa._x = 0;
aa._y = 0;

// запоминаем позицию курсора когда нажали на кнопку
    x = aa._xmouse
    y = aa._ymouse
// применяем настройки стиля для линий
    aa.lineStyle(_root.diam, _root.kolor, _root.al);
    aa.moveTo(x, y);
// Для того, чтобы при простом нажатии и отпускании кнопки
// мыши у нас появлялась точка, пишем: 
aa.lineTo( aa._xmouse+0.2, aa._ymouse+0.2);
// при каждом движении мыши дорисовуем ещё одну линию

onMouseMove = function() {
    aa.lineTo( aa._xmouse, aa._ymouse)
     xx = aa._xmouse 
     yy = aa._ymouse
    }
}


    if (instrument == 2) { // если выбран инструмент линия
// начало аналогично первому варианту
    m++;
_root.otm.gotoAndStop(1);
_root.undo = true;
_root.norep();
    var ttt = this.createEmptyMovieClip("ff" + m, m);
    
    ttt._x = 0;
    ttt._y = 0;
// запоминаем положение курсора
    xx = this._xmouse;
    yy = this._ymouse;
    
    onMouseMove = function():Void {
    ttt.clear(); // очищаем клип
    // применяем стиль
    ttt.lineStyle(_root.diam, _root.kolor, _root.al);
// рисуем линию из первой точки (при нажатии на кнопку)
// в то место, где сейчас курсор
    ttt.moveTo(xx, yy);
    ttt.lineTo(this._xmouse, this._ymouse);
    
        }
    }


    if (instrument == 3) { //если выбран инструмент прямоугольник 
// всё то же самое: создание клипа, применение стиля, 
// запоминание позиции курсора
    m++;
ot = m;
_root.otm.gotoAndStop(1);
_root.undo = true;
_root.norep();
    var ttt = this.createEmptyMovieClip("ff" + m, m);
    trace(ttt);
    ttt._x = 0;
    ttt._y = 0;
    xx = this._xmouse;
    yy = this._ymouse;
    onMouseMove = function():Void {
    ttt.clear();

    ttt.lineStyle(_root.diam, _root.kolor, _root.al);
    ttt.moveTo(xx, yy);
    // рисуем сам прямоугольник
    ttt.lineTo(this._xmouse, yy); 
    ttt.lineTo(this._xmouse, this._ymouse);
    ttt.lineTo(xx, this._ymouse);
    ttt.lineTo(xx, yy);

    }
}
// наращиваем счётчик клипов на 1
    n++;
}
// добавляем обработчик отпускания кнопки
on (release, releaseOutside) {
    delete onMouseMove;
}

Теперь сделаем рабочими кнопки Отменить и Повторить.
На кнопку Отмена вешаем
on (rollOver) {
    if (_root.undo) { 
// undo – булева величина, хранит сведения о том, есть ли что сейчас отменить
        gotoAndStop(2);
    }
}
on (rollOut, releaseOutside) {
    if (_root.undo) {
    gotoAndStop(1);
    } else {
        gotoAndStop(3);
    }
}

on (release) {

if (_root.undo) {
// скрываем самый последний клип
_root.pole["ff"+_root.pole.m]._alpha = 0;
// уменьшаем счётчик
_root.pole.m--;
// теперь мы можем повторить
_root.rep = true;
_root.povt.gotoAndStop(1);
    
    }
    
    if (_root.pole.m == 1) {
        _root.undo = false;
        gotoAndStop(3);
    }
    
}

На кнопку Повторить:
on (rollOver) {
    if (_root.rep) {
        gotoAndStop(2);
    }
}


on (rollOut, releaseOutside) {
    if (_root.rep) { // если можно повторить
    gotoAndStop(1);
    } else {
        gotoAndStop(3);
    }
}

on (release) {
    if (_root.rep) {
    _root.pole.m++;
// показываем клип
_root.pole["ff"+_root.pole.m]._alpha = 100;
}

q = _root.pole.m+1;
// проверяем, можно ли ещё повторять
if (_root.pole["ff"+q] == undefined) { // если нельзя
    _root.rep = false;
    gotoAndStop(3);
}
}

На кнопку Очистить вешаем
on (release) {
var i = _root.pole.m;
while (true) { // Бесконечный цикл
if (_root.pole.m <= 1) {
break; // Если переменная i будет <= 1
// цикл прервется
}
// удаляе все клипы
_root.pole["ff"+_root.pole.m].removeMovieClip();
// уменьшаем счётчик
_root.pole.m--;
}
_root.norep(); // нельзя повторить
_root.undo = false; //нельзя отменить
_root.otm.gotoAndStop(3);
}

Делаем выбор цвета. На палитру вешаем
on (release) {
// делаем снимок флешки
    import flash.display.BitmapData;
   var bmp:BitmapData = new BitmapData(598, 460, false);
   bmp.draw(_root);
// запоминаем координаты курсора
        XXX = _root._xmouse;
        YYY = _root._ymouse;
// получаем цвет пикселя по этим координатам
        _root.kolor = "0x" + bmp.getPixel(XXX, YYY).toString(16);
       
    // применяем новый цвет к индикатору
        color1 = new Color(_root.smp.k);
        color1.setRGB(_root.kolor);
}

Выбор диаметра. Сам ползунок делаем MovieClip, называем p. Внутри него делаем кнопку, называем b. На неё вешаем:
on (press) {
    this.startDrag(false, 0, 0, 80, 0);
    this.onMouseMove = function() {
        _root.diam = Math.round((_root.diametr.p._x / 80) * 30 + 2);
        _root.smp.k._width = _root.diam;
        _root.smp.k._height = _root.diam;
        
    }
}

on (release, releaseOutside) {
    this.stopDrag();
    delete onMouseMove;
}

На «дорожку» под ползунком вешаем
on (release) {
    _parent.p._x = this._xmouse;
    _root.diam = Math.round((_root.diametr.p._x / 80) * 30 + 2);
    _root.smp.k._width = _root.diam;
    _root.smp.k._height = _root.diam;
    
}

Аналогично делаем выбор жёсткости, на кнопку b вешаем
on (press) {
    this.startDrag(false, 0, 0, 80, 0);
    this.onMouseMove = function() {
        _root.al = Math.round((_root.opacity.p._x / 80) * 100);
        _root.smp.k._alpha = _root.al;
                
    }
}

on (release, releaseOutside) {
    this.stopDrag();
    delete onMouseMove;
}

На «дорожку»:
on (release) {
    _parent.p._x = this._xmouse;
        _root.al = Math.round((_root.opacity.p._x / 80) * 100);
        _root.smp.k._alpha = _root.al;
}

Ну вот и всё, если вы правильно всё собрали, то вы получите флеш рисовалку намного круче, чем на Вконтакте!

Добавлено: 14 Августа 2013 12:03:48 Добавил: Андрей Ковальчук

P2P-Чат, прощай сервер.Урок., Обычный чат,без серверных технологий.

Было много разговоров про пиринговые сети,но вроде еще ни кто про них урок не написал,вот я и решился ознакомить всех со зверем по имени стратус,скажу сразу пример скромный,я не пытался создать высоко коммерческий чат,а задача была донести людям смысл и простату работы p2p в as3.Результат.
Получим вот такой простенький чат, без использования серверной части...т.е. полностью на flash.(для проверки откройте пару окон с этой страницей,ну или если кто будет в сети можете с ним по переписываться)Чему вы научитесь в этом уроке.
Вы узнаете о новых классам плеера 10.1, позволяющим передавать данные, без использования серверной части, на основе технологии p2p.Что я использовал,при создании проекта.
FD и Flex SDK 4.5,а так же пакет библиотеки для компонентов. Скачать можно тут.Начинаем программировать.Для начала создадим класс события помощника HelperEvent.as:

package kor.events {

import flash.events.*

/**
 * ...
 * @author koras_dor
 */
public class HelperEvent extends Event {
 
 public static const ON_CONNECT_CLICK:String = "onConnectClick";
 public static const ON_SUBMIT_CLICK:String = "onSubmitClick";
 
 private var _nick:String
 private var _message:Object
 
 
 public function HelperEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false){
  super(type, bubbles, cancelable);
 }
 
 public override function clone():Event {
  return new HelperEvent(type, bubbles, cancelable);
 }
 
 public override function toString():String {
  return formatToString("HelperEvent", "type", "bubbles", "cancelable", "eventPhase");
 }
 public function set nick(value:String):void {
  _nick = value
 }
 public function get nick():String { return _nick; }
}
}
Здесь все и так ясно,так что объяснять не буду.Дальше создаем окно предлагающее ввести ник и подключиться к нашему чату за это будет отвечать класс WindowSprite.as:
package kor.sprites {

import flash.display.*
import flash.events.*
import flash.text.*


import com.bit101.components.*

import kor.events.*


/**
 * ...
 * @author koras_dor
 */
public class WindowSprite extends Window {
 private static const WIDTH:Number = 300
 private static const HEIGHT:Number = 200
 
 private static const TEXTFIELD_WIDTH:Number = 200
 private static const TEXTFIELD_HEIGHT:Number = 20
 
 private var _label:Label
 private var _it:InputText
 private var _btn:PushButton
 private var _preloader:ProgressBar
 
 public function WindowSprite(parent:DisplayObjectContainer = null, title:String = "", xpos:Number = 0, ypos:Number = 0) {
  super(parent, xpos, ypos, title)
  width = WIDTH
  height = HEIGHT
  draggable = false
 
  _label = new Label(this)
  _label.x = WIDTH / 2 - TEXTFIELD_WIDTH / 2
  _label.y = HEIGHT / 2 - TEXTFIELD_HEIGHT
  _label.text = "Nick";
  addChild(_label)
 
  _it = new InputText(this)
  _it.width = TEXTFIELD_WIDTH
  _it.height = TEXTFIELD_HEIGHT
  _it.x = WIDTH / 2 - TEXTFIELD_WIDTH / 2
  _it.y = HEIGHT / 2
  addChild(_it)
 
  _btn = new PushButton(this)
  _btn.x = WIDTH / 2 - TEXTFIELD_WIDTH / 2
  _btn.y = HEIGHT / 2 + TEXTFIELD_HEIGHT * 1.5
  _btn.label = "Connect."
  _btn.addEventListener(MouseEvent.CLICK,onClick)
  addChild(_btn)
 }
 private function onClick(e:MouseEvent):void {
  if (_it.text == "") {
   _label.text = "Error";
  }else {
   var helperEvent:HelperEvent = new HelperEvent(HelperEvent.ON_CONNECT_CLICK)
   helperEvent.nick = _it.text
   dispatchEvent(helperEvent)
  }
 }
 public function disable():void {
  _it.enabled = false
  _btn.enabled = false
 }
}
}
Ну и здесь тоже особо не тяжко,задаем интерфейс всплывающего окна расширяя класс window.
Дальше на очереди класс PanelSprite.as
package kor.sprites {

import flash.display.*
import flash.events.*

import com.bit101.components.*

import kor.events.*

/**
 * ...
 * @author koras_dor
 */
public class PanelSprite extends Panel {
 
 private static const WIDTH:Number = 600
 private static const HEIGHT:Number = 400
 
 private static const OFFSET_X:Number = 10
 private static const OFFSET_Y:Number = 60
 
 private var _label:Label
 private var _ta:TextArea
 private var _it:InputText
 private var _btn:PushButton
 
 public function PanelSprite(parent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number = 0 ) {
  super(parent, xpos, ypos)
  width = WIDTH
  height = HEIGHT
 }
 public function create():void {
  _label = new Label(this)
  _label.x = OFFSET_X
  addChild(_label)
 
  _ta = new TextArea(this)
  _ta.x = OFFSET_X / 2
  _ta.y = OFFSET_Y / 2
  _ta.width = WIDTH - OFFSET_X
  _ta.height = HEIGHT - OFFSET_Y
  addChild(_ta)
 
  _btn = new PushButton(this)
  _btn.x = (WIDTH - OFFSET_X) - _btn.width
  _btn.y = (HEIGHT - OFFSET_Y) + OFFSET_Y / 1.75
  _btn.addEventListener(MouseEvent.CLICK, onSubmitClick)
  _btn.label = "Submit";
  addChild(_btn)
 
  _it = new InputText(this)
  _it.x = OFFSET_X / 2
  _it.y = (HEIGHT - OFFSET_Y) + OFFSET_Y / 1.75
  _it.width = (WIDTH - OFFSET_X) - (_btn.width + OFFSET_X)
  _it.height = _btn.height
  addChild(_it)
 }
 
 private function onSubmitClick(e:MouseEvent):void {
  var helperEvent:HelperEvent = new HelperEvent(HelperEvent.ON_SUBMIT_CLICK)  
  dispatchEvent(helperEvent)
 }
 public function setNick(nick:String):void {
  _label.text = nick.toUpperCase()
 }
 public function setMessage(message:String):void {
  _ta.text += message
 }
 public function getInputMes():String {
  return _it.text
 }
 public function clearInput():void {
  _it.text = "";
 }
}
}
Ну и наш главный класс Main:
package {

import flash.display.*
import flash.events.*
import flash.net.*

import com.bit101.components.*

import kor.sprites.*
import kor.events.*

/**
 * ...
 * @author koras_dor
 */
[Frame(factoryClass="Preloader")]
public class Main extends Sprite {
 
 private static const WIDTH:Number = 800
 private static const HEIGHT:Number = 600
 
 
 private const SERVER:String = "rtmfp://p2p.rtmfp.net/";//сервер
 private const DEVKEY:String = "ваш_ключ_разработчика";// получить его можно http://www.adobe.com/cfusion/entitlement/index.cfm?e=cirrus
 private const GROUP:String = "demiart";// это название нашего канала
 
 private var _panel:PanelSprite
 
 private var _netConnection:NetConnection
 private var _netStream:NetStream
 private var _netGroup:NetGroup
 
 private var _nick:String
 private var _sequence:Number = 0

 public function Main() {
  if (stage) {
   init();
  }else {
   addEventListener(Event.ADDED_TO_STAGE, init);
  }
 }

 private function init(e:Event = null):void {
  removeEventListener(Event.ADDED_TO_STAGE, init);
 
  var window:WindowSprite = new WindowSprite(this, "Enter your name/nick.")
  window.x = WIDTH / 2 - window.width / 2
  window.y = HEIGHT / 2 - window.height / 2
  window.addEventListener(HelperEvent.ON_CONNECT_CLICK, onConnectBtnClick)
  addChild(window)
 }
 private function onConnectBtnClick(e:HelperEvent):void {
  e.target.disable()
  _nick = e.nick
 
  _panel = new PanelSprite(this)
  _panel.x = WIDTH / 2 - _panel.width / 2
  _panel.y = HEIGHT / 2 - _panel.height / 2
  addChild(_panel)
 
//Подключаемся к серверу
  _netConnection = new NetConnection()
  _netConnection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus)
  _netConnection.connect(SERVER + DEVKEY)
 }
//Событие которое слушает состояние подключения и в зависимости от него отсылает на азные функции  
 private function onNetStatus(e:NetStatusEvent):void {
  switch(e.info.code) {
   case "NetConnection.Connect.Success" :
   onConnect()
   break
   case "NetStream.Connect.Success" :
   onStreamConnect()
   break
   case "NetGroup.Connect.Success" :
   onGroupConnect()
   break
   case "NetGroup.Posting.Notify" :
   posting(e.info.message)
   break
  }
 }
 
 //Мы подсоединились.
 private function onConnect():void {
//Настраиваем нашу группу,т.е. даем ей имя и соответствующие возможности для нашего чата.
  var gs:GroupSpecifier = new GroupSpecifier(GROUP)
  gs.multicastEnabled = true
  gs.postingEnabled = true
  gs.serverChannelEnabled = true

  _netStream = new NetStream(_netConnection, gs.groupspecWithAuthorizations())
  _netStream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus)

  _netGroup = new NetGroup(_netConnection, gs.groupspecWithAuthorizations())
  _netGroup.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus)
 }
//Делаем наш плеер клиентом.
 private function onStreamConnect():void {
  _netStream.client = this
 }
//Группа подключилась создаем экземпляра нашей панели и передаем в него ник и слушаем клик кнопки submit.
 private function onGroupConnect():void {
  _panel.create()
  _panel.setNick(_nick)
  _panel.addEventListener(HelperEvent.ON_SUBMIT_CLICK, onSubmitClick)
 }
 
//Кликнули на submitю.
 private function onSubmitClick(e:HelperEvent):void {
                       //Создается объект(пакет данных) который должен содержать имя пользователя,текст сообщение,ваш id и заголовок или счетчик который должен быть уникальным.
  var mes:Object = new Object()
  mes.user = _nick
  mes.text = _panel.getInputMes()
  mes.sequnce = _sequence++
  mes.sender = _netConnection.nearID
  _netGroup.post(mes)
 
  _panel.setMessage("[" + mes.user + "]" + " - " + mes.text + "
");
  _panel.clearInput()
 }
 private function posting(mes:Object):void {
  _panel.setMessage("[" + mes.user + "]" + " - " + mes.text + "
");
 }
}
}
Сразу предупрежу,что русский текст не поддерживают эти компоненты.Сделал не на флексе специально, что бы попытаться донести до вас не только возможность передачи пиров, но и небольшие принципы ООП.

Добавлено: 14 Августа 2013 11:59:02 Добавил: Андрей Ковальчук

Создание flash mp3 плеера с плей-листом (xml)

Перед вами урок по созданию flash mp3 плеера с плейлистом (xml), который можно поставить себе на сайт. Урок будет полезен начинающим программировать на actionscript 2, так как мы будем учиться разбирать xml формат данных и работать с классом Sound.
Скорее всего, вы уже много раз встречали на сайтах подобные mp3 плееры, позволяющие прослушать песню прямо на сайте, не скачивая её к себе на компьютер (так называемой, потоковое воспроизведение). Сейчас мы с вами сделаем сами такой флеш mp3 плеер (flash actionscript 2), который вы сможете установить на свой сайт. Если вдруг у вас что-то не получится, вы можете бесплатно скачать исходник flash mp3 плеера, который я сделал для примера. Наш плеер будет брать плейлист из внешнего .xml файла, то есть количество треков не является фиксированным и ничем не ограничено.
В нашей флешке – mp3 плеере будет всего два кадра: первый – пустой, второй – сам плеер. Пока не загрузится xml файл с плейлистом, флешку будет стоять на первом кадре. В первый кадр пишем код:

// для того, чтобы флешка не масштабировалась при изменении размеров
Stage.scaleMode = 'noScale';
// для того, чтобы флешка могла работать с кириллическими символами
System.useCodepage = false;
// останавливаем флешку на первом кадре,
// и ждём пока загрузится xml 
// после загрузки переводим нашу флешку на второй кадр,
// с нашим mp3 плеером
stop();
playlist = new XML();
playlist.ignoreWhite = true;
playlist.onLoad = function() {
    _root.gotoAndStop(2);
}

playlist.load("playlist.xml");


Теперь переходим на второй кадр и начинаем делать все функциональные элементы плеера: кнопки и список воспроизведения. Плеер, который я сделал для примера, не отличается шикарным дизайном, ещё раз повторюсь, это только пример smile
Создание flash mp3 плеера с плей-листом (xml)



Названия (instance name) мувиклипов:
play_but – кнопка play;
pause_but – кнопка pause. Она находится точно под кнопкой play.
stop_but – кнопка stop;
scroll – клип со скроллером (полосой прокрутки) для списка воспроизведения (плейлиста). В нём находится горизонтальная линия, а также сам бегунок, его instance name – bar;
Клип – список воспроизведения называется pl. В нём находятся рамка, пустой клип-контейнер (container), маска для него (m), такого же размера, как и рамка;
vol – регулятор громкости. Внутри него – горизонтальная линия-подложка и линия другого цвета (bar), которая будет показывать уровень громкости;
position – отображение прогресса загрузки и воспроизведения. На самом нижнем слое находится подложка, выше – линия другого цвета (mp3_load), отображающая загрузку mp3 трека, ещё выше – линия, показывающая позицию воспроизведения (bar).
В самом верху есть динамическое текстовое поле (var = song), которое показывает название играющей песни.
Также необходимо сделать клип – элемент плейлиста. В моём примере он устроен так: в самом низу находится клип fon (как бы картинка – подложка), в нём есть три кадра, которые будут сменяться при наведении мыши и выборе песни (первый кадр – неактивное состояние, второй – при наведении мыши, третий – если играет эта песня), в первом кадре этого клипа висит
stop();

Над клипом fon находятся два текстовых поля, для отображения названия песни и длительности (var label и time). Клипу – элементу плейлиста нужно дать идентификатор экспорта item. В его первом (и единственном) кадре находится код:
// делаем обработчики кликов на xml плейлист (выбор песни)
// а также смену фона при наведении/уходе курсора с элемента списка воспроизведения
// При наведении на пункт:
fon.onRollOver = function () {
        if (_root.isPlaying != i) { // если сейчас играет не эта песня,
            fon.gotoAndStop(2); // то переводим мувилип fon на второй кадр
    }
}
// При уведении курсора и при отпускании кнопки мыши вне пункта списка
fon.onRollOut = fon.onReleaseOutside = function () {
        if (_root.isPlaying != i) {// если сейчас играет не эта песня,
            fon.gotoAndStop(1);// то переводим мувилип fon обратно на первый кадр
    }
}

fon.onRelease = function() {
// playSong – функция, при вызове которой будет проигрываться песня номер i
    _root.playSong(i);
}


Для того, чтобы проверять наш плеер, создадим xml плейлист (playlist.xml) в одной папке с нашим mp3 плеером. Его содержание должно быть примерно таким:
<data>
<item url="1.mp3" label="Stigmata – Цена твоей жизни" time ="3:31" />
<item url="2.mp3" label="Mushroomhead - These Filthy Hands" time ="5:23" />
<item url="3.mp3" label="###### - Мёртвые тела" time ="4:13" />
<item url="4.mp3" label="Bullet For My Valentine - Waking the demon" time ="4:14" />
<item url="5.mp3" label="Slipknot - Psychosocial " time ="4:43" />
</data>


Атрибут url хранит ссылку на mp3 файл, label – название трека, time – время (продолжительность) песни.
Переходим на временную диаграмму, во второй кадр вставляем код (комментарии по ходу):
// инициализация
yy = 5; // размещать элементы плейлиста будем от 5
dif = 3; // расстояние по высоте между элементами в списке
url = []; // массив для хранения ссылок на песни
// перебираем все элементы xml файла плей-листа нашего mp3 плеера
for (i=0; i<playlist.firstChild.childNodes.length; i++) {
    mc = _root.pl.container.attachMovie('item', 'item'+i, i);
    // в список воспрозведения из библиотеки добавляем мувиклип item 
    mc._x = 5; // немного сдвигаем его по оси _x
    mc._y = yy; // назначаем новую координату по _y
    yy += mc._height+dif; // считаем координаты для следующего элемента плей-листа
    dataHolder = playlist.firstChild.childNodes[i];
// в каждый элемет плейлиста (списка) пишем несколько переменных:
    mc.label = dataHolder.attributes.label; // название песни
    mc.time = dataHolder.attributes.time; // время звучания песни
    mc.i = i; // в каждый пункт списка пишем его номер
    mc.playing = false; // значит, что текущая песня не является выбраной в данный момент
    url[i] = dataHolder.attributes.url; // заносим ссылку на mp3 файл в массив
}
///////////////////////////
// для того, чтобы текст в динамических полях отображался, маску нужно применять программно.
pl.container.setMask(pl.m);// что мы и делаем =)
// если список умещается в контейнер, то полоса прокрутки (скроллер) нам не нужен.
if (pl.container._height < pl.m._height) { // проверяем
    scroll._visible = false; // если это так, скрываем скроллер
}

// если скроллер нам всё-таки нужен
// при нажатии на ползунок
scroll.bar.onPress = function() {
// начинаем его перетаскивать от самого верху (0) до низа-выоста ползунка
    scroll.bar.startDrag(false, 4, 0, 4, scroll._height-scroll.bar._height);
        scroll.onMouseMove = function () { // при движении курсора
        // двигаем контейнер со списком воспроизведения на нужное расстояние
        pl.container._y = 0-((pl.container._height-pl.m._height+10) * scroll.bar._y / (scroll._height-scroll.bar._height));
    }
}
// когда пользователь отпускает кнопку мыши, нужно остановить движение ползунка
scroll.bar.onRelease = scroll.bar.onReleaseOutside = function() {
    scroll.bar.stopDrag();
}

////////////////

isPlaying = null; // изначально при заходе на страницу песня у нас не играет
//////////////////////
// функция playSong принимает номер песни, которую нужно играть как параметр
//////////////////////////
function playSong(mp3) {
    sound = new Sound(); // создаём новый обьект класса Sound
    sound.loadSound(url[mp3], true); // вызываем метод loadSound 
    // первый параметр - ссылка, откуда нужно грузить mp3 трек (берётся из массива url)), 
    // второй параметр означает, что можно проигрывать звук, не дожидаясь его полной загрузки
    // (потоковое воспроизведение)
    // нужно "выделить" пункт, который сейчас играет
    pl.container['item'+mp3].fon.gotoAndStop(3);
    // также нужно перевести в неактивное состяние пункт списка, который играл до этого
    pl.container['item'+isPlaying].fon.gotoAndStop(1);
    // isPlaying - номер песни, котора сейчас играет, переопределяем её
    isPlaying = mp3;
    // song - навзвание mp3 трека вверху
    song = pl.container['item'+isPlaying].label;
    // скрываем кнопку play, показываем кнопки pause и stop
    play_but._visible = false;
    pause_but._visible = true;
    stop_but._visible = true;
    
// ставим шириниу индикатора загрузки mp3 равной 0
position.mp3_load._width = 0;
// точно то же для индикатора позиции mp3 файла
position.bar._width = 0;
    
}

// регулировка громкости
vol.onRelease = function() {
    vol.bar._width = vol._xmouse;
    sound.setVolume(vol._xmouse/vol._width * 100);
}
// изначально пользователь не видит кнопок play, pause и stop
play_but._visible = false;
pause_but._visible = false;
stop_but._visible = false;
// ставим шириниу индикатора загрузки mp3 равной 0
position.mp3_load._width = 0;
// точно то же для индикатора позиции mp3 файла
position.bar._width = 0;

// остановка воспроизведения (кнопка stop)
stop_but.onRelease = function() {
sound.stop(); // останавливаем воспроизведение
// скрываем кнопки play, pause и stop
play_but._visible = false;
pause_but._visible = false;
stop_but._visible = false;
// текущую песню (в плейлисте) переводим в "неактивное" положение (первый кадр)
pl.container['item'+isPlaying].fon.gotoAndStop(1);
isPlaying = null; // отмечаем, что сейчас не играет не одна песня
// ставим шириниу индикатора загрузки mp3 равной 0
position.mp3_load._width = 0;
// точно то же для индикатора позиции mp3 файла
position.bar._width = 0;
}
// пауза воспроизведения
pause_but.onRelease = function() {
    pause_but._visible = false; // скрываем кнопку пауза
    play_but._visible = true; // показываем кнопку play
    stop_pos = sound.position/1000; // запоминаем позицию, где мы остановили воспроизведение
// как видите, у обьектов класса Sound есть интересная особенность,
// свойство position возвращает позицию воспроизведения в миллисекундах,
// в то время как метод start принимает время в секундах
    sound.stop(); // осанавливаем воспроизведение
}
// продолжение воспроизведения (play)
play_but.onRelease = function() {
    pause_but._visible = true; // показываем кнопку пауза
    play_but._visible = false; // скрываем кнопку play
    sound.start(stop_pos); // стартуем воспроизведение с позиции паузы
    
}
// функция, которая будет выполняться при каждой смене кадра (обновление позиции индикаторов)
onEnterFrame = function() { 
    if (isPlaying != null) { // если сейчас играет какая-либо песня
    t = sound.getBytesTotal(); // получаем полный размер песни (в байтах)
    l = sound.getBytesLoaded(); // число загруженых байт
// в зависимости от размера части загруженого mp3 трека, меняем ширину mp3_load
    position.mp3_load._width = Math.round(l/t * position._width);
// также в зависимости от прогресса воспроизведения меняем ширину мувиклипа bar
    position.bar._width = Math.round(sound.position/sound.duration * position._width);
    }
}
// нажатие на бегунок Прогресс
position.onRelease = function() {
    if (position._xmouse < position.mp3_load._width) { // если позиция, которую выбрал пользователь уже загружена
        // то перематываем mp3 файл на нужное место
        sound.start((sound.duration/1000) * position._xmouse/position._width );
    }
}


Вот собственно и всё, «собрав» всё правильно, вы получите отличный flash mp3 плеер для своего сайта!

Добавлено: 14 Августа 2013 07:10:07 Добавил: Андрей Ковальчук