Если вы не используете какой-нибудь фреймворк, в котором уже реализованы методы программной анимации, то вам, скорее всего, будет интересно узнать, как их создать на "чистом" JavaScript, и с какими проблемами при реализации можно столкнуться. Эта запись будет посвящена именно этому вопросу.
Под программной анимацией мы понимаем изменение параметров визуального объекта через код приложения, в случае JavaScript это - изменение параметров стилей объектов HTML, либо объектов canvas. В связи с этим, существует несколько проблем, с которыми вы уже возможно сталкивались, когда пытались создать простейшие анимации, например, перемещение объекта.
Первая проблема связана с тем, что стили определенного элемента будут доступны в JavaScript через конструкцию [element].style только в двух случаях:
1. Если стиль был прописан в HTML inline-записью (в атрибуте style соответствующего тега), например:
<div style="position:absolute; top: 5px"></div>
В случае этого примера через JavaScript будут доступны значения свойств position и top объекта [element].style, а остальные свойства (в т.ч. установленные через CSS) будут недоступны.
2. Если соответствующие свойства были установлены через JavaScript, то они будут доступны везде, ведь на самом деле объект [element].style отражает состояние атрибута style соответствующего тега.
Если вам сложно понять, в чем же заключается проблема, взгляните на этот пример:
Как видите, хотя CSS-свойство margin-left и повлияло на отображение, его "нету" в объекте [element].style.
Справиться с этой проблемой можно получив значения из "вычисленного" или "текущего" стиля элемента. В соответствии с названиями, это будет метод getComputedStyle (Mozilla, Opera, ...) и объект currentStyle (IE).
В w3c-совместимых браузерах у объекта window доступен метод getComputedStyle, который принимает два параметра - сам элемент, для которого мы хотим получить стили, и "псевдо-элемент". Второй параметр обязательный и для простых элементов должен быть пустой строкой, или null. А вообще, этот параметр работает по аналогии с "псевдо-классами" в CSS, т.е. может принимать параметры "hover", "visited" и подобные.
Этот метод возвращает объект CSSStyleDeclaration, другими словами, объект такого же типа, как и [element].style, но в данном случае мы не можем менять свойства этого объекта, а только получать их значения.
Вот переделанный пример, подобный тому, что был представлен выше:
Этот пример, конечно же, не будет работать в браузерах Internet Explorer.
Стоит отдельно отметить, что getComputedStyle возвращает действительные значения стилей, которые будут изменяться в соответствии с изменением свойств объекта [element].style.
Если в вашей задаче необходимо узнать значения вычисленных свойств "по-умолчанию", то вы можете вызвать метод getComputedStyle у объекта document.defaultView с теми же параметрами, в результате получив вычисленные значения свойств строго указанных в CSS, или inline-стиле элемента.
В браузерах семейства Internet Explorer проблема доступа к действительным значениям стилей решена более просто и топорно.
Каждый элемент имеет свойство [element].currentStyle, которое представляет собой подобие объекта [element].style, но содержит текущие стили этого элемента. Как и в случае getComputedStyle, менять эти свойства - нельзя.
В отличие от getComputedStyle, мы не можем получить значения стилей "по-умолчанию" в браузере Internet Explorer, и кроме этого, не можем получить значения стилей для "псевдо-элементов".
Обратившись к предыдущим примерам, перепишем их для Internet Explorer:
Стоит отметить, что с помощью currentStyle нельзя получить значения для "составных" CSS свойств, которые перечисляют свойства через пробел (например background). Вам нужно обращаться к составляющим свойствам напрямую (например [element].currentStyle.backgroundColor, для цвета фона). В то же время, составные свойства margin и padding выводят верные значения.
Поскольку различий в реализации не так уж и много, будет очень просто получать значение текущего вычисленного стиля кросс-браузерно:
var computedStyle = element.currentStyle || window.getComputedStyle(element, null);
В скриптах, которые будут изменять свойства стилей элементов, лучше реализовать следующий принцип работы:
Если свойство стиля, с которым будет производиться работа, не доступно в объекте [element].style, получаем его из текущих вычисленных стилей и "запоминаем", или обновляем соответствующее свойство в [element].style. После этого нам уже не нужно обращаться к действительным стилям, ведь значение интересующего нас свойства будет соответствовать значению в [element].style.
Вторая проблема, с которой сталкиваешься при работе со свойствами стилей, заключается в том, что при изменении значений определенных свойств из JavaScript, нужно указывать единицы измерения вместе с числовым значением.
Обычно, если мы хотим переместить какой-то элемент, то мы получаем его текущее положение и прибавляем к нему определенное значение. Но значения в CSS хранятся в виде строки, например "15px". Поскольку в JavaScript оператор "+" производит конкатацию, если один из элементов операции - строка, то обычные операции, как с числами, тут не сработают:
var a = "15px";
a++; // NaN - not a number
var a = "15px";
a += 5; // 15px5
var a = "15px";
var b = a + 7; // 15px7
И наоборот, если мы попытаемся обновить свойство, приравняв ему число, то в режиме следования стандартам - ничего не произойдет, так как не указаны единицы измерения.
Поэтому, мы должны всегда следить за тем, в каком виде мы работаем с значением свойства и в каком виде мы его записываем. Работать удобнее в виде числа, а записывать нужно в виде строки, содержащей единицы измерения.
В общем виде, работа должна происходить так:
// Допустим, мы работаем с каким то элементом,
// для которого доступен style.top:
var top = parseInt(element.style.top); // Получили число из строки
top++; // Увеличили его на единицу
// Так как "px" строкового типа, то произойдет
// конкатация и будет записано правильное значение
// с единицами измерения:
element.style.top = top + "px";
Обратите внимание, что в последней строке мы можем сделать вот так:
element.style.top = top + 25 + 100 + "px";
Потому что при последовательном вычислении выражения конкатация произойдет только в последнюю очередь, а выражения слева от строки будут складываться, как числа.
Такие погрешности измерений, конечно же, редко когда "всплывают", но такие случаи, к сожалению, случаются. Самый распространенный случай, когда вы создаете цикл, на подобии:
while(left != 50) {
// ... тут код ...
}
И увеличиваете позицию на нецелое число. В итоге значения 50 может и не быть вовсе, даже если вы 250 раз прибавите 0.2:
var c = 0;
for (var i = 0; i < 250; i++) {
c += 0.2;
}
alert(c); // 50.00000000000017
Безусловно, значением свойства стиля [element].style.top, не может быть нецелое число, тем не менее, такой прием часто используют для контроля над скоростью перемещения.
Чтобы избежать этой проблемы, нужно всегда округлять нецелые числа до нужных нам порядков (вплоть до целого числа). Благо для этого в JavaScript существуют методы:
// Взять определенное количество чисел, после запятой:
parseFloat(50.03000000000017.toPrecision(4)); // 50.03
// Округление до целого в меньшую сторону:
Math.floor(0.4); // 0
// Округление до целого в "ближайшую" сторону:
Math.round(0.4); // 0
Math.round(0.6); // 1
// Округление до целого в большую сторону:
Math.ceil(0.4); // 1
Кроме того, при описании условия цикла перемещения, лучше использовать сравнение "больше"/"меньше", чтобы быть полностью защищенным от ошибок вычислений.
Для создания программной анимации вам понадобится цикл, внутри которого будут постепенно изменяться какие-то параметры объекта, например его положение или размер.
Но в случае обычного цикла, например for или while, выполнение всех скриптов на странице, кроме самого перемещения, будет остановлено, браузер "подвиснет", пока перемещение не будет закончено. Поскольку JavaScript язык однопоточный, единственный доступный нам вариант - таймер.
Говоря в общем смысле, программная анимация это таймер и математическая формула, высчитывающая значение в определенный момент времени.
Обычно, для конечной анимации используется тайм-аут, а для бесконечной - интервал.
Первым параметром функции setTimeout и setInterval принимают функцию, а вторым параметром - количество миллисекунд, через которое эта функция будет выполнена. В случае интервала, функция будет выполняться бесконечное количество раз, со тем значением интервала, которое было задано вторым параметром.
Эти функции возвращают идентификатор, по которому соответствующий тайм-аут/интервал можно "очистить" функциями clearTimeout / clearInterval соответственно.
Обычно удобнее использовать "рекурсивные" тайм-ауты, для создания программной анимации:
setTimeout(function() {
/* Тут изменение параметров */
if (/* условие срабатывания итерации */)
setTimeout(arguments.callee, 0);
}, 0);
Это удобно потому, что вам не нужно очищать такой тайм-аут, ведь когда анимация закончится, новый тайм-аут просто не будет установлен. И во-вторых, в отличие от интервала, все итерации такого тайм-аута будут выполнены строго последовательно и только тогда, когда изменение параметров одной итерации закончено, поэтому не будет ситуации "гонки выполнения".
В первом кадре анимации нам не нужны никакие изменения параметров, поэтому даже первая итерация "цикла" вызывается по тайм-ауту.
В данном примере я поставил "нулевой" тайм-аут, который будет выполнен сразу, как только выполнение JavaScript выйдет из того контекста, в котором был объявлен этот тайм-аут.
Редко когда нужна такая частая обработка анимации, поэтому можно в качестве второго параметра указать 10 (100 обработок в секунду), или больший тайм-аут, но никогда нельзя быть уверенным в том, что "тайминг" не собьется, ведь выполнение обработки элемента происходит совсем не моментально.
Программная анимация это изменение параметра объекта по определенному математическому закону в зависимости от времени. Это может быть линейная функция, квадратичная функция, синусоида, асимптотическая функция и т.д.
К сожалению, именно этот момент часто "провисает" у тех людей, которые занимаются исключительно веб-программированием. Многие забыли школьную математику, а другие до сих пор боятся слова "синус". В этом разделе я постараюсь привести все популярные формулы, которые используются в программной анимации.
Итак, для создания анимации нам нужно знать начальное значение, конечное значение, длительность анимации (в миллисекундах, или кадрах), и начальное время анимации.
В идеале, вы не должны анимировать объекты используя в качестве переменной времени - "кадр" анимации, но в некоторых случаях это может быть полезно (когда не важно точное время исполнения, или нужна максимально быстрая анимация). Формулы для "кадров" и настоящего времени будут одинаковые, различны будут лишь их входные параметры.
Итак, для начала определим, с какими параметрами мы будем работать:
from - начальное состояние изменяемого параметра. to - конечное состояние изменяемого параметра. duration - количество миллисекунд (или кадров), за которое должно быть достигнуто конечное значение. now - текущий момент времени (или кадр) с начала анимации. progress - значение положения анимации от 0 до 1.
По сути, progress это отношение now к duration.
Для удобства, можно вычленить общую часть из всех законов перемещения и получить общую формулу:
result = (to - from) * delta(progress) + from
Где delta - закон изменения параметра в зависимости от положения анимации (т.е. от progress).
Дальше будут рассматриваться некоторые из возможных функций delta, для каждой из которых будет приведена формула, график и пример перемещения блока на JavaScript.
Для всех примеров будет использоваться такой код:
var element = document.getElementById("example");
var from = 0; // Начальная координата X
var to = 500; // Конечная координата X
var duration = 1000; // Длительность - 1 секунда
var start = new Date().getTime(); // Время старта
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
if (progress < 1) // Если анимация не закончилась, продолжаем
setTimeout(arguments.callee, 10);
}, 10);
Где будем объявлять разные функции delta, для наших примеров, а сам код будем выполнять по событию click на элементе примера.
В этом коде можно наткнуться на проблему неточных вычислений (о которой мы говорили выше). Поэтому в каждой итерации нужно проверять не закончилась ли анимация (progress > 1), и ставить анимацию в позицию конца. Кроме этого, нужно округлять числа до нужной точности (кол-ва знаков после запятой) и до целых чисел.
Кроме того, нужно обрабатывать "двойной" старт анимации, когда функция вызывается в тот момент, когда другая (или та же самая) анимация уже выполняется. Обычно нужно контролировать таймер и останавливать его, если одну анимацию прерывает другая.
Я опущу эти моменты в примерах, но вам лучше всегда контролировать свои анимации, если вы хотите добиться нужной точности.
К сожалению, этот пример не вносит ясность, для чего используется эта формула, потому что в нем вычисляется синус по возрастающей, а значение достигает максимума. Если бы движение не было ограничено достижением верхней точки, то блок возвращался к началу и продолжал движение туда-сюда по синусоидальному закону.
Эта функция изменяет параметр по типу "лука", т.е. мы сначала "отводим тетиву", а потом "стреляем". В отличие от других функций, она зависит от двух параметров.
Математическая формула: delta = progress2 * ((x + 1) * progress - x)
Где x - коэффициент, через который высчитывается расстояние, на которое мы "отводим" параметр.
Представьте себе, что мы отпускаем мячик и он падает на пол, после чего несколько раз подскакивает и останавливается. Эта функция описывает изменение параметра в точности наоборот - параметр будет "подскакивать", после чего достигнет высшей точки.
Эта функция несколько сложнее приведенных выше, и математической функции (как и графика) для нее я привести не могу, (потому что не знаю, как такая конструкция будет описываться в математике). Если кто-то сможет мне в этом помочь, отпишите в комментариях, буду очень благодарен.
JavaScript функция delta:
function delta(progress) {
for(var a = 0, b = 1, result; 1; a += b, b /= 2) {
if (progress >= (7 - 4 * a) / 11)
return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);
}
}
Код этой функции взят из MooTools.FX.Transitions, как я понимаю, числа в этой функции были подобраны. Есть еще несколько разных реализаций Bounce, но это - самая короткая.
Математическая формула: delta = 2(10 * (p - 1)) * cos(20 * progress * П * x / 3)
Где x - коэффициент "инетрности", другими словами, на сколько значение будет колебаться в начале движения.
Для того, чтобы было понятно, о чем идет разговор, будем оперировать понятиями сходными с параметрами Tween в пакете Adobe Flash.
Функции, которые были приведены выше, реализуют эффект в начале анимации (easeIn). Но бывают случаи, когда нам нужно реализовать эффект в конце анимации (easeOut), либо и в начале в конце сразу (easeInOut).
Для создания эффекта в конце анимации нам нужно просто "обратить" нашу функцию delta следующим образом: delta = 1 - delta(1 - progress)
Давайте рассмотрим пример с обратной (easeOut) функцией Bounce (кликните для анимации):
JavaScript функция delta:
function delta(progress) {
function d(progress) {
for(var a = 0, b = 1, result; 1; a += b, b /= 2) {
if (progress >= (7 - 4 * a) / 11)
return -Math.pow((11 - 6 * a - 11 * progress) / 4, 2) + Math.pow(b, 2);
}
}
return 1 - d(1 - progress);
}
Конечно, ее можно обернуть лучше (сделать это лучше в таймере анимации), но для примера должно быть достаточно.
Немного сложнее сделать эффект и в начале, и в конце анимации (easeInOut).
Если значение progress меньше 0.5 (т.е. сейчас идет первая половина анимации), то функция delta будет высчитываться по формуле: delta = delta(2 * progress) / 2
Если значение progress больше 0.5 (т.е. отображается вторая половина анимации) - то: delta = (2 - delta(2 * (1 - progress))) / 2
Другими словами, мы меняем функцию на обратную на половине выполнения анимации.
Давайте рассмотрим easeInOut пример с функцией Elastic (кликните для анимации):
В отличие от других примеров, в этом время анимации было заменено на 2.5 секунды, иначе она происходит слишком прерывисто. В среднем, длительность easeInOut анимации лучше увеличивать в полтора раза, по сравнению с эффектами по отдельности для начала, или конца анимации.
Поскольку "анимировать" можно абсолютно любой параметр, то сфера применения программной анимации ограничена лишь вашей фантазией.
Вместо координаты (как в примерах в этой записи), вы можете изменять параметр прозрачности, высоту или ширину элемента и даже цвет.
Вот, например, блок, который изменяет свой цвет по закону формулы Bounce с easeOut:
Кликни меня, я стану зеленым!
Или вы можете вообще создать какие-нибудь замысловатые анимации, например ввода текста:
Кроме того, вы можете применять такие анимации в элементах canvas, или при отображении vml/svg, в общем - везде, где это будет к месту.
Эта запись сделана не для копипаста, а для размышлений, поэтому я не привожу код, который можно было бы взять и использовать.
Это, скорее всего, не последняя запись на тему анимации. Когда будет (свободного не бывает) время, напишу про параллельные анимации и перемещение по траектории.
Прошу о всех ошибках/замечаниях (в т.ч. по части орфографии и пунктуации) писать в комментарии, кроме того, интересно было бы услышать идеи и предложения.
Стоит упомянуть, что в методе currentStyle используются camelStyle-имена CSS-свойств, а в getComputedStyle - синтаксис, принятый в CSS.
И currentStyle не возвращает значения свойст, имеющих составные значения, например нельзя получить заначение background-position, нужно отдельно получать background-position-x и background-position-y.
На счет currentStyle и композитных свойств - спасибо, сейчас внесу это в статью.
А на счет camelStyle - в w3c-совместимых можно получать значения свойств двумя способами: либо через свойства (с синтаксисом camelStyle), либо вызывая метод getPropertyValue - где имя свойства указывается в синтаксисе CSS. Причем это не зависит от того, работаем мы с объектом [element].style, или с window.getComputedStyle(). Для совместимости с currentStyle лучше использовать "общий" синтаксис, а именно - camelStyle.
У меня не хватило времени, чтоб написать так, как я хотел.
Я хочу еще парочку записей по этой теме сделать, а потом можно будет скомпоновать в одну-две полноценных статей и выложить в основные материалы сайта.
Результат вычислений (result) нужно присваивать свойству стиля элемента в том случае, если анимация не закончилась. Если анимация закончилась, присваеваем конечные значения явно, т.к. последнее значение progress не будет равно 1, а во многих случаях тончость результата важна.
Андрей, спасибо Вам огромное за статью, я сейчас делаю HTML презентацию и наткнулся на вашу статью во время поисков эффектов для всплывания текста.
Моя презентация целиком построена на основе amcharts.com и ammap.com и как раз не хватало эффектов баунсинга для обычных текстовых сообщений.
Я не большой спец по джаваскрипту, но на основе статьи сварганил скрипт на VBS для вываливания нескольких сообщений сверху экрана (). Ваша статья - это как раз то что нужно - супер просто.
var element = document.getElementById("mydiv");
var from = 0; // Начальная координата X
var to = 500; // Конечная координата X
var duration = 1000; // Длительность - 1 секунда
var start = new Date().getTime(); // Время старта
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
if (progress < 1) // Если анимация не закончилась, продолжаем
setTimeout(arguments.callee, 10);
}, 10);
Супер статья, спасибо огромное.
Единственное дополнение. В приведенном примере bounce невозможно нормально его настроить как хочется.
Вот примерный код для этой функции с изменяемыми параметрами
function(p, n, b) {
// Это easeOut вариант функции (в статье приведен easeIn)
// p - прогресс
// n - кол-во подпрыгиваний (в статье n=4)
// b - высота следующего подскока в отношении к высоте предыдущего (в статье b=0.25)
var sum = [1];
for(var i=1; i<n; i++) {
sum[i] = sum[i-1] + Math.pow(b, i/2);
}
var x = 2*sum[n-1]-1;
for(var i=0; i<n; i++) {
if(x*p >= (i>0 ? 2*sum[i-1]-1 : 0) && x*p <= 2*sum[i]-1) {
return Math.pow(x*(p-(2*sum[i]-1-Math.pow(b, i/2))/x), 2)+1-Math.pow(b, i);
}
}
return 1;
}
Вероятно можно упростить код. Буду рад увидеть улучшенные варианты.
К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2
π
t/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.
К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2πt/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.
А не судьба для каждой строки выложить код? Или хотя бы для первой анимации, а то хз куда начинающему программисту на Java всовывать для каждой новой анимации разную функцию.
Доброго времени суток, Увожаемые. Спасибо огромное за статью. Но у меня возник вопрос:
Если анимацию вызывать по событию onmouseover, как ее остановить событием onmouseout?
Спасибо.
Я новичек в JS, прошу сильно не бить )
А если меня аткая ситуация. При клике выполняется функция которая изменяет значение magin-left, как сделать так чтобы при следующем клике изменялось уже изменное значение. Ну допустим при первом клике мы изменили значение на 50px, на втором тоже меняем на 50, но добавляя его к прошлому, тоетсь получится 100
getCompudedStyle может вычислить стиль элемента, даже если он не задан в CSS, например высоту блочного элемента, которая меняется от количества содержимого(кроме IE, он в таком случае своим currentStyle дает auto). Но, узнал сегодня что ее можно "снять" этим методом, только если он уже добавлен на страницу. Столкнулся с этим когда пытался записать высоту блока в его свойство, сразу после создания и добавления контента. Возвращало NAN. А когда делал это уже после того как добавил элемент на страницу, все было правильно.
А я присваиваю стилям значения переменных без "px" и у меня все работает. Кроме того, я не беру у объектов текущие значения стилей, а только присваиваю. Вся анимация и математика делается только на переменных. Это намного проще, но проблема в том, что переменные должны быть глобальными и не пересекаться с другими анимациями. Это легко решить если все переменные буквами а в разных анимациях в конце приписываются разные числа. Мог бы дать ссылку на сайт, но не умею. Вот ссылка slideshow
Анимация не выполняется, багер выдает (Ошибка при анализе значения «margin-left». Потерянное объявление.)
вот код всего дока
function animClick (){
var anim = document.getElementById('anim');
var from = 0;
var to = 500;
var duration = 1000;
var start = new Date().getTime();
setTimeout(function(){
var now = (new Date().getTime) - start;
var progress= now / duration;
var result = (to - from) * delta(progress) + from;
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
if (progress < 1) // Если анимация не закончилась, продолжаем
setTimeout(arguments.callee, 10);
}, 10);
Вот так:
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
if (progress < 1)
{
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
setTimeout(arguments.callee, 10);
}
else
{
progress = 1;
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
}
}, 10);
Тем самым можно избежать некорректного результата при progress > 1 (актуально при анимации цвета) и гарантировать выполнение последнего рассчёта при progress = 1
Cтатья интересна, познавательна. Радует еще и то, что в элементе Канвас ХТМЛ5 с помощью яваскрипта можно выделывать все что угодно. Геометрию визуализировать. За канвасом будущее.
Статья хорошая,...
а кто математику в школе учил, тот знает, что график функции - это много точек на плоскости с коорлинатами Х и У, где Х - это значение передаваемое функции (в данном случае - progress), а У - это возвращённый ею результат.
Подскажите кто то ,как связать одно с другим. А то я новичок ,и пока кое что не понимаю.
Меня интересует как прикрепить код Javascript к div ,чтобы произошло движение самой анимации.А то я уже куча сайтов облазил,и как то не получается.
Помогите пожалуйста разобраться:
У меня всё та же проблема с подсчетом:
2550+53.70+36.00+87.20=2726.8999999999996
соответсвенно должно быть 2726.90
Фигня какая-то, попробовал Math.round(total); и Math.round(all_sum);
но почему то не получается, может куда-то не туда вставляю его или что-то другое нужно написать.
Заранее спасибо!
Вот код:
function load_item(xml)
{
$('#choice').load("load_xml.php",{xml:xml},onAjaxSuccess);
$('#price').load("price.php",{xml:xml},onAjaxSuccess);
refresh();
}
function load_image(image)
{
$('#image').load("image.php",{image:image});
}
function onAjaxSuccess()
{
refresh();
}
function minim(value)
{
if(value==true)
{
var elems = document.getElementsByClassName('item');
for(var i=0; i<elems.length; i++)
{
elems[i].checked=false;
if(elems[i].getAttribute('min')==1)
{
elems[i].checked=true;
}
}
}
else
{
var elems = document.getElementsByClassName('item');
for(var i=0; i<elems.length; i++)
{
elems[i].checked=false;
}
}
refresh();
}
function calc_all_sym(count,total)
{
var all_sum=parseInt(count)*parseInt(total);
document.getElementById('all_sym').innerHTML="Общая сумма: "+all_sum+"€";
refresh();
}
function change_item_count(id,value)
{
elem=document.getElementById('a'+id);
elem.setAttribute('count',value);
refresh();
}
function refresh()
{
elem=document.getElementById('all_sum');
if(elem!=null)
{
var konf_count=document.getElementById('all_sum').value;
}
else
{
konf_count=1;
}
var total=0;
var model=document.getElementById('m');
var image_name=document.getElementById('image_name').value;
if(model!=null)
{
var model_id=model.value;
}
var my_select=document.getElementById('select');
var my_select_value=my_select.value;
var my_cost=document.getElementById('q');
var my_cost_value=my_cost.value;
var total=parseInt(my_cost_value);
document.getElementById('configuration').innerHTML="<h3>Собранная Вами конфигурация:</h3>";
document.getElementById('configuration').innerHTML+="<input type=hidden name=model_id value='"+model_id+"'></input>";
var itemGroups=document.getElementsByName('itemGroup');
document.getElementById('configuration').innerHTML+="<h4>Модель: <b>"+my_select_value+"</b> – Цена: € "+my_cost_value+"</h4>";
for(var x=0; x<itemGroups.length; x++)
{
var t=x+1;
document.getElementById('configuration').innerHTML+="<h4 id=hgroup"+t+" style=display:none;>"+itemGroups[x].value+"</h4><div id=group"+t+"></div>";
}
var form1=document.getElementById('configuration');
var input1='';
var vname=image_name;
var elems = document.getElementsByClassName('item');
for(var y=0; y<elems.length; y++)
{
if((elems[y].checked==true))
{
val=document.getElementById(elems[y].id).value;
if(val>0)
{
var group=elems[y].getAttribute('group');
if(elems[y].getAttribute('vname')!='')
{
vname+='_'+elems[y].getAttribute('vname');
}
zakaz=document.getElementById('a'+elems[y].id);
count=zakaz.getAttribute('count');
cost=zakaz.getAttribute('cost');
itemID=zakaz.getAttribute('item_id');
total=total + count*cost;
if(count>1)
{
$('#group'+group).append('<div class=selected>'+zakaz.value+' '+count+'шт. (€ '+count*cost+')</div>');
}
else
{
$('#group'+group).append('<div class=selected>'+zakaz.value+'</div>');
}
document.getElementById('hgroup'+group).style.display='block';
input1+="<input type=hidden name=item["+itemID+"] value='"+count+"'></input>";
}
}
}
document.getElementById('configuration').innerHTML+="<input type=hidden name=model value='"+my_select_value+"'></input>";
document.getElementById('configuration').innerHTML+="<hr size=1 width=450 align=left><h2>Цена заказа: € "+total+" </h2><hr size=1 width=450 align=left>";
document.getElementById('configuration').innerHTML+=input1;
document.getElementById('configuration').innerHTML+="<h4>Количество конфигураций: <input name=konf_count id='all_sum' type=text size=1 value='"+konf_count+"' onchange='calc_all_sym(this.value,"+total+");'></input></h4>";
document.getElementById('configuration').innerHTML+="<h4 id=all_sym>Общая сумма: € "+document.getElementById('all_sum').value*total+" </h4>";
document.getElementById('configuration').innerHTML+="<input type=hidden name=all_cost value='"+document.getElementById('all_sum').value*total+"'></input>";
document.getElementById('configuration').innerHTML+="<input type=hidden name=cost value='"+total+"'></input>";
document.getElementById('configuration').innerHTML+="<input class=btn type=submit value=Заказать></input>";
vname+='.png';
load_image(vname);
}
Программная анимация это изменение параметра объекта по определенному математическому закону в зависимости от времени. Это может быть линейная функция, квадратичная функция, синусоида, асимптотическая функция и т.д.
К сожалению, именно этот момент часто "провисает" у тех людей, которые занимаются исключительно веб-программированием. Многие забыли школьную математику, а другие до сих пор боятся слова "синус". В этом разделе я постараюсь привести все популярные формулы, которые используются в программной анимации.
Андрей, я написал вот такой код на основе вашего кода:
function select(selector) {
var s = typeof selector == 'string' ? document.querySelector(selector) : selector;
this.animate = function (options, duration, delay) {
var start = new Date().getTime();
duration = duration || 500;
delay = delay || 10;
if (delay < 10) delay = 10;
else if (delay > 2000) delay = 2000;
for (var prop in options) {
var value = options[prop],
currentValue = parseInt(s.style[prop]);
setTimeout(function anim() {
var now = (new Date().getTime()) - start,
progress = now / duration,
result = (value - currentValue) * progress + currentValue;
s.style[prop] = result;
if (progress < 1) setTimeout(anim, delay);
}, delay);
}
};
return this;
}
select('#test').animate({
width: 57,
height: 57
}, 2000);
ну что-то на подоьие jquery.. всё прекрасно работает, но если свойств всего один, а если их больше, в таком случае анимация применяется только к последнему, в чём проблема? может быть что код некорректный? я просто только начал изучать ооп относительно js, поэтому подскажите пожалуйста как правильно поступить
статья - класс! но есть вопрос: функция delta(progress)<=1 что б анимация работала правильно? а то у меня с анимацией фиг знает что делается когда именно дельта больше 1. а ещё, если delta(progress)<1 при progress=1 то анимация не полностью выполняется =( где ошибка?
Отличный пост, спасибо большое. Побольше бы таких.
Потрясающая статья. Спасибо!
Добавил статью на главную сайта.
Стоит упомянуть, что в методе currentStyle используются camelStyle-имена CSS-свойств, а в getComputedStyle - синтаксис, принятый в CSS.
И currentStyle не возвращает значения свойст, имеющих составные значения, например нельзя получить заначение background-position, нужно отдельно получать background-position-x и background-position-y.
На счет currentStyle и композитных свойств - спасибо, сейчас внесу это в статью.
А на счет camelStyle - в w3c-совместимых можно получать значения свойств двумя способами: либо через свойства (с синтаксисом camelStyle), либо вызывая метод getPropertyValue - где имя свойства указывается в синтаксисе CSS. Причем это не зависит от того, работаем мы с объектом
[element].style
, или сwindow.getComputedStyle()
. Для совместимости с currentStyle лучше использовать "общий" синтаксис, а именно - camelStyle.Афигеть, дайте две!
Спасибо.
Класс! )
> Изменение степени меняет время нарастания скорости, например для показателя
> степени 5 график будет следующий:
А как сделать постепенное замедление? Указать степень -5?
Нет, достаточно "обратить" функцию (easeOut).
не могу установить джава скрипт,какой то сбой произошел,в интернете не работают многие функции.
Отличная статья! Доступно и грамотно. Молодец, Андрей!
Прочитал статью повнимательнее - она еще интереснее, чем я думал. Спасибо, Андрей!
У меня не хватило времени, чтоб написать так, как я хотел.
Я хочу еще парочку записей по этой теме сделать, а потом можно будет скомпоновать в одну-две полноценных статей и выложить в основные материалы сайта.
Результат вычислений (result) нужно присваивать свойству стиля элемента в том случае, если анимация не закончилась. Если анимация закончилась, присваеваем конечные значения явно, т.к. последнее значение progress не будет равно 1, а во многих случаях тончость результата важна.
За статью огромное спасибо.
Примите мои извинения, Андрей, нашел вашу заметку к коду...
классная статья
Статья просто супер, очень помогла при подготовке магистерской дипломной работы !!!!!!!!!!!!!!!!!!!!!
клас.
вопрос: вот Вы приводите график функции математического закона : синус, линейная и т.д. и соответсвенно картинки для них. Они гиф.
а как вот их строить явой ? чтоб и в опере работало и в ие ?
(да, есть свг, и флэш, а если без них ?).
Андрей, спасибо Вам огромное за статью, я сейчас делаю HTML презентацию и наткнулся на вашу статью во время поисков эффектов для всплывания текста.
Моя презентация целиком построена на основе amcharts.com и ammap.com и как раз не хватало эффектов баунсинга для обычных текстовых сообщений.
Я не большой спец по джаваскрипту, но на основе статьи сварганил скрипт на VBS для вываливания нескольких сообщений сверху экрана (). Ваша статья - это как раз то что нужно - супер просто.
Спасибо за статью.
Вот только ф-цию "CIRC" можна зделать проще (сэкономит потом драгоценные ресурсы)
delta = 1 - sqrt(1-progress*progress);
Кому интересно почему может посмотреть в учебник по тригонометрии
реверсная опечатка: коэффициент "инертности"
Спасибо большое! Полезная статья...
Иногда лучше чистый Java Script вместо jquery если чтобы не тормозило.
function onClick()
{
var element = document.getElementById("mydiv");
var from = 0; // Начальная координата X
var to = 500; // Конечная координата X
var duration = 1000; // Длительность - 1 секунда
var start = new Date().getTime(); // Время старта
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
if (progress < 1) // Если анимация не закончилась, продолжаем
setTimeout(arguments.callee, 10);
}, 10);
}
function delta(progress) {
return progress;
}
работает
И что теперь математику учить
Действительно, очень полезная статья, только если бы вы дали исходники, было бы намного проще новичкам понять что к чему
как изменить такое представление?
таким образом не выходит:
Супер статья, спасибо огромное.
Единственное дополнение. В приведенном примере bounce невозможно нормально его настроить как хочется.
Вот примерный код для этой функции с изменяемыми параметрами
Вероятно можно упростить код. Буду рад увидеть улучшенные варианты.
Андрей!
У меня проблемы вот на этой странице:
element.style.left = result + "px";
Идет высветка "требуется объект"
С уважением
Влад
Андрей!
У меня проблемы вот на этой странице:
element.style.left = result + "px";
Идет высветка "требуется объект"
С уважением
Влад
у меня была такая же проблема, вылечил так:
element.style.left = Math.round(result) + "px";
К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2
t/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.
К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2πt/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.
А не судьба для каждой строки выложить код? Или хотя бы для первой анимации, а то хз куда начинающему программисту на Java всовывать для каждой новой анимации разную функцию.
здесь примеры рабочие, можешь в файербаге их просмотреть.
Спасибо!
Доброго времени суток, Увожаемые. Спасибо огромное за статью. Но у меня возник вопрос:
Если анимацию вызывать по событию onmouseover, как ее остановить событием onmouseout?
Спасибо.
Я новичек в JS, прошу сильно не бить )
Напиши обработчик который возвращает назад произведенные действия.
Скиньте пожалуйста исходник для этой статьи, очень поможете.
Статья просто суппер! Давно искал подобный урок! Спасибо огромное.
Дополнение: метод
.currentStyle
работает еще и в Опере<зануда>Только это не метод, а свойство.</зануда>
А если меня аткая ситуация. При клике выполняется функция которая изменяет значение magin-left, как сделать так чтобы при следующем клике изменялось уже изменное значение. Ну допустим при первом клике мы изменили значение на 50px, на втором тоже меняем на 50, но добавляя его к прошлому, тоетсь получится 100
Нашел вот такую формулу для EaseInOut:
Супер пост.
ЕГо надо на главную страницу, или в статьи как минимум добавить.
а не подскажите как сделать одновременную анимация. Например при клике на кнопке должно двигаться 2 или 3 блока.
getCompudedStyle может вычислить стиль элемента, даже если он не задан в CSS, например высоту блочного элемента, которая меняется от количества содержимого(кроме IE, он в таком случае своим currentStyle дает auto). Но, узнал сегодня что ее можно "снять" этим методом, только если он уже добавлен на страницу. Столкнулся с этим когда пытался записать высоту блока в его свойство, сразу после создания и добавления контента. Возвращало NAN. А когда делал это уже после того как добавил элемент на страницу, все было правильно.
Вот и пригодилась математика.
Объясните пожалуйста, как вывели эту формулу:
И, предположим, у меня есть график прямой y = kx + b. Каким образом, по какой формуле можно из этой функции найти delta?
супер, сппасибо
Класс
компьютерная графика - очень интересно!
Я в восторге! Столько материала, и так грамотно изложить. Спасибо, Андрею!!!
А я присваиваю стилям значения переменных без "px" и у меня все работает. Кроме того, я не беру у объектов текущие значения стилей, а только присваиваю. Вся анимация и математика делается только на переменных. Это намного проще, но проблема в том, что переменные должны быть глобальными и не пересекаться с другими анимациями. Это легко решить если все переменные буквами а в разных анимациях в конце приписываются разные числа. Мог бы дать ссылку на сайт, но не умею. Вот ссылка slideshow
Подскажите плиз.
Анимация не выполняется, багер выдает (Ошибка при анализе значения «margin-left». Потерянное объявление.)
вот код всего дока
function animClick (){
var anim = document.getElementById('anim');
var from = 0;
var to = 500;
var duration = 1000;
var start = new Date().getTime();
setTimeout(function(){
var now = (new Date().getTime) - start;
var progress= now / duration;
var result = (to - from) * delta(progress) + from;
anim.style.marginLeft = Math.round(result) + 'px';
if (progress < 1)
setTimeout(arguments.callee, 10);
}, 10);
}
function delta(progress){
return progress;
}
#anim{
width:20px;
height:20px;
margin-left:0;
background:#333;
}
PS: сильно не бейте)
Пропиши вместо "margin-left" marginLeft
Предлагаю модифицировать этот фрагмент:
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
if (progress < 1) // Если анимация не закончилась, продолжаем
setTimeout(arguments.callee, 10);
}, 10);
Вот так:
setTimeout(function() {
var now = (new Date().getTime()) - start; // Текущее время
var progress = now / duration; // Прогресс анимации
if (progress < 1)
{
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
setTimeout(arguments.callee, 10);
}
else
{
progress = 1;
var result = (to - from) * delta(progress) + from;
element.style.left = result + "px";
}
}, 10);
Тем самым можно избежать некорректного результата при progress > 1 (актуально при анимации цвета) и гарантировать выполнение последнего рассчёта при progress = 1
Подскажите пожалуйста, как модифицировать код Bounce, чтобы отскоков было не 4, а скажем 3 или 2?
Cтатья интересна, познавательна. Радует еще и то, что в элементе Канвас ХТМЛ5 с помощью яваскрипта можно выделывать все что угодно. Геометрию визуализировать. За канвасом будущее.
Статья хорошая,...
а кто математику в школе учил, тот знает, что график функции - это много точек на плоскости с коорлинатами Х и У, где Х - это значение передаваемое функции (в данном случае - progress), а У - это возвращённый ею результат.
Подскажите кто то ,как связать одно с другим. А то я новичок ,и пока кое что не понимаю.
Меня интересует как прикрепить код Javascript к div ,чтобы произошло движение самой анимации.А то я уже куча сайтов облазил,и как то не получается.
Помогите пожалуйста разобраться:
У меня всё та же проблема с подсчетом:
2550+53.70+36.00+87.20=2726.8999999999996
соответсвенно должно быть 2726.90
Фигня какая-то, попробовал Math.round(total); и Math.round(all_sum);
но почему то не получается, может куда-то не туда вставляю его или что-то другое нужно написать.
Заранее спасибо!
Вот код:
Круто, разобрав алгоритм почувствовал просветление =)
Программная анимация это изменение параметра объекта по определенному математическому закону в зависимости от времени. Это может быть линейная функция, квадратичная функция, синусоида, асимптотическая функция и т.д.
К сожалению, именно этот момент часто "провисает" у тех людей, которые занимаются исключительно веб-программированием. Многие забыли школьную математику, а другие до сих пор боятся слова "синус". В этом разделе я постараюсь привести все популярные формулы, которые используются в программной анимации.
Дуже велике дякую!)
Не работают примеры. Браузер Opera 24.0
доброго времени суток
Андрей, я написал вот такой код на основе вашего кода:
ну что-то на подоьие jquery.. всё прекрасно работает, но если свойств всего один, а если их больше, в таком случае анимация применяется только к последнему, в чём проблема? может быть что код некорректный? я просто только начал изучать ооп относительно js, поэтому подскажите пожалуйста как правильно поступить
В Google chrome не работает
статья - класс! но есть вопрос: функция delta(progress)<=1 что б анимация работала правильно? а то у меня с анимацией фиг знает что делается когда именно дельта больше 1. а ещё, если delta(progress)<1 при progress=1 то анимация не полностью выполняется =( где ошибка?
у меня не работает ни один клик кроме зеленеющей полоски и движ текста