Антон Шевчук // Web-разработчик

jQuery для начинающих. Часть 9. Пишем плагины анимации // JavaScript

jQuery Logo
Новая статья совсем не для начинающих. Будем разрабатывать плагины для расширения анимации, а это уже другой уровень…

Материалы данной статьи включены в учебник «jQuery для начинающих». Учебник распространяется бесплатно, и сопровождается интерактивными примерами.

Обучаем animate

Большинство разработчиков уже сталкивалось с функцией animate в jQuery, это достаточно удобный инструмент для анимирования элементов на странице, а я расскажу как вы можете легко изменить поведение данной функции под свои цели.

Для начала затравка – метод animate манипулирует объектом $.fx, в себе он содержит следующие методы:

  • cur – возвращает текущее значение атрибута
  • custom – отвечает за запуск анимации
  • hide/show – простые функции hide/show
  • step – каждый отдельный шаг анимации, т.е. вызывается много-много раз
  • update – данная функция отвечает за применение атрибутов

Из всего приведенного мы будем расширять объект $.fx.step. Возьмем достаточно тривиальную задачу – заставим плавно изменить цвет шрифта для заданного набора элементов:

$('p').animate({color:'#ff0000'});

Приведенный выше код не даст никакого эффекта, нам потребуется прокачать $.fx.step:

(function($) {
// color - свойство с которым мы будем работать
$.fx.step.color = function(fx) {
 
	// настройки анимации
	fx.options;
 
	// свойство которое изменяем
	fx.prop;
 
	// начальные значения
	fx.start;
 
	// результирующие значения
	fx.end;
 
	// коэффициент, изменяется от 0 до 1
	fx.pos;
 
	// еденицы измерения
	fx.unit;
 
};
})(jQuery);

Перед вами основные свойства объекта $.fx – это и есть наши кирпичики, с их помощью будем строить нашу анимацию. Перед работой стоит заглянуть внутрь каждого из приведенных свойств:

// добавим логирование внутрь нашего плагина
console.log(fx.options, fx.prop, fx.start, fx.end, fx.pos, fx.unit);

/* вывод в консоли:
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0008351307317917 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0009670080649619717 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0010877292712792586 / px
Object duration=5000 old=false curAnim=Object orig=Object / color / 0 / #ff0000 / 0.0012155411253085835 / px
..
*/

Как и предполагалось – метод вызывается N кол-во раз, в зависимости от продолжительности анимации, при этом fx.pos постепенно наращивает своё значение с 0 до 1 (аналогично ведет себя и параметр fx.state, только знаков после запятой у него поменьше, есть ли еще какая разница – я не знаю).

fx.pos, по умолчанию, наращивается линейно, если надо как-то иначе — то стоит посмотреть на easing плагин или дочитать статью до конца (об этом я уже упоминал в статье Эффекты)

Теперь не мешало бы заставить эту систему работать, начнем с инициализации крайних значений (листинг функции parseColor вы сможете найти в самом примере):

// инициализация должна проходить лишь однажды
if (fx.state == 0 ) {
	// получаем текущее значение элемента
	var start = $.curCSS(fx.elem,'color');
 
	// разбираем на составляющие текущее состояние элемента
	// start содержит строку вида rgb(255, 255, 255)
            var res = start.match(/rgb\(([0-9]{1,3}),\s?([0-9]{1,3}),\s?([0-9]{1,3})\)/);

            if (!res) {
                // или #ffffff;
                var res = start.match(/^#(\w{2})(\w{2})(\w{2})$/);
                var rgb = [parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)];
            } else {
                var rgb = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
            }

	// составляющие запихиваем в fx.start
	fx.start = rgb;
 
	// разбираем что нам нужно получить, и закидываем в fx.end
	fx.end   = parseColor(fx.end);
}

Теперь необходимо изменить текущее значение свойств и применить его:

// нехитрое вычисление 
var R = Math.round(((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]);
var G = Math.round(((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1]);
var B = Math.round(((fx.end[2] - fx.start[2]) * fx.pos) + fx.start[2]);
 
// непосредственно измение свойств элемента
fx.elem.style.color = 'rgb('+R+','+G+','+B+')';

Всё вместе:

$.fx.step.color = function(fx) {
	// инициализация должна проходить лишь однажды
	if (fx.state == 0 ) {
		// получаем текущее значение элемента
		var start = $.curCSS(fx.elem,'color');
 
		// разбираем на составляющие текущее состояние элемента
		// start содержит строку rgb(255, 255, 255)
		var res = start.match(/rgb\(([0-9]{1,3}),\s?([0-9]{1,3}),\s?([0-9]{1,3})\)/);
 
		// составляющие запихиваем в fx.start
		fx.start = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
 
		// разбираем что нам нужно получить, и закидываем в fx.end
		fx.end   = parseColor(fx.end);
	}
	// нехитрое вычисление 
	var R = Math.round(((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]);
	var G = Math.round(((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1]);
	var B = Math.round(((fx.end[2] - fx.start[2]) * fx.pos) + fx.start[2]);
 
	// непосредственно измение свойств элемента
	fx.elem.style.color = 'rgb('+R+','+G+','+B+')';
};

Результатом станет плавное перетекание исходного цвета к красному (#F00 == #FF0000 == 255,0,0)

Своя анимация

Теперь, познав основы, можем попробовать анимировать некоторый неизвестный параметр shiver (да пусть дрожат пред нами):

$('p').animate({shiver:100});

Идем по натоптанному пути:

(function($) {
	var left;
	$.fx.step.shiver = function(fx) {
 
		// инициализация должна проходить лишь однажды
		if (fx.state == 0 ) {
 
			// начальное расположение элемента от левого края
			// дрожать будем по горизонтали
			left = $.curCSS(fx.elem,'left');
 
			// устанавливаем относительное расположение элемента
			fx.elem.style.position = 'relative';
		}
	};
})(jQuery);

Так же реализуем саму дрожь элемента:

// если это последняя итерация - вернем всё на своё место
if (fx.now == fx.end) {
	fx.elem.style.left = left;
} else if (Math.round(1000 * fx.pos)%2==0) {
	// четная итерация
	// непосредственно измение свойств элемента
	fx.elem.style.left = - Math.round(Math.random()*fx.end) + fx.unit;
 
} else {
	// нечетная итерация
	// непосредственно измение свойств элемента
	fx.elem.style.left = + Math.round(Math.random()*fx.end) + fx.unit;
}

Собираем вместе, и смотрим на результат работы

Пишем свой easing плагин

Возвращаясь к easing’у – мы можем легко написать свою функцию, которой будет следовать анимация. Дабы особо не фантазировать – я взял пример из статьи o MooTools – очень наглядный пример с сердцебиением, которое описывается следующими функциями:

Heart Beat Functions

Теперь напишем соответствующий код:

$.extend($.easing, {
    /**
     * Heart Beat
     *
     * @param x
     * @param t current time
     * @param b begInnIng
     * @param c change In value
     * @param d duration
     *
     * @link http://habrahabr.ru/blogs/mootools/43379/
     */
    heart:function(x, t, b, c, d) {
        if (x < 0.3)
            return Math.pow(x, 4) * 49.4;
 
        if (x < 0.4)
            return 9 * x - 2.3;
 
        if (x < 0.5)
            return -13 * x + 6.5;
 
        if (x < 0.6)
            return 4 * x - 2;
 
        if (x < 0.7)
            return 0.4;
 
        if (x < 0.75)
            return 4 * x - 2.4;
 
        if (x < 0.8)
            return -4 * x + 3.6;
 
        if (x >= 0.8)
            return 1 - Math.sin(Math.acos(x));
    }
});

Чуть-чуть расширю пример дополнительными функциями (развернем и скомбинируем):

heartIn:function (x, t, b, c, d) {
    return $.easing.heartIn(x, t, b, c, d);
},
heartOut:function (x, t, b, c, d) {
    return c - $.easing.heartIn(1 - x, t, b, c, d) + b;
},
heartInOut: function (x, t, b, c, d) {
    if (t < d/2) return $.easing.heartIn(1 - x, t, b, c, d) + b;
    return $.easing.heartOut(1 - x, t, b, c, d) + b;
}

Графически это будет выглядеть так:

HeartIn

HeartOut

HeartInOut

Результат лучше не показывать кардиолагам.

Цикл статей

  1. jQuery для начинающих
  2. jQuery для начинающих. Часть 2. JavaScript Меню
  3. jQuery для начинающих. Часть 3. AJAX
  4. jQuery для начинающих. Часть 4. Селекторы
  5. jQuery для начинающих. Часть 5. Эффекты
  6. jQuery для начинающих. Часть 6. События
  7. jQuery для начинающих. Часть 7. Пишем плагины
  8. jQuery для начинающих. Часть 8. Расширяем фильтры
  9. jQuery для начинающих. Часть 9. Пишем плагины анимации

© Антон Шевчук 2007-2016