Хорошо забытое старое
Нашел давний свой код для организации потоков в флеше. Немножко почистил и добавил описание.
Часто бывает так, что нужно сделать, к примеру, красивое анимированное меню, пункты которого движутся плавно и не мешают друг другу.
Самый простой технически и самый трудоемкий вариант - сделать переход между каждыми двумя пунктами руками.
Но представьте, что для четырех пунктов надо сделать 24 перехода (да, да, двадцать четыре - посчитайте сами на досуге).
Тут начинаются вопросы - а как сделать так, чтобы если я быстро провожу по меню, пункты не открывались полностью, чтобы закрывались не с полностью раскрытого состояния, а с текущего...
Так что если есть желание сделать красивое и правильно реагирующее меню, не обойтись без предварительной работы.
Тот же код может использоваться и во множестве других мест, где Вашему Флеш-приложению придется выполнять последовательность действий.
Основная проблема в том, что в флеше не существует понятия "потоки" или "нити" (threads). Единственным приближением к этому можно считать сами мувиклипы, но нельзя, например, программно положить функцию в произвольный кадр клипа.
Поэтому для реализации всех вышеприведенных функций я разработал класс Thread. Базовая идея этого класса заключается в том, что он содержит массив действий и выполняет их в указанном порядке
Что может объект этого класса (в дальнейшем нить):
Вызывать другие функции
Ждать указанное количество кадров или миллисекунд
Ждать до момента проигрывания указанного кадра (например, нить показа пункта меню может ждать, пока предыдущий пункт меню не скроется полностью)
Переходить между действиями по условию или без условий
Выставлять и убирать флаги сигналов (т.е. указаний на внутреннее состояние этой нити)
Ждать флагов, выставляемых другой нитью.
Стоит учесть, что вызов функции позволяет дописывать нить по ходу ее выполнения, или создавать вспомогательные нити, останавливать основную нить и ждать, пока отработают вспомогательные.
Удобно и красиво, правда?
Список свойств:
Название: autoStart.
тип: boolean
Действие: Нить начинает отрабатываться сразу же, как только в нее добавляются команды.
Список функций:
Функция: init
Параметры: owner
Метод вызывается при инициализации нити, как объекта. Параметр owner указывает на контекст нити (мувиклип, в котором нить будет выполняться). Контекстом по умолчанию считается _root
Функция: addCondition
Параметры: target, condition, address1, address2
Условный переход по условию condition в контексте target. Если контекст не задан, используется контекст нити. Если condition==true, происходит переход на адрес address1, в противном случае на address2. Если адрес является строковым значением, происходит смещение на указанное количество действий, например "-2" или "5", если же адрес является числом, происходит переход на указаннный номер действия.
Функция: addFunctionCall
Параметры: interval, target, function, arguments
Вызов функции function в контексте target с параметрами arguments через interval миллисекунд. Если опустить interval, функция выполнится через одну миллисекунду. Если опустить параметр target, функция выполнится в контексте нити
Функция: addGoto
Параметры: address
Перемещение внутреннего указателя нити на указанное действие. Если параметр address является строковым, происходит смещение на указанное количество действий, например "-2" или "5", если же address является числовым, происходит переход на указаннный номер действия.
Функция: addSetSignal
Параметры: signal
Вызвать сигнал signal
Функция: addStop
Параметры: нет
Остановка работы нити
Функция: addWait
Параметры: value
Подождать value кадров, если value - текстовый параметр, и value миллисекунд в обратном случае
Функция: addWaitFrame
Параметры: target, value
Подождать, пока мувиклип target не доиграет до кадра value. Если target отсутствует, нить ждет пока ее контекст не дойдет до указанного кадра
Функция: addWaitSignal
Параметры: thread, signal
Подождать, пока нить thread не вызовет сигнал signal
Функция: run
Параметры: нет
Принудительный запуск нити
Функция: stop
Параметры: нет
Принудительный останов нити
А теперь перейдем непосредственно к коду
Часто бывает так, что нужно сделать, к примеру, красивое анимированное меню, пункты которого движутся плавно и не мешают друг другу.
Самый простой технически и самый трудоемкий вариант - сделать переход между каждыми двумя пунктами руками.
Но представьте, что для четырех пунктов надо сделать 24 перехода (да, да, двадцать четыре - посчитайте сами на досуге).
Тут начинаются вопросы - а как сделать так, чтобы если я быстро провожу по меню, пункты не открывались полностью, чтобы закрывались не с полностью раскрытого состояния, а с текущего...
Так что если есть желание сделать красивое и правильно реагирующее меню, не обойтись без предварительной работы.
Тот же код может использоваться и во множестве других мест, где Вашему Флеш-приложению придется выполнять последовательность действий.
Основная проблема в том, что в флеше не существует понятия "потоки" или "нити" (threads). Единственным приближением к этому можно считать сами мувиклипы, но нельзя, например, программно положить функцию в произвольный кадр клипа.
Поэтому для реализации всех вышеприведенных функций я разработал класс Thread. Базовая идея этого класса заключается в том, что он содержит массив действий и выполняет их в указанном порядке
Что может объект этого класса (в дальнейшем нить):
Вызывать другие функции
Ждать указанное количество кадров или миллисекунд
Ждать до момента проигрывания указанного кадра (например, нить показа пункта меню может ждать, пока предыдущий пункт меню не скроется полностью)
Переходить между действиями по условию или без условий
Выставлять и убирать флаги сигналов (т.е. указаний на внутреннее состояние этой нити)
Ждать флагов, выставляемых другой нитью.
Стоит учесть, что вызов функции позволяет дописывать нить по ходу ее выполнения, или создавать вспомогательные нити, останавливать основную нить и ждать, пока отработают вспомогательные.
Удобно и красиво, правда?
Список свойств:
Название: autoStart.
тип: boolean
Действие: Нить начинает отрабатываться сразу же, как только в нее добавляются команды.
Список функций:
Функция: init
Параметры: owner
Метод вызывается при инициализации нити, как объекта. Параметр owner указывает на контекст нити (мувиклип, в котором нить будет выполняться). Контекстом по умолчанию считается _root
Функция: addCondition
Параметры: target, condition, address1, address2
Условный переход по условию condition в контексте target. Если контекст не задан, используется контекст нити. Если condition==true, происходит переход на адрес address1, в противном случае на address2. Если адрес является строковым значением, происходит смещение на указанное количество действий, например "-2" или "5", если же адрес является числом, происходит переход на указаннный номер действия.
Функция: addFunctionCall
Параметры: interval, target, function, arguments
Вызов функции function в контексте target с параметрами arguments через interval миллисекунд. Если опустить interval, функция выполнится через одну миллисекунду. Если опустить параметр target, функция выполнится в контексте нити
Функция: addGoto
Параметры: address
Перемещение внутреннего указателя нити на указанное действие. Если параметр address является строковым, происходит смещение на указанное количество действий, например "-2" или "5", если же address является числовым, происходит переход на указаннный номер действия.
Функция: addSetSignal
Параметры: signal
Вызвать сигнал signal
Функция: addStop
Параметры: нет
Остановка работы нити
Функция: addWait
Параметры: value
Подождать value кадров, если value - текстовый параметр, и value миллисекунд в обратном случае
Функция: addWaitFrame
Параметры: target, value
Подождать, пока мувиклип target не доиграет до кадра value. Если target отсутствует, нить ждет пока ее контекст не дойдет до указанного кадра
Функция: addWaitSignal
Параметры: thread, signal
Подождать, пока нить thread не вызовет сигнал signal
Функция: run
Параметры: нет
Принудительный запуск нити
Функция: stop
Параметры: нет
Принудительный останов нити
А теперь перейдем непосредственно к коду
#initclip
//
// Thread engine. Freeware. (c) 2006 Ilya Shlyakhovoy, info@regenerate.ru
//
Thread=function(owner) {
this.init(owner)
}
Thread.prototype.init=function(owner) {
if (owner!=undefined) {
this._parent=owner;
} else {
this._parent=_root;
}
this.$PC=0;
this.$queue=[];
this.$signals=[];
this.$signal=undefined;
this.$delay=1;
this.$waiting=false;
this.$classname='thread';
this.addProperty('autoStart',this.ge tAutoStart,this.setAutoStart);
this.autoStart=true;
ASSetPropFlags(this,null,131);
}
Thread.prototype.getAutoStart=function() {
if (typeof(this.onEnterFrame)=='function' or this.$waiting) return false;
return this.$as;
}
Thread.prototype.setAutoStart=function(a s) {
this.$as=as;
}
Thread.prototype.checkRun=function() {
if (this.$interval==undefined and this.autoStart) this.run();
}
Thread.prototype.run=function() {
if (this.$interval==undefined) {
this.$delay=1;
clearInterval(this.$interval) ;
this.$interval=setInterval(t his.process,this.$delay,this);
}
}
Thread.prototype.stop=function() {
clearInterval(this.$interval);
this.$interval=undefined;
}
Thread.prototype.$procfunction_0=functio n(current) {
clearInterval(this.$interval);
this.$interval=undefined;
this.$delay=1;
this.$PC++;
}
Thread.prototype.$procfunction_1=functio n(current) {
if (typeof(current.value)=='string') {
clearInterval(this.$interval) ;
this.$interval=undefined;
this.$queue[this.$PC].passed=0;
this.onEnterFrame=function() {
if (++this.$queue[this.$PC].passed>=Number(t his.$queue[this.$PC].value)) {
delete this.onEnterFrame;
this.$PC++;
this.$delay=1;
this.$interval=setInterv al(this.process,this.$delay,this);
}
}
} else {
if (current.value!=this.$delay) {
clearInterval(this.$inte rval);
this.$interval=setInterval(t his.process,current.value,this);
}
this.$PC++;
}
}
Thread.prototype.$procfunction_2=functio n(current) {
clearInterval(this.$interval);
this.$interval=undefined
this.onEnterFrame=function() {
if (this.$queue[this.$PC].where._currentfra me==Number(this.$queue[this.$PC].value)) {
delete this.onEnterFrame;
this.$PC++;
this.$delay=1;
this.$interval=setInterval(t his.process,this.$delay,this);
};
};
}
Thread.prototype.$procfunction_3=functio n(current) {
clearInterval(this.$interval);
this.$interval=undefined;
this.$wait=true;
this.$PC++;
this.$signal=current.signal;
current.thread.getSignal(this);
}
Thread.prototype.$procfunction_4=functio n(current) {
var wh=current.where,fn=current.func;
if (typeof(current.where)=='string') wh=eval(current.where);
if (typeof(current.func)=='string') fn=eval(current.func);
fn.apply(wh,current.args);
var dtt=Number(current.delay) || 1;
this.$PC++;
if (dtt!=this.$delay) {
this.$delay=dtt;
clearInterval(this.$interval);
this.$interval=setInterval(this.proc ess,this.$delay,this);
};
}
Thread.prototype.$procfunction_5=functio n(current) {
if (typeof(current.value)=='string') {
this.$PC+=Number(current.value);
} else {
this.$PC=current.value;
};
clearInterval(this.$interval);
this.$delay=1;
this.$interval=setInterval(this.proc ess,this.$delay,this);
}
Thread.prototype.$procfunction_6=functio n(current) {
for (var a in this.$signals) if (this.$signals[a].checkSignal(current.si gnal)) {
delete this.$signals[a];
this.$signals.splice(a,1);
};
this.$PC++;
clearInterval(this.$interval);
this.$delay=1;
this.$interval=setInterval(this.proc ess,this.$delay,this);
}
Thread.prototype.$procfunction_7=functio n(current) {
if (current.where[current.condition]) {
current.value=current.addres s1;
} else {
current.value=current.addres s2;
};
if (typeof(current.value)=='string') {
this.$PC+=Number(current.value);
} else {
this.$PC=current.value;
};
clearInterval(this.$interval);
this.$delay=1;
this.$interval=setInterval(this.proc ess,this.$delay,this);
}
Thread.prototype.process=function(ths) {
this=ths;
if (this.$PC>=this.$queue.length) {
clearInterval(this.$interval) ;
this.$interval=undefined;
return;
};
var current=this.$queue[this.$PC];
this['$procfunction_'+current.ac tion](current);
}
Thread.prototype.getSignal=function(thrd) {
this.$signals.push(thrd);
}
Thread.prototype.checkSignal=function(si gn){
if (this.$signal==sign and this.$signal!=undefined) {
this.$signal=undefined;
this.$interval=setInterval(t his.process,1,this);
this.$wait=false;
return true;
}
return false;
}
Thread.prototype.addStop=function(value) {
this.$queue.push({action:0});
this.checkRun();
}
Thread.prototype.addWait=function(val) {
this.$queue.push({action:1,value:v al});
this.checkRun();
}
Thread.prototype.addWaitFrame=function(w hr,val) {
if (typeof(whr)=='number') {
this.$queue.push({action:2,w here:this._parent,value:whr});
} else if (typeof(whr)=='string') {
this.$queue.push({action:2,w here:this._parent,value:Number(whr)});
} else {
this.$queue.push({action:2,w here:whr,value:Number(val)});
}
this.checkRun();
}
Thread.prototype.addWaitSignal=function(t hrd,sign) {
if (thrd.$classname=='thread') this.$queue.push({action:3,thread:thrd,s ignal:sign});
this.checkRun()
}
Thread.prototype.addFunctionCall=functio n() {
if (arguments.length==0) return;
var dt=1,ac=0,whr;
if (typeof(arguments[ac])=='number') {
dt=arguments[ac];
ac++;
}
if (typeof(arguments[ac])!='function') {
whr=arguments[ac];
ac++;
} else {
whr=this._parent;
}
this.$queue.push({action:4,where:w hr,func:arguments[ac],delay:dt,args:argu ments.slice(ac+1)});
this.checkRun();
}
Thread.prototype.addGoto=function(addr) {
this.$queue.push({action:5,value:add r});
this.checkRun();
}
Thread.prototype.addCondition=function(w hr,cond,addr1,addr2) {
if (arguments.length==0) return;
var ac=0,myw;
if (typeof(arguments[0])!='movieclip') {
myw=this._parent;
} else {
ac++;
myw=whr;
}
this.$queue.push({action:7,where:m yw,condition:arguments[ac],address1:argu ments[ac+1],address2:arguments[ac+2]});
this.checkRun();
}
Thread.prototype.addSetSignal=function(s ign) {
this.$queue.push({action:6,signal:si gn});
this.checkRun();
}
#endinitclip