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

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. Пишем плагины анимации

16 thoughts on “jQuery для начинающих. Часть 9. Пишем плагины анимации”

  1. А как же таймер? И ограничение на множественные клики? Сейчас получается, что если 100раз ткнуть в картинку, она не скоро остановится. Да и остановить намеренно ее не получится.

  2. Спасибо за статью, очень хорошо пишите.
    Ради шутки можно сердцебиение построить опираясь на какую-нибудь последовательность с http://www.physionet.org/. Будет похоже на настоящее))))

  3. Скажу по секрету: в программировании большую роль играет математика чем знание синтаксиса :)

    1. Скажу по секрету: в программировании большую роль играют навыки программирования, чем математика. Они связаны, но никак не зависимы.

    2. Ну смотря какую какую отрасль рассматривать. Я за 5 лет программинга сайтов кроме 4-х элементарных действий ничего не применял.

      Вот если бы это было что-то в движении, 3-д. Пришлось бы подумать.

  4. Антон, добрый день!
    Вопрос немного не по теме, но сам я не могу его решить.
    Имеется тестовая страничка http://test.web-management.ru/benefits.htm
    Подскажите пожалуйста, как можно связать движение ползунка с изменением слайдов внизу и чтобы при этом надпись “Сайт как инструмент бизнеса” постепенно заполнялся зеленым цветом. Я новичок в jQuery и не могу настроить такие эффекты.

  5. да, в web-dev математика играет не такую большую роль, как в других областях. Мои знания мат. и функционального анализа, дифф. ур-ий в частных производных, матфизики и фракталов нигде не пригодятся =D

  6. Уважаемый Антон!
    С НОВЫМ ГОДОМ! Позвольте выразить Вам самую искреннюю признательность за те материалы, которые Вы размещаете. Все предельно понятно, отлично структурировано, написано доступным языком и крайне полезно. Даже при учете того, что большая часть уже известна и применялась на практике неоднократно, на многие вопросы появляется совершенно иной взгляд (быть может, несколько иное понимание сути).
    Еще раз, огромное Вам спасибо! Успехов в Новом Году! :)
    С уважением,
    AUROSS (Андрей)

  7. Антон, наша главная страница перестала работать потому, что в новом jquery перестал существовать curAnim. Можете подсказать, где посмотреть пример кода, как переползти на jquery 1.6? Просто советы использовать jquery ui как-то мало помогают.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.