Javascript.RU

Основы программной анимации на JavaScript

Если вы не используете какой-нибудь фреймворк, в котором уже реализованы методы программной анимации, то вам, скорее всего, будет интересно узнать, как их создать на "чистом" 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 соответствующего тега.

Если вам сложно понять, в чем же заключается проблема, взгляните на этот пример:

<style type="text/css">
    .someClass {
        margin-left: 15px;
    }
</style>
<div onclick="alert(this.style.marginLeft || 'Нету :(')" class="someClass">Нажми на меня</div>

Пример "в живую":

Нажми на меня

Как видите, хотя CSS-свойство margin-left и повлияло на отображение, его "нету" в объекте [element].style.

Справиться с этой проблемой можно получив значения из "вычисленного" или "текущего" стиля элемента. В соответствии с названиями, это будет метод getComputedStyle (Mozilla, Opera, ...) и объект currentStyle (IE).

В w3c-совместимых браузерах у объекта window доступен метод getComputedStyle, который принимает два параметра - сам элемент, для которого мы хотим получить стили, и "псевдо-элемент". Второй параметр обязательный и для простых элементов должен быть пустой строкой, или null. А вообще, этот параметр работает по аналогии с "псевдо-классами" в CSS, т.е. может принимать параметры "hover", "visited" и подобные.

Этот метод возвращает объект CSSStyleDeclaration, другими словами, объект такого же типа, как и [element].style, но в данном случае мы не можем менять свойства этого объекта, а только получать их значения.

Вот переделанный пример, подобный тому, что был представлен выше:

<style type="text/css">
    .someClass {
        margin-left: 15px;
    }
</style>
<div onclick="alert(window.getComputedStyle(this, null).marginLeft)" class="someClass">Нажми на меня</div>

Пример "в живую":

Нажми на меня

Этот пример, конечно же, не будет работать в браузерах Internet Explorer.

Стоит отдельно отметить, что getComputedStyle возвращает действительные значения стилей, которые будут изменяться в соответствии с изменением свойств объекта [element].style.

Если в вашей задаче необходимо узнать значения вычисленных свойств "по-умолчанию", то вы можете вызвать метод getComputedStyle у объекта document.defaultView с теми же параметрами, в результате получив вычисленные значения свойств строго указанных в CSS, или inline-стиле элемента.

В браузерах семейства Internet Explorer проблема доступа к действительным значениям стилей решена более просто и топорно.

Каждый элемент имеет свойство [element].currentStyle, которое представляет собой подобие объекта [element].style, но содержит текущие стили этого элемента. Как и в случае getComputedStyle, менять эти свойства - нельзя.

В отличие от getComputedStyle, мы не можем получить значения стилей "по-умолчанию" в браузере Internet Explorer, и кроме этого, не можем получить значения стилей для "псевдо-элементов".

Обратившись к предыдущим примерам, перепишем их для Internet Explorer:

<style type="text/css">
    .someClass {
        margin-left: 15px;
    }
</style>
<div onclick="alert(this.currentStyle.marginLeft)" class="someClass">Нажми на меня</div>

Пример "в живую":

Нажми на меня

Стоит отметить, что с помощью 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";

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

Об этой проблеме уже упоминалось в статье про базовые типы JavaScript.

Такие погрешности измерений, конечно же, редко когда "всплывают", но такие случаи, к сожалению, случаются. Самый распространенный случай, когда вы создаете цикл, на подобии:

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 находится в прямой зависимости от progress:

Математическая формула:
delta = progress

График функции:

JavaScript функция delta:

function delta(progress) {
    return progress;
}

Пример (кликните для анимации):

Это тоже простой случай, где результат delta будет равен progress, возведенному в нужную степень.

В качестве частных случаев степенной функции рассматривают квадратичные, кубические функции и т.д.

Математическая формула:
delta = progressn
Где n - показатель степени.

График функции для квадратичной функции:

JavaScript функция delta для квадратичной функции:

function delta(progress) {
    return Math.pow(progress, 2);
}

Пример для квадратичной функции (кликните для анимации):

Изменение степени меняет время нарастания скорости, например для показателя степени 5 график будет следующий:

А пример будет выглядеть так (кликните для анимации):

Математическая формула:
delta = 1 - sin(arccos(progress))

График функции:

JavaScript функция delta:

function delta(progress) {
    return 1 - Math.sin(Math.acos(progress));
}

Пример (кликните для анимации):

Математическая формула:
delta = 1 - sin((1 - progress) * П/2)

График функции:

JavaScript функция delta:

function delta(progress) {
    return 1 - Math.sin((1 - progress) * Math.PI/2);
}

Пример (кликните для анимации):

К сожалению, этот пример не вносит ясность, для чего используется эта формула, потому что в нем вычисляется синус по возрастающей, а значение достигает максимума. Если бы движение не было ограничено достижением верхней точки, то блок возвращался к началу и продолжал движение туда-сюда по синусоидальному закону.

Эта функция изменяет параметр по типу "лука", т.е. мы сначала "отводим тетиву", а потом "стреляем". В отличие от других функций, она зависит от двух параметров.

Математическая формула:
delta = progress2 * ((x + 1) * progress - x)
Где x - коэффициент, через который высчитывается расстояние, на которое мы "отводим" параметр.

График функции (с x = 1.5):

JavaScript функция delta:

function delta(progress, x) {
    return Math.pow(progress, 2) * ((x + 1) * progress - x);
}

Пример c x = 1.5 (кликните для анимации):

Представьте себе, что мы отпускаем мячик и он падает на пол, после чего несколько раз подскакивает и останавливается. Эта функция описывает изменение параметра в точности наоборот - параметр будет "подскакивать", после чего достигнет высшей точки.

Эта функция несколько сложнее приведенных выше, и математической функции (как и графика) для нее я привести не могу, (потому что не знаю, как такая конструкция будет описываться в математике). Если кто-то сможет мне в этом помочь, отпишите в комментариях, буду очень благодарен.

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 - коэффициент "инетрности", другими словами, на сколько значение будет колебаться в начале движения.

График функции:

JavaScript функция delta:

function delta(progress, x) {
    return Math.pow(2,10 * (progress - 1)) * Math.cos(20 * progress * Math.PI * x / 3);
}

Пример (кликните для анимации):

В отличие от других примеров, в этом время анимации было заменено на 2 секунды, иначе она происходит слишком прерывисто.

Для того, чтобы было понятно, о чем идет разговор, будем оперировать понятиями сходными с параметрами 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 анимации лучше увеличивать в полтора раза, по сравнению с эффектами по отдельности для начала, или конца анимации.

JavaScript функция delta:

function delta(progress, x) {
    function d(progress, x) { 
        return Math.pow(2,10 * (progress - 1)) * Math.cos(20 * progress * Math.PI * x / 3);
    }

    if (progress < .5)
        return d(2 * progress, x) / 2;
    else
        return (2 - d(2 * (1 - progress), x)) / 2;
}

Где и как применять программные анимации

Поскольку "анимировать" можно абсолютно любой параметр, то сфера применения программной анимации ограничена лишь вашей фантазией.

Вместо координаты (как в примерах в этой записи), вы можете изменять параметр прозрачности, высоту или ширину элемента и даже цвет.

Вот, например, блок, который изменяет свой цвет по закону формулы Bounce с easeOut:

Кликни меня, я стану зеленым!

Или вы можете вообще создать какие-нибудь замысловатые анимации, например ввода текста:

Кроме того, вы можете применять такие анимации в элементах canvas, или при отображении vml/svg, в общем - везде, где это будет к месту.

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

Это, скорее всего, не последняя запись на тему анимации. Когда будет (свободного не бывает) время, напишу про параллельные анимации и перемещение по траектории.

Прошу о всех ошибках/замечаниях (в т.ч. по части орфографии и пунктуации) писать в комментарии, кроме того, интересно было бы услышать идеи и предложения.

+63

Автор: hogart, дата: 4 февраля, 2009 - 11:22
#permalink

Отличный пост, спасибо большое. Побольше бы таких.


Автор: Yojik, дата: 4 февраля, 2009 - 14:15
#permalink

Потрясающая статья. Спасибо!


Автор: Илья Кантор, дата: 4 февраля, 2009 - 16:40
#permalink

Добавил статью на главную сайта.


Автор: Гость (не зарегистрирован), дата: 4 февраля, 2009 - 18:01
#permalink

Стоит упомянуть, что в методе currentStyle используются camelStyle-имена CSS-свойств, а в getComputedStyle - синтаксис, принятый в CSS.

И currentStyle не возвращает значения свойст, имеющих составные значения, например нельзя получить заначение background-position, нужно отдельно получать background-position-x и background-position-y.


Автор: Андрей Параничев, дата: 4 февраля, 2009 - 18:27
#permalink

На счет currentStyle и композитных свойств - спасибо, сейчас внесу это в статью.

А на счет camelStyle - в w3c-совместимых можно получать значения свойств двумя способами: либо через свойства (с синтаксисом camelStyle), либо вызывая метод getPropertyValue - где имя свойства указывается в синтаксисе CSS. Причем это не зависит от того, работаем мы с объектом [element].style, или с window.getComputedStyle(). Для совместимости с currentStyle лучше использовать "общий" синтаксис, а именно - camelStyle.


Автор: korzhik, дата: 7 февраля, 2009 - 17:39
#permalink

Афигеть, дайте две!
Спасибо.


Автор: Dmitry1 (не зарегистрирован), дата: 7 февраля, 2009 - 18:50
#permalink

Класс! )

> Изменение степени меняет время нарастания скорости, например для показателя
> степени 5 график будет следующий:

А как сделать постепенное замедление? Указать степень -5?


Автор: Андрей Параничев, дата: 7 февраля, 2009 - 20:38
#permalink

Нет, достаточно "обратить" функцию (easeOut).


Автор: Гость (не зарегистрирован), дата: 20 апреля, 2009 - 20:09
#permalink

не могу установить джава скрипт,какой то сбой произошел,в интернете не работают многие функции.


Автор: Dmitry A. Soshnikov, дата: 8 февраля, 2009 - 15:02
#permalink

Отличная статья! Доступно и грамотно. Молодец, Андрей!


Автор: Илья Кантор, дата: 13 февраля, 2009 - 23:51
#permalink

Прочитал статью повнимательнее - она еще интереснее, чем я думал. Спасибо, Андрей!


Автор: Андрей Параничев, дата: 14 февраля, 2009 - 16:05
#permalink

У меня не хватило времени, чтоб написать так, как я хотел.
Я хочу еще парочку записей по этой теме сделать, а потом можно будет скомпоновать в одну-две полноценных статей и выложить в основные материалы сайта.


Автор: Андрюша (не зарегистрирован), дата: 18 апреля, 2009 - 05:24
#permalink

Результат вычислений (result) нужно присваивать свойству стиля элемента в том случае, если анимация не закончилась. Если анимация закончилась, присваеваем конечные значения явно, т.к. последнее значение progress не будет равно 1, а во многих случаях тончость результата важна.

За статью огромное спасибо.


Автор: Андрюша (не зарегистрирован), дата: 18 апреля, 2009 - 05:29
#permalink

Примите мои извинения, Андрей, нашел вашу заметку к коду...


Автор: Гость (не зарегистрирован), дата: 25 апреля, 2009 - 17:18
#permalink

классная статья


Автор: Ада (не зарегистрирован), дата: 26 мая, 2009 - 21:02
#permalink

Статья просто супер, очень помогла при подготовке магистерской дипломной работы !!!!!!!!!!!!!!!!!!!!!


Автор: крок (не зарегистрирован), дата: 27 мая, 2009 - 09:47
#permalink

клас.
вопрос: вот Вы приводите график функции математического закона : синус, линейная и т.д. и соответсвенно картинки для них. Они гиф.

а как вот их строить явой ? чтоб и в опере работало и в ие ?
(да, есть свг, и флэш, а если без них ?).


Автор: Гость (не зарегистрирован), дата: 29 мая, 2009 - 09:29
#permalink

svg+vml => opera+ff+ie


Автор: rank1 (не зарегистрирован), дата: 31 июля, 2009 - 14:35
#permalink

Андрей, спасибо Вам огромное за статью, я сейчас делаю HTML презентацию и наткнулся на вашу статью во время поисков эффектов для всплывания текста.
Моя презентация целиком построена на основе amcharts.com и ammap.com и как раз не хватало эффектов баунсинга для обычных текстовых сообщений.
Я не большой спец по джаваскрипту, но на основе статьи сварганил скрипт на VBS для вываливания нескольких сообщений сверху экрана (). Ваша статья - это как раз то что нужно - супер просто.


Автор: Агорес (не зарегистрирован), дата: 13 августа, 2009 - 14:42
#permalink

Спасибо за статью.

Вот только ф-цию "CIRC" можна зделать проще (сэкономит потом драгоценные ресурсы)

delta = 1 - sqrt(1-progress*progress);

Кому интересно почему может посмотреть в учебник по тригонометрии


Автор: Gozar, дата: 24 октября, 2009 - 20:03
#permalink

Функция Elastic

Математическая формула:
delta = 2(10 * (p - 1)) * cos(20 * progress * П * x / 3)
Где x - коэффициент "инетрности"

реверсная опечатка: коэффициент "инертности"


Автор: Meromax (не зарегистрирован), дата: 11 ноября, 2009 - 09:36
#permalink

Спасибо большое! Полезная статья...
Иногда лучше чистый Java Script вместо jquery если чтобы не тормозило.


Автор: Гость (не зарегистрирован), дата: 16 декабря, 2009 - 23:26
#permalink

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;
}

работает


Автор: Гость (не зарегистрирован), дата: 6 января, 2010 - 19:09
#permalink

И что теперь математику учить
Действительно, очень полезная статья, только если бы вы дали исходники, было бы намного проще новичкам понять что к чему Sad


Автор: Гость (не зарегистрирован), дата: 23 января, 2010 - 05:16
#permalink

как изменить такое представление?

<div id="element" 
onclick="
animate('div',  	function d(p) { return Math.pow(progress, 2);},  1000)
">
<div class="a" id="div"></div>
</div>

таким образом не выходит:

document.getElementById('element').onclick = function() {};

Автор: Мараторий, дата: 24 января, 2010 - 07:31
#permalink

Супер статья, спасибо огромное.
Единственное дополнение. В приведенном примере 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;
}

Вероятно можно упростить код. Буду рад увидеть улучшенные варианты.


Автор: wlad (не зарегистрирован), дата: 19 февраля, 2010 - 14:14
#permalink

Андрей!

У меня проблемы вот на этой странице:

element.style.left = result + "px";

Идет высветка "требуется объект"

С уважением

Влад


Автор: wlad (не зарегистрирован), дата: 19 февраля, 2010 - 14:14
#permalink

Андрей!

У меня проблемы вот на этой странице:

element.style.left = result + "px";

Идет высветка "требуется объект"

С уважением

Влад


Автор: Гость (не зарегистрирован), дата: 10 мая, 2010 - 02:33
#permalink

у меня была такая же проблема, вылечил так:
element.style.left = Math.round(result) + "px";


Автор: Гость (не зарегистрирован), дата: 4 марта, 2010 - 19:17
#permalink

К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2

&pi;

t/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.


Автор: Гость (не зарегистрирован), дата: 4 марта, 2010 - 19:20
#permalink

К вопросу о функции Bounce. Это оно: y=Kt*[1+sin(2πt/T)], где К - наклон огибающей максимумов (в данном случае - прямая), Т - длительность одного скачка, t - время? Вместо прямой можно использовать любую другую кривую для ограничения высоты подскока.


Автор: Гость (не зарегистрирован), дата: 22 марта, 2010 - 23:12
#permalink

А не судьба для каждой строки выложить код? Или хотя бы для первой анимации, а то хз куда начинающему программисту на Java всовывать для каждой новой анимации разную функцию.


Автор: Гость (не зарегистрирован), дата: 16 марта, 2012 - 12:08
#permalink

здесь примеры рабочие, можешь в файербаге их просмотреть.


Автор: kartg (не зарегистрирован), дата: 13 апреля, 2010 - 19:51
#permalink

Спасибо!


Автор: Shevan, дата: 22 июня, 2010 - 03:13
#permalink

Доброго времени суток, Увожаемые. Спасибо огромное за статью. Но у меня возник вопрос:
Если анимацию вызывать по событию onmouseover, как ее остановить событием onmouseout?
Спасибо.
Я новичек в JS, прошу сильно не бить )


Автор: kibal4iw, дата: 26 июля, 2010 - 17:46
#permalink

Напиши обработчик который возвращает назад произведенные действия.


Автор: Гость (не зарегистрирован), дата: 14 июля, 2010 - 00:42
#permalink

Скиньте пожалуйста исходник для этой статьи, очень поможете.


Автор: kibal4iw, дата: 22 июля, 2010 - 17:09
#permalink

Статья просто суппер! Давно искал подобный урок! Спасибо огромное.


Автор: RE_, дата: 26 июля, 2010 - 09:00
#permalink

Дополнение: метод .currentStyle работает еще и в Опере


Автор: B@rmaley.e><e, дата: 26 июля, 2010 - 18:22
#permalink

<зануда>Только это не метод, а свойство.</зануда>


Автор: WyrazZz (не зарегистрирован), дата: 12 августа, 2010 - 15:36
#permalink

А если меня аткая ситуация. При клике выполняется функция которая изменяет значение magin-left, как сделать так чтобы при следующем клике изменялось уже изменное значение. Ну допустим при первом клике мы изменили значение на 50px, на втором тоже меняем на 50, но добавляя его к прошлому, тоетсь получится 100


Автор: GreatRash, дата: 17 августа, 2010 - 12:45
#permalink

Нашел вот такую формулу для EaseInOut:

function delta(progress) {
    return Math.pow(progress, 2) * (3 - 2 * progress);
}

Автор: mycoding, дата: 25 октября, 2010 - 19:23
#permalink

Супер пост.
ЕГо надо на главную страницу, или в статьи как минимум добавить.


Автор: Гость (не зарегистрирован), дата: 30 октября, 2010 - 14:23
#permalink

а не подскажите как сделать одновременную анимация. Например при клике на кнопке должно двигаться 2 или 3 блока.


Автор: poorking, дата: 7 декабря, 2010 - 15:40
#permalink

getCompudedStyle может вычислить стиль элемента, даже если он не задан в CSS, например высоту блочного элемента, которая меняется от количества содержимого(кроме IE, он в таком случае своим currentStyle дает auto). Но, узнал сегодня что ее можно "снять" этим методом, только если он уже добавлен на страницу. Столкнулся с этим когда пытался записать высоту блока в его свойство, сразу после создания и добавления контента. Возвращало NAN. А когда делал это уже после того как добавил элемент на страницу, все было правильно.


Автор: Epilepsy Hazard, дата: 12 декабря, 2010 - 19:14
#permalink

Вот и пригодилась математика.


Автор: Илья Черёмушкин (не зарегистрирован), дата: 29 января, 2011 - 12:00
#permalink

Объясните пожалуйста, как вывели эту формулу:

result = (to - from) * delta(progress) + from

И, предположим, у меня есть график прямой y = kx + b. Каким образом, по какой формуле можно из этой функции найти delta?


Автор: Гость (не зарегистрирован), дата: 2 марта, 2011 - 12:21
#permalink

супер, сппасибо


Автор: Рустик-Кустик (не зарегистрирован), дата: 23 марта, 2011 - 15:17
#permalink

Класс


Автор: Гость (не зарегистрирован), дата: 19 апреля, 2011 - 20:29
#permalink

компьютерная графика - очень интересно!


Автор: kazimira6, дата: 23 июля, 2011 - 16:27
#permalink

Я в восторге! Столько материала, и так грамотно изложить. Спасибо, Андрею!!!


Автор: Виктор Кон, дата: 25 сентября, 2011 - 22:25
#permalink

А я присваиваю стилям значения переменных без "px" и у меня все работает. Кроме того, я не беру у объектов текущие значения стилей, а только присваиваю. Вся анимация и математика делается только на переменных. Это намного проще, но проблема в том, что переменные должны быть глобальными и не пересекаться с другими анимациями. Это легко решить если все переменные буквами а в разных анимациях в конце приписываются разные числа. Мог бы дать ссылку на сайт, но не умею. Вот ссылка slideshow


Автор: LegGnom, дата: 26 сентября, 2011 - 14:57
#permalink

Подскажите плиз.

Анимация не выполняется, багер выдает (Ошибка при анализе значения «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: сильно не бейте)


Автор: stepennwolf, дата: 11 марта, 2012 - 21:33
#permalink

Пропиши вместо "margin-left" marginLeft


Автор: Константин Артамонов (не зарегистрирован), дата: 23 октября, 2011 - 18:42
#permalink

Предлагаю модифицировать этот фрагмент:

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


Автор: breakitnow, дата: 23 декабря, 2011 - 09:00
#permalink

Подскажите пожалуйста, как модифицировать код Bounce, чтобы отскоков было не 4, а скажем 3 или 2?


Автор: Гость (не зарегистрирован), дата: 13 марта, 2012 - 20:14
#permalink

Cтатья интересна, познавательна. Радует еще и то, что в элементе Канвас ХТМЛ5 с помощью яваскрипта можно выделывать все что угодно. Геометрию визуализировать. За канвасом будущее.


Автор: K313, дата: 15 апреля, 2012 - 16:26
#permalink

Статья хорошая,...
а кто математику в школе учил, тот знает, что график функции - это много точек на плоскости с коорлинатами Х и У, где Х - это значение передаваемое функции (в данном случае - progress), а У - это возвращённый ею результат.


Автор: Гость (не зарегистрирован), дата: 11 мая, 2012 - 19:55
#permalink

Подскажите кто то ,как связать одно с другим. А то я новичок ,и пока кое что не понимаю.
Меня интересует как прикрепить код Javascript к div ,чтобы произошло движение самой анимации.А то я уже куча сайтов облазил,и как то не получается.


Автор: Гость (не зарегистрирован), дата: 17 августа, 2012 - 09:43
#permalink

Помогите пожалуйста разобраться:
У меня всё та же проблема с подсчетом:

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+"&euro;";
    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> – Цена: &euro; "+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+'шт. (&euro; '+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>Цена заказа: &euro; "+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>Общая сумма: &euro; "+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);
}

Автор: Gray (не зарегистрирован), дата: 23 августа, 2012 - 16:02
#permalink

Круто, разобрав алгоритм почувствовал просветление =)


Автор: Гость (не зарегистрирован), дата: 10 декабря, 2012 - 16:12
#permalink

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

К сожалению, именно этот момент часто "провисает" у тех людей, которые занимаются исключительно веб-программированием. Многие забыли школьную математику, а другие до сих пор боятся слова "синус". В этом разделе я постараюсь привести все популярные формулы, которые используются в программной анимации.


Автор: Гость (не зарегистрирован), дата: 29 января, 2013 - 12:13
#permalink

Дуже велике дякую!)


Автор: Andreyya (не зарегистрирован), дата: 15 сентября, 2014 - 22:12
#permalink

Не работают примеры. Браузер Opera 24.0


Автор: Tecvid, дата: 25 ноября, 2014 - 22:47
#permalink

доброго времени суток

Андрей, я написал вот такой код на основе вашего кода:

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, поэтому подскажите пожалуйста как правильно поступить


Автор: Гость (не зарегистрирован), дата: 3 июля, 2015 - 19:28
#permalink

В Google chrome не работает


Автор: Гость (не зарегистрирован), дата: 2 декабря, 2017 - 16:53
#permalink

статья - класс! но есть вопрос: функция delta(progress)<=1 что б анимация работала правильно? а то у меня с анимацией фиг знает что делается когда именно дельта больше 1. а ещё, если delta(progress)<1 при progress=1 то анимация не полностью выполняется =( где ошибка?


Автор: Гость (не зарегистрирован), дата: 9 мая, 2018 - 09:10
#permalink

"инетрности",


Автор: Гость (не зарегистрирован), дата: 9 мая, 2018 - 09:11
#permalink

у меня не работает ни один клик кроме зеленеющей полоски и движ текста


Автор: Гость (не зарегистрирован), дата: 13 апреля, 2022 - 02:42
#permalink

Автор: Гость (не зарегистрирован), дата: 16 апреля, 2022 - 14:23
#permalink

Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
7 + 9 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
Андрей Параничев
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние комментарии
Последние темы на форуме
Forum