Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > JavaScript: Общие вопросы > Цепочка функций


Автор: Dr.Zlo 28.1.2008, 11:07
В больших скриптах сталкиваюсь с необходимостью разделения анимации и логики, т.е. к примеру сначала элемент нужно красиво показать\передвинуть\подсветить, потом загрузить в него инфу, после загрузки инфы задвинуть\переместить\раздвинуть элемент, и самое главное все эти действия нужно выполнять ПОСЛЕДОВАТЕЛЬНО, следующий этап начинается только после завершения предыдущего. В основном работаю с jQuery, но если кто знает решение с помощью стандартных средств js, пишите, буду только рад. 
Мне нужно решение из серии:

chain(animate1(), logic1(),animate2()...)

или

animate1().logic1().animate2()...

Сразу скажу, стандартное решение jQuery для callback функций анимации меня не устраивает, т.е.

$('#elem').animate({.....}, speed, 'linear', function(){ logic1() }) - это мне не подходит

Автор: ksnk 28.1.2008, 12:01
А чем не устраивает стандарное решение, можно поподробнее?



Автор: Dr.Zlo 28.1.2008, 13:53
Стандартное решение не утсраивает тем, что в таком случае в скрипте получается каша из визуальных эффектов и логических функций, к тому же если нам надо в каждую функцию передавать различные параметры, будет вообще ужас, если к примеру после анимации anim(x) надо выполнить logic(args), затем anim2(y), затем logic2(args2)  нужно исполнять что-то вроде:

Код

function anim(x, args, y, args2){
    $(x)animate({...}, speed, 'linear', function(){
        logic(args, y, args2)
    })
}

function anim2(y, args2){
    $(y)animate({...}, speed, 'linear', function(){
        logic2(args2);
    })
}

function logic(args, y, args2){
    // делаем что-то логическое c args
    anim2(y, args2)
}

function logic2(args2){
    // делаем что-то логическое c args2
    alert('Уррра! Наши мучения закончились!!!')
}

//вызов
anim(x, args, y, args2);


Другой пример: нам надо выполнять одну и ту же функцию anim() перед выполнением logic(args) и logic2(args2), тогда вообще получается жёсткая жесть:


Код

function anim(x, callback){
    $(x)animate({...}, speed, 'linear', function(){
        eval(callback)
    })
}

function logic(args){
    // делаем что-то логическое c args
}

function logic2(args2){
    // делаем что-то логическое c args2
}

// вызов
anim(x, 'logic('+args+')')

...

// и второй раз
anim(x, 'logic2('+args2+')')


И если не дай бог в args или args2 передаётся объект, тогда вообще придётся создавать глобальные переменные, а это вообще не фэн-шуй.

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

Автор: ksnk 28.1.2008, 16:12
Дык взять и написать объект chain, который представляет собой массив функций, каждая из которых вместо callback'а вызывает chain.next()
chain.next обязан выкинуть из собственного массива первую функцию и вызвать ее.

Код

var chain=[
function (){alert('start!');chain.next()},
function (){setTimeout(chain.next,3000)},
function(){alert('Hello!');chain.next()}
]
chain.next=function(){var x=chain.shift();if(x)x();}

chain.next();

пойдет?

Автор: Dr.Zlo 28.1.2008, 16:28
- во-первых, такой способ принципиально не отличается от  банального вызова следующей функции в конце предыдущей
- во-вторых опять же возникает проблема передачи переменных
- в-третьих здесь функции жёстко привязываются к цепи, а ведь идея состоит в том, что функции могут вызываться как цепочечно, так и по отдельности.
Нужна некая chain конструкция, для работы которой не пришлось бы залезать в функцию и прописывать ей callback вжёсткую. В данном случае основную проблему я вижу в том, чтоб определять момент, когда текущая функция цепочки закончила работу, чтоб передать управление следующей функции, просто мне мозга пока не хватает придумать, как это реализовать  smile 

Автор: ksnk 28.1.2008, 16:58
Dr.Zlo, Я не очень пока понимаю суть возражений. Напишите как бы вы хотели писать smile  Для начала - можно "описать" ту цепочку, которую я предложил - пара алертов через таймаут. 

Автор: Dr.Zlo 28.1.2008, 17:18
ksnk, в идеале хотелось бы так:

Код

function start(mes){
    alert(mes);
}

function process(mes){
    setTimeout('alert('+mes+')', 3000);
}

function end(){
    alert('the end!');
}

chain(start('Hello!'), process('processing'), end());


Но к сожалению уже на уровне передачи вызовов функций в качестве параметров для chain эти функции пытаются выполниться, так что можно что-то вроде :

chain([{ 'start' : ['Hello!']}, {'process' : ['processing']}, {'end' : []});

Автор: ksnk 28.1.2008, 17:47
Ну, более реально записать вот так
Код

 chain(start,'Hello!')
.chain(end)

...
function chain(){
  var x ;
  if(!this instanceof chain) {
    x=new chain();
    x.chain=chain;
  } else 
    x=this;

  // тут разбираемся с количеством и типом передаваемых нам переменных
 ...

  return $x;
}

правда с таймаутом будет затык. Только сам таймаут знает когда он закончится...

Автор: Dr.Zlo 28.1.2008, 18:13
В том-то и дело, у меня часто получаются конструкции из серии:

Код

function animate(){
    $('#x').animate({'color' : 'red'}, speed, 'linear', function(){
        $('#y').animate({'width': 300, 'height': 400}, speed*2, 'linear', function(){
            $('#z').css({'display': 'block','color' : 'red'}).animate({'marginLeft' : 100, 'marginTop': 100}, speed/2, 'linear', function(){
                return callback();
            })
        })
    })
}


Так что если никто не знает как на завершение функции повесить событие, придётся мне смириться с тем, что в каждой функции нужно будет прописывать callback, что-нибудь вроде chain.next();

Кстати, тут ещё одна проблема. Если есть функция например с тремя параметрами start(a,b,c) и мы пишем:

 chain(start : ['first', 'second', 'third']).chain(...).chain(...)

Как потом в теле chain вызвать start и передать ему эти 3 параметра? и что будет если один параметр- строка, второй- объект, 3й например число?

Автор: ksnk 28.1.2008, 18:17
Решением было бы зафиксировать интерфейс "функций анимации". К примеру - они принимают 2 параметра. Первый - параметри или  объект с реальными параметрами функции, второй - callback, который должен вызваться по завершении функции. если CallBack'а нет - не вызываем ничего.

тогда примерно вот что получается
Код

function chain(a,b){
  var x ;
  if(!(this instanceof Array)) {
    x=new Array();
//  } else if(!x.chain){
    // initialize new chain object
    x.chain=chain;
    // замкнем на себя указатель на себя
    x.next=(function(){
        var xx=x; 
        return function(){
            var x=xx.shift();
            if(x && x.a)
                x.a(x.b,xx.next)
        }
    })() ;
  } else {
    x=this;
  }
  x.push({a:a,b:b});
  return x;
}
// ***************************************
// функции анимации
function start(mes,callback){
    alert(mes);
    if(callback)callback();
}

function process(mes,callback){
    setTimeout(function(){alert(mes);if(callback)callback()}, 3000);
}

function end(x,callback){
    alert('the end!');
    if(callback)callback();
}

function compare(x,callback){
    if (x.first!=x.second)
        alert('Wow');
    else
        alert('Ok')
    if(callback)callback();
}
//**************************
// цепочка....
 chain(start,'Hello!')
.chain(process,'Message')
.chain(compare,{first:'Hello', second:'World'})
.chain(end)
.next();



Автор: ksnk 29.1.2008, 01:22
В http://forum.vingrad.ru/forum/topic-193081/15.html я уже даже сие применил... правда не особо показательно...
Окончательный интерфейсные соглашения
  • все функции анимации получают 2 параметра, второй из которых callback, обязательный к вызову по завершении операции.
  • Все функции анимации, ОБЯЗАНЫ использовать setInterval вместо setTimeout. Делить и разбираться сразу с двумя типами хандлеров мне показалось слишком круто.
  • Все функции анимации, использующие setInterval обязаны выдавать его как результат своей работы.
  • Никакой другой результат выдаваться не должен
Описание внешних функций chain 
chain() - вызов без параметров инициализирует новый объект chain 
chain(a,b) - поместить в очередь вызов функции a с праметрами b и chain.next
chain.run() - запуск очереди на выполнение. Можно вызывать неоднократно. Уже запущеннй процесс так не остановить.
chain.clear() - остановить цепочку

Каждая внешняя функция возвращает себя как результат и по этому можно писать цепочки таких вызовов...


Листинг функции chain
Код

// ***********************************
// chain
function chain(a,b,flag){
    var x ;
    if(!(this instanceof Array)) {
        x=new Array();
        // initialize new chain object
        // just to clear internal timeout
        x.stop=function(){
            if(this.timeout) 
                clearInterval(this.timeout);
            this.running=false;
            this.timeout=null;
        }
        // is animation running
        x.running=false;
        // the chain function again
        x.chain=chain;
        // function stop current and clear all  pending animations
        x.clear=function(){
            this.stop();
            while(this.length) this.pop();
            return this;
        }
        // internal function - execute next pending function
        x.exec=function(x){
            if((this.running=(x && x.a)))
                this.timeout=x.a(x.b,this.next);
        };
        // замкнем на себя указатель на себя
        x.run=function(){
            if(this.running) return this;
            else return this.next();
        };
        // common callback function
        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
                if(xx.length)
                    xx.exec(xx.shift());
                return xx;
            }
        })() ;
    } else {
        x=this;
    }
    if(a) x.push({a:a,b:b,flag:flag});
    return x;
}




Автор: Dr.Zlo 29.1.2008, 10:52
ksnk, спасибо!

Поюзал твою функцию, решил немного модифицировать, хочу чтоб выполняемые функции  не пришлось сильно кромсать и передавать объект вместо набора переменных. Вот что получилось:

Код


function start_func(mes1, mes2, mes3){
    alert(mes1+mes2+mes3)
    if (start_func.next) start_func.next();
}

function alert_func(mes){
    alert(mes)
    if (alert_func.next) alert_func.next();
}

// ***********************************
// chain
function chain(a,b,flag){
    var x ;
    if(!(this instanceof Array)) {
        x=new Array();
        // initialize new chain object
        // just to clear internal timeout
        x.stop=function(){
            if(this.timeout) 
                clearInterval(this.timeout);
            this.running=false;
            this.timeout=null;
        }
        // is animation running
        x.running=false;
        // the chain function again
        x.chain=chain;
        // function stop current and clear all  pending animations
        x.clear=function(){
            this.stop();
            while(this.length) this.pop();
            return this;
        }
        // internal function - execute next pending function
        x.exec=function(x){
            if((this.running=(x && x.a)))
                a.next = this.next;
                // Передаем 10 переменных про запас, это конечно идиотизм, но как сделать по-другому я не знаю
                this.timeout=x.a(x.b[0], x.b[1], x.b[2], x.b[3], x.b[4], x.b[5], x.b[6],x.b[7],x.b[8],x.b[9])
        };
        // замкнем на себя указатель на себя
        x.run=function(){
            if(this.running) return this;
            else return this.next();
        };
        // common callback function
        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
                xx.exec(xx.shift());
                return xx;
            }
        })() ;
    } else {
        x=this;
    }
    if(a) x.push({a:a,b:b,flag:flag});
    return x;
}


Вызов будет таким:

chain(start_func , ['Превед ',  ' всем ', ' патцанам!']).chain(alert_func , ['stop!!!']).run()

Возникает 2 проблемы:
1. После выполнения последнего элемента chain,  FireBug выдаёт ошибку:
"x has no properties"

2.  this.timeout=x.a(x.b[0], x.b[1], x.b[2], x.b[3], x.b[4], x.b[5], x.b[6],x.b[7],x.b[8],x.b[9]) - это позор программиста, но если никто не знает как это обойти и при этом передавать в функцию все переменные в нормальном виде, а не объектом, то я лучше оставлю так

Автор: ksnk 29.1.2008, 15:20
1. У меня чего-то не выдает... :(
2. написать вот так               

Код

              x.a.next = this.next; // <<< тут было неправильно!!!
                // Передаем 10 переменных про запас, это конечно идиотизм, но как сделать по-другому я не знаю
              this.timeout=x.a.apply(null,x.b);

Я, в принципе думал, что нужно бы проверять тип второго параметра  и если это массив - вызвать apply, но меня остановило то, что в жизни бывают и "просто массивы" smile

Добавлено @ 15:26
для пущего единообразия можно писать вот так.
Код

function start_func(mes1, mes2, mes3){
    alert(mes1+mes2+mes3)
    if (arguments.callee.next) arguments.callee.next();
}

function alert_func(mes){
    alert(mes)
    if (arguments.callee.next) arguments.callee.next();
}

 правда это немного подлинее  ;-)

Автор: ksnk 29.1.2008, 15:57
1. У меня тоже выдает....  :-( Посмотрим - поковыряем...

Автор: ksnk 29.1.2008, 17:24
Вот вариант, который получился в итоге. Правила оформления функций анимации несколько поменялись... 
Код

/**
 * Инициализация  цепочки  исполняемых  функций.
 * 
 * !!!!!!!!!интерфейсные соглашения о "функциях анимации"!!!!!!
 * каждая  функция  анимации  обязана  по  своему  завершению обеспечить вызов
 *     такой  функции  arguments.callee.next  либо передав ее callback'ом кому
 *     нибудь                 либо                 явно                 вызвав
 *     if(arguments.callee.next)arguments.callee.next();
 * 
 * Все  функции  анимации, ОБЯЗАНЫ использовать setInterval вместо setTimeout.
 *     Делить  и  разбираться  сразу  с  двумя типами хандлеров мне показалось
 *     слишком круто.
 *     
 * Все  функции  анимации,  использующие  setInterval обязаны выдавать его как
 *     результат своей работы.
 *     
 * Если  функция использует setInterval и не выдает его в качестве результата,
 *     исполнение этой функции не будет остановлено вызовом chain.clear();
 *     
 * Никакой другой результат выдаваться не должен
 *        
 * !!!!!!!!Описание внешних функций Chain!!!!!!!!!
 * 
 * new Cain() - вызов без параметров инициализирует новый объект Chain
 * chain.chain(a,b)  -  поместить  в  очередь  вызов функции a с праметрами b.
 *     Если   функция   принимает   несколько   параметров  -  все  параметры,
 *     необходимые  для  нее  передаются  в  массиве b. если функция принимает
 *     единственный параметр - массив - он ОБЯЗАН быть передан как [b]
 * chain.run()  -  запуск  очереди на выполнение. Можно вызывать неоднократно.
 *     Если процесс уже идет вреда не будет.
 * chain.wait(ms) - вставить в цепочку команду задержки на ms миллисекунд    
 * chain.clear() - остановить выполнение цепочки
 * 
 * Каждая  внешняя  функция  возвращает  собственный объект как результат и по
 *     этому можно писать цепочки таких вызовов...
 *     
 * @param {Object} a
 * @param {Object} b
 * @param  {Object}  flag  - установка этого параметра помечает ОБЯЗАТЕЛЬНОСТЬ
 *     вызова этого метода даже в случае clear()
 * @example var x=new Chain(); x.wait(3000).chain(exec,'xxx').run();
 * @example Chain(show,'xxx').wait(3000).chain(exec,'xxx').run();
 * @example примеры функций анимации
 * 
 * function collapse(o,_to){
 *       return animate(o,_to,0,arguments.callee.next);
 * }
 * 
 * function hideit(o){
 *     o.style.display='none';
 *     o.style.width='0px';
 *     if(arguments.callee.next)arguments.callee.next();
 * }
 */
function Chain(a,b,flag){
    var x =new Array();//Thanks 2 AKS ;-)
    x.running = false;
    // совершенно приватная функция
    // остановим таймаут, если есть...
    function stop(){
        if(x.timeout) 
            clearInterval(x.timeout);
        x.running=false;
        x.timeout=null;
    }
    // выполним следующий элемент
    function exec(xx){
        if ((x.running = (xx && xx.a))) {
            xx.a.next=x.next
            x.timeout = xx.a.apply(null,xx.b);
        }    
    };
    // common callback handler
    // замкнем на себя указатель на себя
    x.next = function(){ 
        stop();
        if (x.length) {
            exec(x.shift());
        }
        return x;
    }
    
    // main chain function
    x.chain=function(a,b,c){
        if(!a) return this;
        if(!(b instanceof Array)) b=[b];
        this.push({a:a,b:b,flag:c});
        return this;
    }
    
    // perform waiting in a row
    x.wait=function(ms){
        var xx=this;
        return this.chain(function (ms){
                return setInterval(xx.next,ms)
            },ms)
    }
    
    // stop all pending animation
    x.clear=function(){
        stop();
        var x={};
        while(this.length && (!x.flag)) x=this.shift();
        if(x && x.flag) exec(x);
        return this;
    }
    
    // run the chain in case it stopped
    x.run=function(){
        if(this.running) return this;
        else return this.next();
    };
    
    return x.chain(a,b,flag);
}


P.S. поправлена ошибка в  функции chain.wait...
P.P.S почищен код, благодаря AKS
P.p.P.S поправлен метод clear, из-за чего были глюки с исполнением "аварийного" chain'а

Автор: Dr.Zlo 29.1.2008, 18:12
Я избавился от ошибки следующим образом:

Код

        x.next=(function(){
            var xx=x; 
            return function(){
                xx.stop();
        if (xx.length==0) return false; // Если цепочка закончилась, уходим
                xx.exec(xx.shift());
                return xx;
            }
        })() ;


После этого нормально стало всё работать и не косячить после последнего элемента. Если честно я до сих пор до конца не втыкаю как работает твой chain, это решение я нашёл интуитивно.
Но по ходу ты пофиксил тем же способом и ужё ушёл намного дальше в разработку chain smile
Вечером посмотрю и протестю твой новый вариант.

Автор: AKS 29.1.2008, 20:25
Цитата(ksnk @  29.1.2008,  17:24 Найти цитируемый пост)
var x =(this instanceof Array)?this:new Array();

Смотрю и не могу понять - когда условие this instanceof Array будет истинным? Когда Вы "провернете" http://forum.vingrad.ru/forum/topic-73457/anchor-entry1396256/15.html#?

Автор: ksnk 29.1.2008, 21:35
AKS, Нееееее! Тут как раз все значительно интереснее  ;-) объект chain это и ЕСТЬ настоящий массив честно сделанный через new Array!

Добавлено через 42 секунды
this instanceof Array - проверка на то, вызывают ли нас первый раз. Тогда this указывает на объект window

Автор: ksnk 29.1.2008, 23:28
AKS, Впрочем, Вы как всегда правы! smile Проверка идиотская и в этом варианте лишняя. В оправдание могу предъявить цепочку соображений, которая привела к появлению этого кода.

Первоначальное решение было опубликовано выше несколькими постами и последующие варианты лишь "облагораживали" его.

Проявившееся желание написать chain(x).chain(y).chain(z)... породило совершенно логичную мысль о том, что каждая функция chain выдает некий объект как результат. причем первый chain в цепочке должен его (этот объект) породить. А так как по сути первый и последующие элементы цепочки одинаковы по входным параметрам, то родилась уникальная мысль написать конструкцию 
function chain(){..var x = new chain().... this.chain=chain/* Yoooo!!!*/ ...}; chain.prototype=new Array();,
 все в одном флаконе. ;-)
Когда пришла пора "со всей этой фигней взлетать", возникла задача по определению контекста вызова chain(), new chain(), и .chain'а, рудиментарное наследие от которой до сих пор сидела в коде. по дороге напоролся на "баг", не позволяющий наследоваться от Array запланированным способом. Хорошо, что внезапно посетивший меня приступ здравого смысла заставил несколько оптимизировать код, но, к сожалению, не до конца.
Так что, AKS, спасибо!

Автор: Dr.Zlo 30.1.2008, 09:32
ksnk, вчера внедрил chain в довольно большое приложение. Всё отлично работает, 

Но есть несколько пожеланий. 

1. Иногда нужно собрать chain по кусочкам, и только потом выполнить, или к примеру иногда удобней объявить chain где-нибудь в начале скрипта, а вызывать в нужных местах. Допустим у нас есть задача: для всех чётных элементов списка выполнить функцию по цепочке. Вот как я это вижу: 
 
Код

var objs=document.getElementsByTagName('li');
var x=new Chain();
for(var i=0; i<objs.length; i++){
    if ((Math.floor((i+1)/2)-Math.floor((i)/2))==0){
        var obj = objs[i]
        x.chain(animate, obj);
    }
}
x.run();


Было бы прикольно, если бы такая конструкция работала

2. После завершения chain лучше избавляться от созданного массива (обnullять его, что-ли), ведь если все эти массивы будут висеть в памяти, после нескольких вызовов chain скрипт будет заметно тормозиться

Автор: ksnk 30.1.2008, 09:44
Цитата(Dr.Zlo @  30.1.2008,  09:32 Найти цитируемый пост)
 Иногда нужно собрать chain по кусочкам, и только потом выполнить,

До тех пор пока не вызван метод run, chain остается "не запущеным".
Цитата(Dr.Zlo @  30.1.2008,  09:32 Найти цитируемый пост)
После завершения chain лучше избавляться от созданного массива
 Факт вызова очередной функции chain'а "автоматически" выкидывает первый элемент... как рожок в автомате - патрон стреляет - гильза вылетает smile

imho, если переписать вашу конструкцию вот так
Код

var objs=document.getElementsByTagName('li');
var x=Chain(); // идеология поменялась... new вызывать не надо...
for(var i=0; i<objs.length; i++){
    if (!( i | 1)) { // четные
        x.chain(animate, objs[i]);
    }
}
x.run();

То я не вижу причин почему бы она не заработала...

т.е. Все уже сделано ;-)

Автор: ksnk 30.1.2008, 10:00
Честно говоря меня преследует вопрос эстетический ;-) Удобно ли то, что "первая" функция называется Chain (с большой буквы). Можно было бы ради единообразия назвать ее chain (с маленькой), как и "собственный" метод. На каком-то этапе мне показалось идеологически более верно то, что первый элемент цепочки пишется с большой буквы. Правда до серьезного практического применения этой "идеологии" я пока не добрался. 

Автор: Dr.Zlo 30.1.2008, 10:41
По поводу именования. 
Думаю по фэн-шую было бы правильно создавать цепь, потом присоединять к ней звенья. Цепь по-английски chain, по поводу звеньев яндекс словари говорят следующее:

звено
ср.

1) link

2) (механизма или структуры) unit, section; part, branch

3) team, group, unit

4) (самолетов) flight

5) воен. level, echelon, squad 

Так как с link будут несколько другие ассоциации, то можно взять unit для определения звена цепи. 
В итоге можно договориться использовать конструкцию вроде:

chain().unit(func1, param1).unit(func2, param2).unit(func3, param3).run();

В крайнем случае оставить всё как есть, только избавиться от Chain с большой буквы, он путает и сбивает с толку, как мне кажется

Автор: ksnk 30.1.2008, 11:03
ok! Надо будет при случае поменять большую букву...
Насчет unit'а - я против... ;-) ; - я предполагал первонально, что будет вот так
chain(func1, param1).chain(func2, param2).chain(func3, param3).run(); Единая сущьность для первой и последующих итераций... Imho, пусть так и остается. Всех изменений в скрипте, если надо - переназвать "головную" функцию...

Автор: Sardar 30.1.2008, 14:53
Уже вторая страница, а я что-то не пойму сути вопроса... smile
Оно?
Код

        function chain(func) {
            var prev = this; //ссылка на предыдущую chain
            var args = []; //из-за особого чувства юмора у ECMA, arguments это не Array, метода .slice нет...
            for(var i = 1; i < arguments.length; i++) args.push(arguments[i]);

            var hnd = function() {
                if(prev instanceof Function) prev(); //у самого первого chain здесь будет window или любой другой объект, чьим методом была chain
                func.apply(window, args);
            }

            hnd.chain = arguments.callee; //цепочка
            return hnd;
        }

        chain(alert, "OK").chain(alert, "cool")(); //результат chain - функция, не забываем её вызвать

Автор: Dr.Zlo 30.1.2008, 14:58
Всё-таки отсроченный запуск chain не работает, я тестировал, просто не запускается и всё. Вот исходник:

Код

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<script language="javascript" src="jquery.js"></script>
<script>
$(document).ready(function(){
    var objs=document.getElementsByTagName('li');
    var x=Chain(); 
    for(var i=0; i<objs.length; i++){
        if ( i & 1) { // твой вариант определения чётности был не правильный
            //cur_li(objs[i])
            x.chain(cur_li, objs[i]);
        }
    }
    x.run();
})

function cur_li(obj){
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (cur_li.next) cur_li.next();
    })
}

</script>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
</ul>
</body>
</html>


Кстати, ещё одна интересная тема для размышлений- можно ли chain замкнуть на саму себя, чтоб все операции выполнялись в цикле заданное количество раз, либо бесконечно, пока мы насильно не остановим chain?

Добавлено @ 15:08
Sardar, в твоём случае функции выполняются одновременно, а суть chain состоит в том, чтоб выполнять их последовательно, чтоб к примеру сначала выполнилась анимация, после завершения анимации выполнилась логика, после завершения логики вторая анимация и т.п. В этом-то и суть вопроса smile

Автор: ksnk 30.1.2008, 15:35
Цитата

 // твой вариант определения чётности был не правильный

Согласен... Зато твой - определение "нечетности", а не "четности" ;-)

Вот такая правка исправит ситуацию... Первым элементом цепочки оказывалась undefined конструкция, которая генерировалась Chain()'ом...
Код

    // main chain function
    x.chain=function(a,b,c){
        if(!a) return this; //<<< 
        if(!(b instanceof Array)) b=[b];
        this.push({a:a,b:b,flag:c});
        return this;
    }




Автор: ksnk 30.1.2008, 16:13
Да , еще вот это я поменял
Код

function cur_li(obj){
    var xx= cur_li.next ; 
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (xx) xx();
    })
}


Добавлено через 57 секунд
Хотя, как было - тоже работает ...

Автор: ksnk 30.1.2008, 17:28
Обновил http://forum.vingrad.ru/index.php?showtopic=193391&view=findpost&p=1396039... теперь в этом примере она еще и прекращать анимацию, в том числе и от jQuery,  умеет корректно...
Код

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<script language="javascript" src="jquery.js"></script>
<script>
var CHAIN=Chain(); // <<< Это, чтобы на кнопочку повесить ! :-)

function cur_li(obj){
    $(obj).animate({'fontSize': 40}, 1000, 'linear', function(){
        if (cur_li.next) cur_li.next();
    })
}
function stop_animation(obj_array){
    var i=obj_array.length;
    while(i--){
         $(obj_array.shift()).stop(); // clear all animation напроч!!!
    }
   stop_animation.next();
}

function run_animation(){
    var objs=document.getElementsByTagName('li');
    var x=CHAIN,par=[]; // <<< objs - это не массив...
    for(var i=0; i<objs.length; i++){
        if ( i & 1) { 
            //cur_li(objs[i])
            par.push(objs[i])
            x.chain(cur_li, objs[i]);
        }
    }
    x.chain(stop_animation,[par],'require') // // <<< последний параметр - не важно что, лишь бы был...
    .run();
}

$(run_animation);

</script>
</head>
<body>
<div id='debug' style='float:right;'></div>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
</ul>
<input type='button' onclick='run_animation()' value='Run animtion again'>
<input type='button' onclick='CHAIN.clear()' value='Stop animation'>
</body>
</html>

Автор: Sardar 30.1.2008, 18:46
Цитата(Dr.Zlo @  30.1.2008,  13:58 Найти цитируемый пост)
Sardar, в твоём случае функции выполняются одновременно, а суть chain состоит в том, чтоб выполнять их последовательно,

А ты попробуй smile  Функции вызываются в порядке следования, одна закончиться сразу следующая, без паузы.

Автор: ksnk 30.1.2008, 19:26
Sardar, имеется ввиду, что в цепочке могут присутствовать функции, которые не заканчиваются фактом своего вызова. К примеру, выставляющие мрачный таймаут и чекающие какое-нибудь странное состояние. Про такие функции только она сама может сказать, когда-же она пожелает закончится ;-) Пример - получение Ajax'ом странички. Я сейчас активно думаю над  возможностью писать 
chain(get_ajax,'mypage.php?action=hello')
.chain(pace_into_div,[null,null,'div_id']) // null - placeholder для RequestText, RequestError

Моя схема позволяет вызываемой функции самой определить время своего завершения и вызвать следующий элемент в цепочке... А Ваша? smile

Добавлено через 9 минут и 1 секунду
Цитата(Dr.Zlo @  30.1.2008,  14:58 Найти цитируемый пост)
Кстати, ещё одна интересная тема для размышлений- можно ли chain замкнуть на саму себя, чтоб все операции выполнялись в цикле заданное количество раз, либо бесконечно, пока мы насильно не остановим chain?

Хм... вставлять в chain операторы ветвления по условию, циклы и свитчи... + парраллельное исполнение анимаци... smile Соблазнительно, но пока думаю, что это ненужный геморой...
В принципе, можно как-то обойтись и существующей схемой. Достаточно иметь указатель на головной chain в исполняемой процедуре. Сама процедура уже может навставлять в массив всего чего пожелает... Надо только разумно ограничить ее желания ;-)
В принципе, указатель на головной chain можно получить, вызвав arguments.calee.next с каким- нибудь характерным параметром... В общем - нужно подумать...

Автор: Dr.Zlo 30.1.2008, 23:56
Кстати, вот примитивная реализация цикличности. Тупо и забавно smile

Код

$(document).ready(function(){
    loop_chain();
})

function loop_chain(){
  var obj =$('#animate_div');
  var speed = 500;
  Chain(animate, [obj, speed]).chain(animate2, [obj, speed]).chain(loop_chain).run();
}


function animate(obj, speed){
    $(obj).animate({'width': '1000', 'height': '1000'}, speed, 'linear', function(){
        if (animate.next) animate.next();
    })
}

function animate2(obj, speed){
    $(obj).animate({'width': '100', 'height': '100'}, speed, 'linear', function(){
        if (animate2.next) animate2.next();
    })
}


Добавлено через 3 минуты и 45 секунд
Кстати, 
Цитата

Согласен... Зато твой - определение "нечетности", а не "четности" ;-)

не забываем, что в массивах чётные элементы имеют нечётные индексы ;-)

Автор: ksnk 9.2.2008, 01:40
В функции обнаружился баг. Связан он с хранением "цепочки" в самой функции анимации. Если используется анимирование сразу в несколько потоков, цепочки загадочным образом путаются с труднопредсказуемым результатом... Было принято решение откатится до фиксированного формата функций анимации. Правда здравый смысл все-таки немного победил, теперь они получают 3 параметра, 2 из которых - нормальные параметры, а 3-й callback функция для вызова. Во вложении сам chain и примерчик его использования.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)