Скажем, если я делаю Ease-Out, а затем Анимация Ease-In движения объекта от координаты X1 до координаты X2 в течение S шагов через равные промежутки времени. Кто-нибудь может предложить формулу для расчета координат X этого движения?
Формула легкости и плавности анимации
Ответы (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/
t = time
, вы имеете в виду время от начала анимации или время от предыдущего кадра?
- person Sir; 23.10.2015
Лично я бы предпочел использовать функцию, которая получает время в [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] (график)
Вот графики:
Поскольку вторая функция тоже находится в [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 вы получаете эту функцию:
Что переводится на C следующим образом:
float ParametricBlend(float t)
{
float sqt = t * t;
return sqt / (2.0f * (sqt - t) + 1.0f);
}
Изменить 1: добавить решение 3 от @DannyYaroslavski
Изменить 2: лучшее объяснение решения 1
Изменить 3: добавить графики в все решения
"quite optimized (no if)"
Ты издеваешься? Знаете ли вы, насколько функция квадратного корня медленнее, чем простая if
?
- person ahmd0; 12.09.2014
Во всех вышеперечисленных решениях отсутствуют примеры использования.
Нашел хорошее решение здесь:
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)
}
Подробнее здесь
У меня такая же проблема: я хотел оживить свою диаграмму (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]
.
Теперь Вы можете сравнить эти графики:
Вот версия с кривизной в качестве аргумента, следующая этому общему решению на который ссылается 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
}
}
Эта версия позволяет использовать любые функции замедления и замедления (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;
}
}