Розбираємося з анімацією в jQuery

Отже, мова піде про розсинхронізацію руху анімованих об'єктів, навіть якщо всі вони були анімовані одним викликом animate. Давайте розглянемо невеликий приклад одночасної анімації великої кількості об'єктів:
div class = "container" > div class = "animate" > div > div > script type ="text/javascript" > $( function () for ( var i = 0; i '.animate' ).clone().appendTo( '.container' ); > $( '.animate' ). animate( left: 800 >, 500); >); script > * Цей source code був висвітлений з Source Code Highlighter .
Теоретично всі ці об'єкти повинні рухатися синхронно. Насправді ми отримуємо таку картину:
Зрозуміло, що тест синтетичний, у реальній сторінці слід би рухати батька всіх цих об'єктів, але результат в наявності, анімація навіть у межах одного виклику animate стартує і закінчується в різний час. Тут було б логічним припустити, що для кожного об'єкта створюється окремий таймер, який його обслуговує. Але як я вже говорив вище, це припущення не є вірним. Порившись трохи у вихідниках, мені стало зрозуміло, що створюється толкоодин таймер на анімацію елементів всередині одного виклику animate. Більше того, для будь-якої анімації на сторінці, зроблену за допомогою jQuery, використовується лише один таймер, який видаляється після завершення останньої анімації на сторінці і створюється заново за нової необхідності щось анімувати.
Проте причина розсинхронізації близька до множинності таймерів, вона полягає в тому, що для кожного елемента час старту анімації вважається незалежно. Якщо елементів багато, між моментами підрахунку часу першого і останнього елемента, може пройти досить багато часу. Причому цей час буде тим більше, чим повільніше javascript-движок браузера, що ми і бачимо на скріншоті - значення розсинхронізації у опери 110 пікселів (скриншот в масштабі 1:2), у хрому 74 пікселів, а в іншого, дуже популярного, розсинхронізація браузера більше ширини тестового забігу, восьмиста пікселів.
Крім розбіжності анімації різних об'єктів, можливе розбіжність в анімації різних властивостей однієї й тієї ж об'єкта з тих-таки причин. Це може зіпсувати, наприклад, синхронну зміну паддингу та ширини, які в сумі повинні залишатися рівними. У реальній ситуації край об'єкту стрибатиме.
А тепер найголовніше. Враховуючи те, що таймер один, підправити час запуску анімації не є складним і за пару хвилин були внесені необхідні виправлення, що вирішують ці проблеми. Ці виправлення полягають у тому, що час старту анімації для всіх елементів вважається один раз при вході в функцію animate. Я навіть пішов далі, вніс в опції функції animate додатковий параметр startTime, який може приймати значення часу початку анімації. Це дозволить синхронізувати будь-яку кількість викликів функції animate між собою.
Крім синхронізації, можна використовувати початкове значення часу ще для кількох цікавих штук: 1) Задати час старту анімації в минулому і починати анімацію з середини. Зручно тим, що не доведеться виконувати окрему функцію ізінгу (easing) для половинки анімації. 2) Задати час старту в майбутньому, давши повільним движкам браузерів (привіт, IE!) прожувати всі об'єкти. Тут, щоправда, необхідна функція ізінгу, що призводить негативні зміщення часу до нульової координати. стандартні linear і swing не мають таких властивостей. 3) Робити анімацію у кілька проходів або нескінченну анімацію, задаючи час старту анімації у майбутньому. І тому потрібна буде функція изинга, здатна чітко обробити негативне усунення часу. У силу того, що стандартна функція ізінгу swing побудована на Math.cos, вона задовольняє цю вимогу.
Примітка, для тих хто не в курсі — функції ізінгу (easing, українською, напевно, це пом'якшення), це функції, що набувають значення від 0 до 1, де нуль — початковий час, 1 — кінцевий час анімації, і повертають значення також від 0 до 1, де 0 - початкове значення властивості, що змінюється, а 1 - кінцеве. У загальному випадку значення властивостей можуть лежати за межами 0 і 1, ну і з моїм патчем час теж може бути меншим за нуль.
Зміни, які я вніс у код
Час поточного кроку обчислювався функції jQuery.fx.step так: var t = now(); Я додав функції ще один параметр time та змінив визначення часу так: var t = time now();
До функції jQuery.fx.custom довелося додати ще один параметр startTime і обчислювати час старту анімації схожим чином з попередньою функцією: this .startTime = startTime now();
Крім того, всередині того самого єдиного setInterval було доданоотримання часу: var time = now (); і наступна передача цього часу до функцій з масиву, jQuery.timers[], який містить функції jQuery.fx.step.
У функції animate об'єкт optall був доповнений параметром startTime: optall = jQuery.extend(, optall)
Скажу чесно, виправлення особисто мені дуже потрібне, якщо я не знайду жодних проблем, я використовуватиму його як мінімум у своїх проектах. Але мене турбує те, що виправлення таке просте, але розробники jQuery не зробили його, хоча зробили набагато більшу підготовчу роботу з перенесення всієї анімації на один таймер. Можливо, в цьому є логіка і я чогось не бачу? Хто як вважає, чи варто намагатися проштовхнути мої зміни у фреймворк?
PS А ще я вчора зіткнувся з неробочою прозорістю в Опері 9.2 та останніх версіях jQuery. Опис проблеми та тимчасове рішення.
Хардкорна конфа за С++. Ми запрошуємо лише профі.