Формула легкости и плавности анимации

Скажем, если я делаю Ease-Out, а затем Анимация Ease-In движения объекта от координаты X1 до координаты X2 в течение S шагов через равные промежутки времени. Кто-нибудь может предложить формулу для расчета координат X этого движения?


person ahmd0    schedule 19.11.2012    source источник
comment
Ознакомьтесь с robertpenner.com/easing, в частности с исходным кодом сценария действий 2.0. Из этого вы сможете преобразовать его в С#.   -  person Matthew    schedule 20.11.2012


Ответы (6)


Квадратичное облегчение, где:

t = текущее время
b = начальное значение
c = изменение значения
d = продолжительность

 function (float time, float startValue, float change, float duration) {
     time /= duration / 2;
     if (time < 1)  {
          return change / 2 * time * time + startValue;
     }

     time--;
     return -change / 2 * (time * (time - 2) - 1) + startValue;
 };

источник: http://gizma.com/easing/

person Toad    schedule 19.11.2012
comment
Жаба, когда вы говорите t = time, вы имеете в виду время от начала анимации или время от предыдущего кадра? - person Sir; 23.10.2015
comment
t идет от 0 до 1, где 0 — начало анимации, а 1 — конец. Для каждого ключевого кадра вы должны изменить значения и снова позволить t измениться с 0 на 1. - person Toad; 23.10.2015
comment
Каково изменение стоимости? Я не понимаю, откуда это. - person starbeamrainbowlabs; 20.11.2015
comment
Сначала вы используете формулу для перехода от ключевого кадра 1 к ключевому кадру 2. (Так что b — это значение ключевого кадра 1, а с — значение ключевого кадра 2). Затем вы отпускаете t от 0,0 до 1,0. К тому времени, когда вы достигнете 1.0, вы повторите эти шаги, только теперь вы используете ключевой кадр 2 и ключевой кадр 3. - person Toad; 25.11.2015
comment
Этот ответ должен получить 1 миллион лайков. Просмотрел 10 источников, нашел одну и ту же формулу, но ни в одном из них не упоминалось, что за хрень t, b, c и d где. Спасибо чувак - person Milad.Nozari; 15.02.2016
comment
хорошо, я вроде понимаю, но что вы делаете с возвращаемым значением? - person Myke Dev; 19.04.2017
comment
скажем, вы уменьшили значение b=8 до c=17. Затем, учитывая текущее t (время) по отношению к общей продолжительности (d), он возвращает скорректированное значение, которое облегчает движение, а не интерполирует линейно. - person Toad; 21.04.2017
comment
Скажем, начальное значение = 3, и вы хотите уменьшить значение до 5. Тогда изменение значения равно 2. Таким образом, изменение значения является конечным значением - начальным значением. - person Toad; 12.11.2017
comment
Я был сбит с толку, потому что думал, что возвращаемое значение зависит от того, насколько должно измениться начальное значение (на котором основано значение startValue), но вместо этого это то, чем должно стать это начальное значение. обратная смена меня сбила с толку. - person TimSim; 19.12.2017

Лично я бы предпочел использовать функцию, которая получает время в [0; 1] и вывести значение в [0; 1], так что мы можем применить результат к любому типу (2D-вектор, 3D-вектор, ...).

Решение 1

Для квадратичного увеличения/уменьшения кривой кривая разделяется на две отдельные функции в зависимости от значения t:

  • когда t ‹= 0,5: f(x) = 2 * x * x с x в [0;0,5] (график)
  • когда t > 0,5: f(x) = 2 * x * (1 - x) + 0.5 с x в [0;0,5] (график)

Вот графики:

«График
график - часть 2

Поскольку вторая функция тоже находится в [0;0,5], но t > 0,5, когда мы начинаем ее использовать, нам нужно уменьшить t на 0,5.

Это результат, в C:

float InOutQuadBlend(float t)
{
    if(t <= 0.5f)
        return 2.0f * t * t;
    t -= 0.5f;
    return 2.0f * t * (1.0f - t) + 0.5f;
}

Решение 2 (Безье)

Еще одна интересная кривая смешения — это кривая Bézier, которая имеет то преимущество, что вполне оптимизирован (нет, если). Вот кривая от Вольфрам:

Кривая Безье

И вот код C:

float BezierBlend(float t)
{
    return t * t * (3.0f - 2.0f * t);
}

Решение 3 (параметрическая функция)

Другой метод, предложенный @DannyYaroslavski, — это простая формула, предложенная здесь.

Он параметрический и имеет хорошее ускорение и замедление входа/выхода.

С альфа = 2 вы получаете эту функцию:

curve

Что переводится на C следующим образом:

float ParametricBlend(float t)
{
    float sqt = t * t;
    return sqt / (2.0f * (sqt - t) + 1.0f);
}

Изменить 1: добавить решение 3 от @DannyYaroslavski
Изменить 2: лучшее объяснение решения 1
Изменить 3: добавить графики в все решения

person Creak    schedule 08.09.2014
comment
"quite optimized (no if)" Ты издеваешься? Знаете ли вы, насколько функция квадратного корня медленнее, чем простая if? - person ahmd0; 12.09.2014
comment
Вот что я сказал: sqr != sqrt ;) - person Creak; 13.09.2014
comment
В функции InOutQuadBlend есть некоторая ошибка, особенно во втором возврате. Например, при t=1 последние две строки будут оцениваться как 2*(.5)*(1-.5) = .5, а не ожидаемая 1. Я нашел формулу, показанную в math.stackexchange.com/a/121755 делает то, что пытается сделать Creak. - person Danny Yaroslavski; 07.07.2015
comment
Вы правы, @DannyYaroslavski, я изменил формулу, чтобы исправить это. - person Creak; 12.07.2015
comment
В последней функции я полагаю, что X на самом деле является параметром T функции, верно? - person ColdSteel; 17.05.2017
comment
Вторая часть исходной формулы в решении 1 кажется неправильной. Я этого не понимаю, и Wolfram показывает для этого совершенно неверный график. Версия кода C верна, но я также не могу следовать ей. Я пытаюсь изменить это на кубическую функцию и борюсь. - person ygoe; 27.12.2019
comment
Вы правы @ygoe, я исправил формулу и объяснение. Спасибо! - person Creak; 29.12.2019

Во всех вышеперечисленных решениях отсутствуют примеры использования.

Нашел хорошее решение здесь:

 function animate({timing, draw, duration}) {

  let start = performance.now();

  requestAnimationFrame(function animate(time) {
    // timeFraction goes from 0 to 1
    let timeFraction = (time - start) / duration;
    if (timeFraction > 1) timeFraction = 1;

    // calculate the current animation state
    let progress = timing(timeFraction)

    draw(progress); // draw it

    if (timeFraction < 1) {
      requestAnimationFrame(animate);
    }

  });
}

Пример использования:

animate({
  duration: 1000,
  timing(timeFraction) { // here you can put other functions
    return timeFraction;
  },
  draw(progress) {
    elem.style.width = progress * 100 + '%';
  }
});

Другая функция:

function quad(timeFraction) {
  return Math.pow(timeFraction, 2)
}

Подробнее здесь

person Alexander Poshtaruk    schedule 27.09.2019

У меня такая же проблема: я хотел оживить свою диаграмму (Ease in-out).

Brainstorm дал мне два пути:

1) Тригонометрическая формула. Во-первых, я написал y=(sin(x/π*10-π/2)+1)/2, какой аналог sin^2((5*x)/π)

float TrygoEase (float x) {
    float y=(float)Math.pow(Math.sin(5*x/Math.PI),2);
    return y;
}

2) Две параболы. Это было несложно. Я только что использовал y=2*x*x на [0;0.5] и y=-2(x-1)^2+1 на [0.5;1].

float ParabolEase(float x) {
    float y=2*x*x;
    if(x>0.5f){
        x-=1;
        y=-2*x*x+1;
    }
    return y;
} 

Используйте этот способ для x=[0;1], что также возвращает y=[0;1].

Теперь Вы можете сравнить эти графики:

введите здесь описание изображения

person Egor Randomize    schedule 22.06.2018

Вот версия с кривизной в качестве аргумента, следующая этому общему решению на который ссылается Creak.

/*
* applyCurve: apply an S-curve to an input value.
* The highest positive curvature will result in a step from 0 to 1,
* the most negative curvature will result in a constant of 0.5.
*
* progress: the input value between 0 and 1,
* curvature: the amount of curvature between -1 and 1.
*  Negative values curve the other way, 0 applies no curvature.
*/

double applyCurve(double progress, double curvature) {
    assert(progress >= 0.0 && progress <= 1.0);
    assert(curvature >= -1.0 && curvature <= 1.0);

    if (curvature >= 0.0) {
        if (curvature > 0.99999) return progress > 0.5 ? 1.0 : 0.0;

        float exp = 1.0 / (1.0 - curvature); // find s-curve exponent
        return pow(progress, exp) / (pow(progress, exp) + pow(1.0 - progress, exp)); // apply s-curve
    } else {
        if (curvature < -0.99999) return 0.5;

        float exp = 1.0 + curvature; // find s-curve exponent
        return pow(progress, exp) / (pow(progress, exp) + pow(1.0 - progress, exp)); // apply s-curve
    }
}
person Mattijs    schedule 12.05.2020

Эта версия позволяет использовать любые функции замедления и замедления (EaseIn и EaseOut). Обе функции должны принимать параметр значения времени от 0 до 1 и возвращать уменьшенное значение времени от 0 до 1.

float EaseInOut(float t)
{
    if (t <= 0.5f)
    {
        return EaseIn(t * 2) * 0.5f;
    }
    else
    {
        t -= 0.5f;
        return (EaseOut(t * 2) * 0.5f) + 0.5f;
    }
}
person ImmortalMazeWalker    schedule 30.04.2021