Модераторы: Sardar, Aliance
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Учимся делать анимации, Пояснения, примеры, код 
:(
    Опции темы
Sardar
Дата 15.10.2005, 20:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 16
Всего: 317



Цель этого топа выложить готовый код, позволяющий эффективно строить любые анимации. Также мы рассмотрим как всё это работает и научимся делать что нибудь своё smile

Идея родилась из-за этого топа, за что участнику GIK спасибо smile

Что такое анимация?
Анимация это функция от времени, т.е. функция описывающая обьект (что угодно) на временной шкале. Анимация имеет три главных свойства: длительность во времени, начальное и конечное состояния.

Отсюда мы должны понять: любая анимация конечна. Бесконечные анимации создаються путём повторного запуска конечной анимации smile

Полезно представить себе шкалу (линейку) времени, на которой "закрашиваем" период времени отведённый на анимацию. На любой точке шкалы можно узнать состояние обьекта.
Для создания анимаций применяеться много различной теории. Мы пойдём по пути time-based анимации, т.е. то что привыкли видет во флеше и т.п.

Что такое кадр?

Кадрами будем считать момент, когда некторый код порождает очередное состояние обьекта (картинку), которое затем на протяжении некоторого периода времени не изменяеться. Период времени будет минимальной еденицей на временной шкале, чем он меньше, тем плавнее отрисовка. Однако при малых задержках между кадрами браузер попросту не успевает всё отрисовать, на практике это выглядит как замедление исполнения, так что анимация длинной в 1 секунду может длиться 2 и более smile

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

Как работает анимация?

Анимационная функция опрашиваеться через каждый период времени между кадрами. В идеале функция не должна работать дольше чем этот период, но на практике браузер тормозит-с smile

Отработав анимационная функция либо ожидает следующего обращения, либо, если анимация завешилась, передаёт управление следующей анимации.

Все анимационные функции помещаються на одну шкалу времени, назовём её "очередью анимации". После завершения анимация отдаёт управление следующей из очереди. Анимации в очереди отрабатывают друг за другом, а две разные очереди анимации работают паралельно. Можно скомбинировать несколько анимаций в одну, так что они будут отрабатывать паралельно в одной очереди.

Полезно представить это всё как закрашивание периодов на временной шкале.

Временные интервалы

Все анимационныe функции опрашиваються с частотой кадрa, что не всегда необходимо. Например некоторый текст должен сменяться каждые 3 секунды, следовательно анимационная функция должна отрабатывать каждые три секунды, в остальное время функция опрашиваться не должна.

Анимационная функция может увеличить интервал времени между текущим кадром и следующим, таким образом она не будет опрошенна до истечения заданного интервала времени. В коде это вызов метода wait() анимационной очереди AnimationQueue.

До кучи smile

И так врубились что есть:
  • анимационные функции, порождающие кадр. В нашем случае изменяющие некоторое свойство обьекта, например style.width - ширина html элемента.
  • анимационная очередь - AnimationQueue - в которую помещаються анимационные функции.
  • комбинация анимаций - AnimationSet - представляет несколько различных анимаций как одну, таким образом они могут исполняться паралельно в одной анимационной очереди (на одной временной шкале). Переход к следующей произойдёт когда все анимации отработают.
Изучение кода оставлю на читателя, если есть вопросы, задавайте в этом или новом топе. Рассмотрим как это работает на практике smile

Простейшая анимация: два слоя должны изменяться в размерах, один за другим, затем одновременно уменьшиться до 10х10. Одновременно некоторый текст должен "печататься" в отведённом для этого месте.

Замечаем что нам необходимы всего две анимационых функции:
  • изменение размеров обьекта, функция может работать с любым обьектом, для которого есть визуальное преставление.
  • "печать" текста в слое в любом элементе, который может содержать текст.
Рассмотирм по порядку, изменение размеров:
Код
//изменение размеров обьекта
//за разьяснениями параметров смотрим доку в коде, ниже по топу
function resizeAnimation(thread, time, obj, width, height) {
  //для начала узнаем за сколько шагов мы выполним анимацию
  var steps=Math.ceil(time/thread.settings.frameDelay);
  var currstep=1;
  var owidth, oheight, hstep, wstep;

  //анимационная функция, будет вызываться для каждого следующего кадра
  return function(animator) {
    //при первом опросе вучислим начальные размеры обьекта
    //догадайтесь почему этого нельзя сделать раньше ;-)
    if(typeof(owidth)!='number') {
       owidth=obj.offsetWidth;
       oheight=obj.offsetHeight;
       wstep=(width-owidth)/steps;
       hstep=(height-oheight)/steps;
    }
    //после окончания анимации выставляем требуемые размеры, что бы избежать глюков
    if(currstep>steps) {
      obj.style.width=width+'px';
      obj.style.height=height+'px';
      animator.next(); //передаём управление следующей анимации
      return;
    }
    //а вот собственно и сама анимация, каждый шаг приближает размер к требуемому
    obj.style.width=Math.round(owidth+wstep*currstep)+'px';
    obj.style.height=Math.round(oheight+hstep*currstep)+'px';
    currstep++;
  }
}

Печать текста:
Код
//очень простая функция, печатающая по буквам текст в слое
//за разьяснениями параметров смотрим доку в коде, ниже по топу
function typeAnimation(obj, text, timedelay) {
   var len=0; //с каждым шагом увеличиваем длинну вырезаемого текста

   //анимационная функция, но на этот раз она будет отрабатывать гораздо реже
   //чем частота кадров
   return function(animator) {
      animator.wait(timedelay); //задаём время ожидания до следующего кадра
      if(++len>text.length) {len=0; animator.next();} //если текст закончился, отдаём управление следующе анимации
      else obj.innerHTML=text.substring(0, len); //и вот сам анимационный код, вставляющий текст
   }
}


Один раз написав анимационную функцию, мы можем использовать её для множества различных анимаций. Смотрим:

Код
var a, b; //две анимационные очереди

//инициализируем анимации. 
function initAnimations() {
  //создаём очереди (шкалы времени, треды, называем как угодно)
  a=new AnimationQueue(); //тред для квадратов
  a.setLoop(false); //будем крутить только один раз

  b=new AnimationQueue(); //а тред для текста бесконечно
  
  //добавляем две анимации, исполняемые друг за другом
  //обе анимации это написанный нами ранее ресайз html элементов
  a.add(resizeAnimation(a, 1000, document.getElementById('red'), 400, 200));
  a.add(resizeAnimation(a, 2500, document.getElementById('green'), 400, 300));
  
  //эти две анимации будут исполняться параллельно
  var pack=[
    resizeAnimation(a, 2000, document.getElementById('red'), 10, 10),
    resizeAnimation(a, 2000, document.getElementById('green'), 10, 10)
  ];
  a.add(AnimationSet(pack)); //вызовом AnimationSet мы собираем обе анимационных функции в одну
  
  //анимации для текста, на отдельной временной шкале
  b.add(typeAnimation(document.getElementById('text'), "Текст печаетаеться со скоростью десять букв в секунду", 100));
  b.add(typeAnimation(document.getElementById('text'), "А этот со скоростью три буквы в секунду", 300));
  b.add(typeAnimation(document.getElementById('text'), "Можно ещё быстрей, пауза всего 50 миллисекунд", 50));
  b.add(waitAnimation(1000)); //подождём секунду, замечаем что это тоже анимация ;-)

  //кстати заметим что времай требуемое на исполнение чуть больше чем то которое мы задали, о чём и говорилось ранее.
  //нагрузка на проц никакая, просто так устроен интерпретатор JS, для операций реального времени он совсем не годиться =)

  //теперь весь бутерброд запускаем =)
  a.start();
  b.start();
}

//остановка анимации
function stopAnimations() {
  a.stop();
  b.stop();
  a=b=null; //обнуляем обьекты, что бы освободить память
  //если анимация должна запуститься ещё раз, осовобождать не стоит, просто вызываем start() и она запуститься с того момента, на котором остановилась.
}


И так всё просто и ясно. Можно вообще закинуть это дело в XML, что бы сборка анимаций происходила проще, заинтересованных отправляю в SMIL smile

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

Код
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <title>Animation demo</title>
<style type="text/css">
#red {
  width: 100px;
  height: 100px;
  margin: 20px;
  background-color: #e24900;
  font-size: 1px;
}
#green {
  width: 100px;
  height: 100px;
  margin: 20px;
  background-color: #3aca02;
  font-size: 1px;
}
#text {
  border: 1px solid black;
  font-family: Verdana;
  font-size: 14px;
  color: black;
  line-height: 15px;
  height: 15px;
  margin: 20px;
}
</style>

<script type="text/javascript">
<!--

/*============================- Animation queue -==========================*/
AnimationQueue.prototype.settings={
  frameDelay: 24
};

/**
* Анимационный поток, представляет собой цепочку анимационных функций,
* запускаеммх с заданной частотой. Можно представить как процессор и тактовый
* генератор =)
*/
function AnimationQueue() {
  this.anims=[];
  this.currentAnim=0;
  this.timer=null;
  this.waittime=null;
  this.fromTime=null;
  this.loop=true;
}
/**
* Установить повторение анимации после завершения.
* По теории этого метода не должно быть. Выбираеться дефолтовое действие:
* повторять(у нас так) либо не повторять. Затем если нужно выполнить действие
* не равное дефолтовому, то добавляеться обьект "анимация" в ряд, что и выполнит
* необходимое действие.
* НО! на практике чем меньше телодвижений, тем лучше =)
* @param boolean loop true - поставить повтор, false - отменить повтор
*/
AnimationQueue.prototype.setLoop=function(loop) {
  this.loop=Boolean(loop);
}
/**
* Установить паузу для следующего "кадра".
* Метод необходим анимациям, работающих гораздо медленней чем "анимационный
* поток". Время задаёться в миллисекундах, но меньше чем шаг аниматора быть не
* может. Чем меньше время, тем мнее точней будет результирующая пауза.
* @param integer ms пауза в миллисекундах
*/
AnimationQueue.prototype.wait=function(ms) {
  if(typeof(ms)!='number') throw "AnimationQueue.wait: wait time '"+ms+"' must be an integer representing time in milliseconds";
  this.waittime=ms;
  this.fromTime=(new Date()).getTime();
}
/**
* Следующая анимация.
* Методы вызываеться анимациями, когда текущая полностью завершиться, что бы
* отдать управлениие следующей анимации.
*/
AnimationQueue.prototype.next=function() {
  if(!this.loop && (++this.currentAnim)>=this.anims.length) this.stop(); 
  else if(this.loop) this.currentAnim=(++this.currentAnim)>=this.anims.length? 0: this.currentAnim;
  this.waittime=null;
}
/**
* Добавить анимацию в "анимационный поток".
* Метод добавляет анимации в конец цепочки. Можно добавлять новые анимации после
* вызова start, но это может порождать труднонаходимые глюки =)
* Поэтому набиваем поток анимациями заранее, затем вызываем start() и забываем.
* @param function animation функция аниматор, что будет вызываться каждый проход
*   аниматора. Параметром получает ссылку на обьект аниматора.
*/
AnimationQueue.prototype.add=function(animation) {
  if(!(animation instanceof Function)) throw "AnimationQueue.add: given animation object '"+animation+"' is not a function!";
  this.anims[this.anims.length]=animation;
}
/**
* Запустить анимационный поток.
* Выполнение продолжаеться с момента остановки. Параметром можно указать с какой
* анимации начинать выполнение, отсчёт от 0
* @param integer порядковый номер стартовой анимации с 0
*/
AnimationQueue.prototype.start=function(initAnim) {
  if(this.timer!=null||this.anims.length<1) return;
  if(typeof(initAnim)=='number') this.currentAnim=initAnim;
  this.currentAnim=this.currentAnim<0? 0: this.currentAnim>=this.anims.length? this.anims.length-1: this.currentAnim;
  var animator=this;
  this.timer=window.setInterval(function() {
     if(animator.waittime!=null) {
       if(((new Date()).getTime()-animator.fromTime)<animator.waittime) return;
       animator.waittime=null;
     }
     animator.anims[animator.currentAnim](animator);
  }, this.settings.frameDelay);
}
/**
* Остановить анимационный поток.
* Все анимации сохраняют своё состояние! Проще говоря останавливаеться главный
* таймер, функции-анимации просто перестают регулярно вызываться. При следующем
* вызове start() выполнение продолжиться со состояния на момент stop().
*/
AnimationQueue.prototype.stop=function() {
  window.clearInterval(this.timer);
}

/*===============================- Animation Delay -=======================*/
/**
* Анимация - пауза.
* Этот обьект-обёртка понадобился для работы с паузами в пакетах
* анимаций(см. ниже). Представляеться как обычная анимационная функция.
* Передаёт управление по прошествии определённого интервала времени.
* Пауза срабатывает только один раз, для повторяющихся пауз нужно задать третьим
* параметром true.
* Вызов setDelay сбрасывает текущую паузу и устанавливает новую.
* @param function anim анимационная функция
* @param interger msc задержка в миллисекундах
* @param boolean continous true - разрешить постоянную паузу, false(default) - отменить постоянную паузу
* @return function результирующая анимационная функция
*/
function AnimationDelay(anim, msc, continous) {
   var a=function(animator) {
     if(arguments.callee.waittime!=null) {
       if(((new Date()).getTime()-arguments.callee.fromTime)<arguments.callee.waittime) return;
       if(continous) arguments.callee.setDelay(msc);
       else arguments.callee.waittime=null;
     }
     anim(animator);
   }
   a.setDelay=function(ms) {
     if(typeof(ms)!='number') throw "AnimationDelay.setDelay: wait time '"+ms+"' must be an integer representing time in milliseconds";
     this.waittime=ms;
     this.fromTime=(new Date()).getTime();   
   }
   a.setDelay(msc);
}

/*==============================- Animation Set -==========================*/
/**
* Обьединение анимаций по времени.
* Иногда(часто =)) требуеться запустить несколько анимаций одновременно. Обьект
* обьединяет анимации в одну. Выход из обьединения произойдёт после того как все
* анимации вызовут либо next(), либо stop(). Если анимация вызвала
* next()/stop(), то она удаляеться из списка анимаций и при следующем проходе
* вызванна не будет.
* @param array масив с функциями-анимациями, что будут выполняться одновременно
* @return function результирующая анимационная функция
*/
function AnimationSet(set) {
  return function(anim) {
     var animator={}, hasn=false;
     for(var i=0; i<set.length; i++) {
        if(!set[i]) {
          if(i<set.length-1||hasn) continue;
          else {anim.next(); return;}
        }
        hasn=true;
        animator.next=animator.stop=function() { set[i]=null; }
        animator.wait=function(ms) {
          if(!set[i].setDelay) set[i]=AnimationDelay(set[i], ms);
          else set[i].setDelay(ms);
        }
        set[i](animator);
     }
  }
}

/*=========================- Анимационные функции -=======================*/
/**
* Простая анимация создающая задержку в миллисекундах.
* @param integer ms пауза в миллисекундах
*/
function waitAnimation(ms) {
  var w=false;
  return function(animator) {
    if(w) {animator.next(); w=false;}
    animator.wait(ms);
    w=true;
  }
}

/**
* Анимация изменяющая размер обьекта.
* Изначальный размер обьекта вычисляеться при первом "кадре". Далее эти размеры
* подгоняються под заданные.
* Параметрами задаёться целевой обьект, размеры и время, за которое анимация
* должна выполниться.
* Внимание: время всегда используеться чуть больше чем заданно, т.к. анимация
* на сложных обьектах браузера всё таки жутко не эффективная работа =)
* @param AnimationQueue thread анимационный поток в котором будем исполняться
* @param integer time длительность анимации
* @param HTMLElement obj целевой обьект
* @param integer width требуемая ширина обьекта
* @param integer height требуемая высота обьекта
*/
function resizeAnimation(thread, time, obj, width, height) {
  var steps=Math.ceil(time/thread.settings.frameDelay);
  var currstep=1;
  var owidth, oheight, hstep, wstep;
  return function(animator) {
    if(typeof(owidth)!='number') {
       owidth=obj.offsetWidth;
       oheight=obj.offsetHeight;
       wstep=(width-owidth)/steps;
       hstep=(height-oheight)/steps;
    }
    if(currstep>steps) {
      obj.style.width=width+'px';
      obj.style.height=height+'px';
      animator.next();
      return;
    }
    obj.style.width=Math.round(owidth+wstep*currstep)+'px';
    obj.style.height=Math.round(oheight+hstep*currstep)+'px';
    currstep++;
  }
}

/**
* Простая анимация "печатаемого текста".
* Заданная строка "печатаеться" через innerHTML в целевом обьекте.
*/
function typeAnimation(obj, text, timedelay) {
   var len=0;
   return function(animator) {
      animator.wait(timedelay);
      if(++len>text.length) {len=0; animator.next();}
      else obj.innerHTML=text.substring(0, len);
   }
}

/*==============================- Задаём анимации -=======================*/

var a, b; //два анимационных потока
function initAnimations() {
  a=new AnimationQueue(); //тред для квадратов
  a.setLoop(false); //будем крутить только один раз

  b=new AnimationQueue(); //а тред для текста бесконечно
  
  //добавляем две анимации, исполняемые друг за другом
  a.add(resizeAnimation(a, 1000, document.getElementById('red'), 400, 200));
  a.add(resizeAnimation(a, 2500, document.getElementById('green'), 400, 300));
  
  //а эти две анимации будут исполняться параллельно
  var pack=[
    resizeAnimation(a, 2000, document.getElementById('red'), 10, 10),
    resizeAnimation(a, 2000, document.getElementById('green'), 10, 10)
  ];
  a.add(AnimationSet(pack));
  
  //анимации для текста в отдельном потоке
  b.add(typeAnimation(document.getElementById('text'), "Текст печаетаеться со скоростью десять букв в секунду", 100));
  b.add(typeAnimation(document.getElementById('text'), "А этот со скоростью три буквы в секунду", 300));
  b.add(typeAnimation(document.getElementById('text'), "Можно ещё быстрей, пауза всего 50 миллисекунд", 50));
  b.add(waitAnimation(1000)); //подождём секунду, замечаем что это тоже анимация ;-)

  //теперь весь бутерброд запускаем =)
  a.start();
  b.start();
}
function stopAnimations() {
  a.stop();
  b.stop();
  a=b=null;
}
//-->
</script>

</head>
<body>
   <div id="red"></div>
   <div id="green"></div>
   <div id="text"></div>

<button onclick="initAnimations()">Start</button>&nbsp;&nbsp;
<button onclick="stopAnimations()">Stop</button>
</body>
</html>


Что бы было понятно о чём речь:

Закончил, да пребудет с вами великая сила smile


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Aliance
Дата 15.10.2005, 20:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


I ♥ <script>
****


Профиль
Группа: Модератор
Сообщений: 6418
Регистрация: 2.8.2004
Где: spb

Репутация: 6
Всего: 137



Кстати, вот неплохой скрипт, думаю его будет уместно сюда выложить…


Присоединённый файл ( Кол-во скачиваний: 87 )
Присоединённый файл  test.rar 6,04 Kb
PM MAIL WWW ICQ Skype   Вверх
GIK
Дата 16.10.2005, 10:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Добрый человек
**


Профиль
Группа: Участник
Сообщений: 985
Регистрация: 3.6.2005
Где: я только не небыв ал

Репутация: нет
Всего: 14



Блин, СУПЕР сделанно.....
Спасибо Sardar, я в восторге.
Только пока доконца не вкурил.
Ну ничего, надеюсь со временем пойму.



--------------------
Математика=>пиво=> програмирование, три вещи последовательны и совместимы !!!
Программирование - это не деятельнось! Программирование - это состояние души!
Бог - самый крутой программист.
PM MAIL ICQ   Вверх
Innuendo
Дата 6.1.2006, 02:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 745
Регистрация: 24.12.2005
Где: Молдова

Репутация: нет
Всего: 6



Я тож баловался с анимацией...
вот мой скриптец.. он с картинками правда (ну там в архиве они есть), правда в скриптеце есть пару баг (если будете очень быстро щелкать по менюшкам то заметите), и скрипт вывешенный в сеть плохо работает- долго изображения меняет, это потому что я не правельно изображения подгрузил... некогда исправить.
http://xthost.info/menu/menu.rar

там малюсенький архив- но думаю стоит посмотреть smile вам понравится... правда уже такую анимацию лучше во FlasHе делать)

Это сообщение отредактировал(а) Innuendo - 6.1.2006, 02:22


--------------------
=)
PM MAIL ICQ Jabber   Вверх
Sardar
Дата 6.1.2006, 16:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 16
Всего: 317



Innuendo скрипт написан плохо, если не сказать более smile
Во первых он не гибкий, количество опций в меню не определяеться динамически. Код плохо читаем, нет оступов, я вообще любитель K&R стиля...
Нет коментариев, а ведь в этом топе мы учим людей smile
Есть ещё несколько недоделок, не буду перечислять, пост твой оставлю только из-за "красивого вида" всей композиции, может кто по твоей идее напишет анимацию правильно smile


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Innuendo
Дата 6.1.2006, 17:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 745
Регистрация: 24.12.2005
Где: Молдова

Репутация: нет
Всего: 6



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


--------------------
=)
PM MAIL ICQ Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Здесь публикуют скрипты, которые уже проверены в обсуждениях других тем (при этом полезно поставить ссылки на все смежные обсуждения) или переносятся кем-либо из модераторов по просьбе участников, если видно, что в результате обсуждения темы был написан полезный или интересный скрипт. Третий возможный вариант - участник форума публикует скрипт, заведомо известный как полезный и эффективный, для, возможно, небольшой доработки и обсуждения.
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Наши скрипты | Следующая тема »


 




[ Время генерации скрипта: 0.0794 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.