假设我正在对一个对象的运动从X1坐标到X2坐标进行缓出(Ease-Out)和缓入(Ease-In)动画,需要在S步骤中以相等的时间间隔完成。有人能提供计算该运动X坐标的公式吗?
t
的值分为两个不同的函数:t
的值大于0.5,因此我们需要将t
减去0.5。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;
}
另一个有趣的混合曲线是由贝塞尔曲线提供的,它具有相当优化的优势(无需条件判断)。以下是来自Wolfram的曲线:
这是C代码:float BezierBlend(float t)
{
return t * t * (3.0f - 2.0f * t);
}
另一种方法是由@DannyYaroslavski提出的简单公式,可以在这里找到链接。
它是参数化的,并且具有良好的加速和减速效果。
当alpha = 2时,您将得到以下函数:
这在C语言中的翻译如下:float ParametricBlend(float t)
{
float sqt = t * t;
return sqt / (2.0f * (sqt - t) + 1.0f);
}
二次缓出动画方程:
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;
};
source: http://gizma.com/easing/
以上所有解决方案都缺乏使用示例。
在此处找到了一个很好的解决方案(链接):
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)。
我进行了头脑风暴,找到了两种方法:
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) 两个抛物线。这并不难。我只是在[0;0.5]
上使用了y=2*x*x
,在[0.5;1]
上使用了y=-2(x-1)^2+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]
。
现在您可以比较这些图表:
此版本允许您使用任何缓入和缓出函数(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;
}
}
这里有一个版本,其中曲率量作为参数,遵循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
}
}