My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
Documentation  
Описание библиотеки и принципов работы с ней
Featured
Updated Sep 22, 2008 by serge....@gmail.com

Зачем это нужно

В интернете сейчас существует много готовых фреймворков для анимации: практически все современные JavaScript-библиотеки (jQuery, prototype/script.aculo.us, dojo, Ext и т.д.) умеют с ней работать. Почему же потребовалось создавать еще один?

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

Поэтому у меня и возникла мысль написать свою библиотеку — jTweener, которая будет такой же простой в использовании, как и современные JavaScript-библиотеки, но в то же время даст разработчику полный контроль над анимациями.

Первая анимация

Создадим нашу первую анимацию на jTweener: переместим элемент с id="sample" вправо в значение 300px и вниз в значение 200px:

jTweener.addTween(
	document.getElementById(‘sample’),
	{
		left: 300,
		top: 200
	}
);

Как видно из примера, существует объект jTweener, в котором определен ряд методов, один из которых — addTween(). Это самый главный метод для создания анимаций. Он принимает два аргумента. Первый — это объект (или массив объектов), который нужно анимировать. Это может быть как обычный JavaScript объект, так и HTML-элемент (в последнем случае будут изменяться CSS-свойства элемента). Второй аргумент — это объект (хэш), который содержит параметры анимации: свойства объекта, которые нужно анимировать, и настройки самой анимации.

В версии 0.2 был добавлен специальный «фасад» $t, который предназначен для удобства работы с анимациями. При использовании фасада пример выглядел бы так:
	$t(document.getElementById('sample')).tween({
		left: 300,
		top: 200
	});
Подробнее о фасаде читайте в соответствующем разделе.

Глядя на этот пример, может возникнуть вопрос: мы указали конечные значения свойств, а где указывать начальные? Их указывать совсем необязательно: jTweener сам попытается их определить. В случае с HTML-элементом он попытается определить текущие CSS-свойства. Однако он не всегда сделает это правильно (особенно когда свойства указываются в единицах, отличных от пикселей). Поэтому иногда рекомендуется выставлять начальные свойства принудительно:

var elem = document.getElementById(‘sample’);

// ставим начальное значение анимируемого свойства
elem.style.left = '100px';

// запускаем анимацию
jTweener.addTween(elem, {left: 400});

Объект переместится вправо из точки 100px в точку 400px.

Итак, мы научились создавать простую анимацию, теперь попробуем изменить ее свойства: скорость, закон изменения параметра, задержка перед началом и т.д. За эти настройки отвечает ряд предопределенных свойств в хэше параметров:

  • time : Number — продолжительность анимации, секунд. По умолчанию: 1.
  • transition : String, Function — алгоритм анимации (см. функции анимации). По умолчанию: 'easeoutexpo'.
  • delay : Number — задержка перед началом анимации, секунд. По умолчанию: 0.
  • onStart : Function — функция, которую нужно выполнить во время начала анимации.
  • onStartParams : Array — параметры для функции onStart.
  • onUpdate : Function — функция, выполняемая с каждым проходом анимации.
  • onUpdateParams : Array — параметры для функции onUpdate.
  • onComplete : Function — функция, которую нужно выполнить после окончания анимации.
  • onCompleteParams : Array — параметры для функции onComplete.
  • namespace : String — пространство имен, в котором должна отрабатываться анимация. Об этом параметре будет рассказано позже.

По умолчанию все анимации длятся одну секунду и свойства объекта меняются с замедлением по экспоненте ('easeoutexpo'). Давайте сделаем так, чтобы анимация длилась три с половиной секунды, свойства объекта менялись с кубическим ускорением, а само действие запустилось только через одну секунду:

jTweener.addTween(
	document.getElementById('sample'),
	{
		left: 300,
		top: 200,
		time: 3.5,
		transition: 'easeincubic',
		delay: 1
	}
);

Хочу сразу обратить внимание на параметр delay — это довольно мощное и удобное средство для создания последовательностей анимаций. Давайте заставим наш объект двигаться вдоль квадрата:

var elem = document.getElementById(‘sample’);
jTweener.addTween(elem, {left: 200, time: 2});
jTweener.addTween(elem, {top: 200, time: 2, delay: 2});
jTweener.addTween(elem, {left: 0, time: 1, delay: 4});
jTweener.addTween(elem, {top: 0, time: 2, delay: 5});

В этом примере значение параметра delay равно сумме параметров time предыдущих анимаций. Меняя парамтры time и delay можно очень легко добиваться интересных эффектов.

Анимация цвета

При анимации HTML-элемента следующие свойства воспринимаются как цветовые: backgroundColor, borderColor, borderBottomColor, borderLeftColor, borderRightColor, borderTopColor, color, outlineColor. То есть при анимации одного из этих CSS-свойств jTweener ожидает в качестве конечного значения именно цвет, который может быть в одном из следующих форматов: rgb(100, 230, 50), rgb(100%, 45%, 99%), #ffcc00, #fc0.

jTweener.addTween(
	document.getElementById(‘sample’),
	{
		backgroundColor: ‘#00cf00’
	}
);

Анимация прозрачности

С помощью jTweener можно анимировать прозрачность элемента, за это отвечает свойство opacity, значение которого может быть от 0 до 1. Для IE автоматически будет использован фильтр Alpha, а также принудительно выставлено CSS-свойство zoom.

jTweener.addTween(
	document.getElementById('sample'),
	{
		opacity: 0.3
	}
);

Getter/setter

Как было упомянуто вначале, jTweener может анимировать не только HTML-элементы, но и обычные JavaScript объекты. Как правило, от простого изменения свойства объекта толку мало: нужно как-то визуально отразить это изменение, для чего необходимо вызвать какую-нибудь функцию. К сожалению, в JavaScript нет возможности объявить getter/setter функции для свойств объекта (это такие функции, которые автоматически вызываются при получении/установке значения свойства объекта). В jTweener для этих целей можно воспользоваться параметром onUpdate, но есть более изящное решение.

В jTweener используется следующее соглашение: если анимируемое свойство объекта является функцией, то вызов этой функции без аргументов должен работать как getter, а вызов функции с одним аргументом — как setter. Рассмотрим пример. Предположим, нам необходимо двигать какой-то элемент с ускорением и замедлением по окружности. Для этого нам нужно одновременно менять top и left свойства HTML-элемента. Можно написать специальную анимационную функцию для каждой координаты и запустить параллельно две анимации. Но это довольно сложно и требует хорошей математической подготовки. Есть более элегантное решение этой задачи: мы создадим объект, в котором будем менять угловую скорость, а при изменении этого параметра будем считать left и top координаты элемента.

var circle = {
	obj: document.getElementById(‘sample’),
	r: 100, //радиус окружности
	a: 0, // текущий угол
	/** угловая скорость */
	angle: function(){
		if (!arguments.length) {
			// нет аргументов — работаем как getter
			return this.a;
		} else {
			// есть аргумент — работаем как setter

			this.a = arguments[0];

			// переводим угол из градусов в радианы
			var a = this.a / 180 * Math.PI;

			// считаем новые координаты
			this.obj.style.left = Math.sin(a) * this.r;
			this.obj.style.top = Math.cos(a) * this.r;
		}
	}
};

jTweener.addTween(circle, {
	angle: 360, // полный оборот
	transition: 'easeintoutexpo'
});

Процентная анимация

Это особый вид уличной магии анимации, когда анимируемое свойство является функцией. Чем-то похоже на Getter/setter, только функция всегда используется как setter.

Это очень удобное упрощение getter/setter соглашения:

  • В функцию не надо добавлять проверку на установку или получение значения.
  • Не обязательно создавать прокси-объект для такой анимации.
  • В качестве значения функции приходит число от 0 до 1 (поэтому анимация называется процентной).

Добавить такую анимацию можно двумя способами:

  • Как свойство хэша параметров:
  • 	var from_x = 100, to_x = 250;
    	
    	jTweener.addTween(
    		document.getElementById('sample'),
    		{
    			time: 2,
    			transition: 'easeoutcubic',
    			top: 100,
    			moveX: function(value){
    				// значение переменной value в диапазоне от 0 до 1
    				this.style.left = (from_x + (to_x - from_x) * value) + 'px'
    			}
    		}
    	);

  • Через функцию addPercent(). Ее основное отличие от addTween() в том, что ей не обязательно передавать анимируемый объект в качестве параметра:
  • 	var from_x = 100, to_x = 250;
    	var obj = document.getElementById('sample');
    	
    	jTweener.addPercent(
    		{
    			time: 2,
    			transition: 'easeoutcubic',
    			moveX: function(value){
    				// значение переменной value в диапазоне от 0 до 1
    				obj.style.left = (from_x + (to_x - from_x) * value) + 'px'
    			}
    		}
    	);

Процентная анимация используется, как правило, в сложных ситуациях, например, движение вдоль кривой Безье:

	// определяем конечные и контрольные точки кривой Безье
	var from_x = 100, from_y = 340;
	var to_x  = 280, to_y = 340;
	var cp1_x = 390, cp1_y = 75;
	var cp2_x = 30, cp2_y = 75;
	
	// объект, который будем анимировать
	var obj = document.getElementById('sample');
	
	jTweener.addPercent(
		{
			bezier: function(t){
				// у jTweener есть встроенные функции для работы с кривыми Безье
				var x = jTweener.Utils.bezier3(t, from_x, cp1_x, cp2_x, to_x);
				var y = jTweener.Utils.bezier3(t, from_y, cp1_y, cp2_y, to_y);
				
				obj.style.left = x + 'px';
				obj.style.top = y + 'px';
			},
			
			time: 2,
			transition: 'easeinoutcubic'
		}
	);

Относительное изменение свойств

Иногда необходимо делать не абсолютное, а относительное изменение свойств анимируемых объектов, то есть делать изменение относительно текущего значения свойства. Для этого перед значением свойства нужно поставить '+=' или '-=':

jTweener.addTween(
	document.getElementById(‘sample’),
	{
		left: '+=100'
	}
);

Тем самым мы скажем: «переместись вправо на 100 пикселей относительно текущего положения».

Анимация групп объектов

Собственно, это то, ради чего писалась эта библиотека. Рассмотрим следующую ситуацию.

Предположим, мы решили использовать canvas для того, чтобы рисовать какие-то элементы на странице, и нам нужно использовать анимацию: необходимо перемещать 5 прямоугольников на холсте. Canvas работает по следующему принципу: для показа нового изображения нужно сначала очистить весь холст, а потом отрисовать все элементы. Если мы будем использовать параметр onUpdate, изображение будет перерисовываться столько раз, сколько у нас анимируемых объектов (в нашем случае — 5), что крайне негативно скажется на производительности. В идеале нужно сначала изменить координаты всех прямоугольников, после чего один раз все перерисовать. В библиотеке JSTweener, которую я взял за основу, эта задача решалась следующим образом: перекрывался основной метод eventLoop, который отвечал за один проход анимации всех объектов. Но тут возникает другая проблема: если на текущей странице библиотека используется для анимации других объектов, не связанных с canvas, изображение все равно будет постоянно перерисовываться, хотя там не происходит никаких изменений. Что, опять же, приведет к снижению производительности.

Для решения этой проблемы в jTweener было введено такое понятие, как пространство имен (namespace). По умолчанию все анимации работают в пространстве имен default, но при необходимости его можно переопределить для каждой анимации в помощью параметра namespace. А затрем с помощью метода addNSAction() указать функции, который будут выполняться за один проход анимации всех объектов из указанного пространства имен (свойство onUpdate) либо после завершения всех анимаций из этого пространства (свойство onComplete).

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

// описываем прямоугольники
var rects = [
	{x: 0, y: 0, width: 10, height: 10, color: '#c279ff'},
	...
];

// Функция рисования
function draw(){
	...
}

// регистрируем функцию перерисовки в пространстве имен
jTweener.addNSAction({onUpdate: draw}, 'canvas');

// анимируем объекты
for (var i = 0; i < rects.length; i++) {
	jTweener.addTween(rects[i], {
		x: Math.random() * 200,
		y: Math.random() * 200,
		namespace: 'canvas'
	});
}

Удаление анимаций

Анимации удаляются с помощью функции removeTween(), которая имеет несколько вариантов использования:

  • removeTween() — удаляет абсолютно все анимации
  • removeTween(namespace : String) — удаляет все анимации из пространства имен 'namespace'
  • removeTween(obj : Object, Array) — удаляет все анимации у объекта (или массива объектов) obj
  • removeTween(namespace : String, obj : Object, Array) — удаляет анимации у объекта (или массива объектов) obj из пространства имен namespace
  • var obj = document.getElementById('anim_item');
    jTweener.addTween(obj, {left: 200});
    
    document.getElementById('stop_button').onclick = function(){
    	jTweener.removeTween(obj);
    }

Более подробное описание методов jTweener вы можете найти в jsdoc-файле с документацией.

Comment by deniskhripkovv, May 26, 2008

надо разобратся на досуге...

Comment by s.orl...@gmail.com, Jan 3, 2009

Офигенная библиотека, спасибо авторам!

Comment by neolord....@gmail.com, Jul 21, 2009

Очень интересно, в чем преимущество перед тем же scriptaculous?

Comment by vlad.min...@gmail.com, Oct 6, 2009

почему oncomplete срабатывает также и при старте анимации?

Comment by project member serge....@gmail.com, Oct 6, 2009

можете прислать пример?

Comment by tezro...@gmail.com, Nov 26, 2009

neolord.tmb >> Очевидно же...

Comment by lol2fas...@gmail.com, Jun 22, 2010

Круто конечно, только редко нужно (:

Comment by homerstu...@gmail.com, Sep 9, 2010

Сергей, огромное спасибо вам за библиотеку!


Sign in to add a comment
Powered by Google Project Hosting