我正在尝试在代码中模拟动画效果(几乎任何语言都可以,因为它似乎是数学而不是语言)。从本质上讲,它是质量弹簧系统的仿真。我一直在关注WPF / Silverlight的ElasticEase
,这看起来非常接近我正在寻找的,但并不完全。
首先,这就是我正在寻找的东西 - 一个物体,行进一定的秒数,撞到一个位置并立即减速到ocsillate一定的秒数,停留在应用阻尼的同一点。所以为了想象这个,让我说我有一个600w / 900h的画布,我有一个正方形,开始在TranslateTransform.Y
中从900px动画到150px。它需要4秒才能达到150px的高度(187.5px每秒),在这个阶段,它立即受到阻尼,仅在0.4秒(87.5px每秒)到115px高度后再行进约35px,然后反弹1秒到163px高度(48px和48px每秒),然后回升到146px(17px和17px每秒),依此类推,直到ocillations减慢到最后150px的休息位置。 ocirlation周期为16秒。
我上面描述的例子是左上角的蓝色矩形:
这是我事先知道的 - 像素距离和从A点到B点所需的秒数,即ocirlation的秒数。像质量这样的东西似乎并不重要。
我已经尝试了ElasticEase
,问题似乎是我无法让对象在没有缓和4秒的情况下旅行,然后在接下来的16秒内“反弹”。 .Springiness
总是太过分了,即使我把它设置为像20这样的高数字。
ILSpy show的功能如下:
protected override double EaseInCore(double normalizedTime)
{
double num = Math.Max(0.0, (double)this.Oscillations);
double num2 = Math.Max(0.0, this.Springiness);
double num3;
if (DoubleUtil.IsZero(num2))
{
num3 = normalizedTime;
}
else
{
num3 = (Math.Exp(num2 * normalizedTime) - 1.0) / (Math.Exp(num2) - 1.0);
}
return num3 * Math.Sin((6.2831853071795862 * num + 1.5707963267948966) * normalizedTime);
}
我在DropBox的压缩文件夹中添加了2个视频和一个Excel文件。我想这个问题将更多的是正在进行的工作,因为人们会提出更多澄清问题。
(免责声明:当涉及到大部分内容时,我不知道我在说什么)
答案 0 :(得分:9)
略过物理学,然后直接进入等式。
参数: “这是我事先知道的 - 像素距离[D]和从点A到点B的秒数[T0],振荡的秒数[T1]。”此外,我还要添加作为自由参数:振荡的最大大小,Amax,阻尼时间常数,Tc和帧速率Rf,即,在什么时候需要新的位置值。我假设你不想永远计算这个,所以我只做10秒Ttotal,但是有各种合理的停止条件......
码:
这是代码(在Python中)。主要的是方程式,可在def Y(t)
中找到:
from numpy import pi, arange, sin, exp
Ystart, D = 900., 900.-150. # all time units in seconds, distance in pixels, Rf in frames/second
T0, T1, Tc, Amax, Rf, Ttotal = 5., 2., 2., 90., 30., 10.
A0 = Amax*(D/T0)*(4./(900-150)) # basically a momentum... scales the size of the oscillation with the speed
def Y(t):
if t<T0: # linear part
y = Ystart-(D/T0)*t
else: # decaying oscillations
y = Ystart-D-A0*sin((2*pi/T1)*(t-T0))*exp(-abs(T0-t)/Tc)
return y
y_result = []
for t in arange(0, Ttotal, 1./Rf): # or one could do "for i in range(int(Ttotal*Rf))" to stick with ints
y = Y(t)
y_result.append(y)
这个想法是线性运动直到这一点,然后是衰减的振荡。振荡由sin
和衰减提供,乘以exp
。当然,更改参数以获得您想要的任何距离,振动大小等。
注释:
冒这么长的风险,我意识到我可以在GIMP中制作一个gif,所以这就是它的样子:
如果感兴趣的话,我可以发布完整的代码来制作情节,但基本上我只是为每个时间步用不同的D和T0值调用Y.如果我再次这样做,我可以增加阻尼(即减少Tc),但这有点麻烦,所以我按原样离开。
答案 1 :(得分:5)
我和@ tom10一样思考。 (我还考虑过IEasingFunction
IList<IEasingFunction>
,但是从现有的行为中删除所需的行为会很棘手。)
// Based on the example at
// http://msdn.microsoft.com/en-us/library/system.windows.media.animation.easingfunctionbase.aspx
namespace Org.CheddarMonk
{
public class OtakuEasingFunction : EasingFunctionBase
{
// The time proportion at which the cutoff from linear movement to
// bounce occurs. E.g. for a 4 second movement followed by a 16
// second bounce this would be 4 / (4 + 16) = 0.2.
private double _CutoffPoint;
public double CutoffPoint {
get { return _CutoffPoint; }
set {
if (value <= 0 || value => 1 || double.IsNaN(value)) {
throw new ArgumentException();
}
_CutoffPoint = value;
}
}
// The size of the initial bounce envelope, as a proportion of the
// animation distance. E.g. if the animation moves from 900 to 150
// and you want the maximum bounce to be no more than 35 you would
// set this to 35 / (900 - 150) ~= 0.0467.
private double _EnvelopeHeight;
public double EnvelopeHeight {
get { return _EnvelopeHeight; }
set {
if (value <= 0 || double.IsNaN(value)) {
throw new ArgumentException();
}
_EnvelopeHeight = value;
}
}
// A parameter controlling how fast the bounce height should decay.
// The higher the decay, the sooner the bounce becomes negligible.
private double _EnvelopeDecay;
public double EnvelopeDecay {
get { return _EnvelopeDecay; }
set {
if (value <= 0 || double.IsNaN(value)) {
throw new ArgumentException();
}
_EnvelopeDecay = value;
}
}
// The number of half-bounces.
private int _Oscillations;
public int Oscillations {
get { return _Oscillations; }
set {
if (value <= 0) {
throw new ArgumentException();
}
_Oscillations = value;
}
}
public OtakuEasingFunction() {
// Sensible default values.
CutoffPoint = 0.7;
EnvelopeHeight = 0.3;
EnvelopeDecay = 1;
Oscillations = 3;
}
protected override double EaseInCore(double normalizedTime) {
// If we get an out-of-bounds value, be nice.
if (normalizedTime < 0) return 0;
if (normalizedTime > 1) return 1;
if (normalizedTime < _CutoffPoint) {
return normalizedTime / _CutoffPoint;
}
// Renormalise the time.
double t = (normalizedTime - _CutoffPoint) / (1 - _CutoffPoint);
double envelope = EnvelopeHeight * Math.Exp(-t * EnvelopeDecay);
double bounce = Math.Sin(t * Oscillations * Math.PI);
return envelope * bounce;
}
protected override Freezable CreateInstanceCore() {
return new OtakuEasingFunction();
}
}
}
这是未经测试的代码,但如果有问题,调试应该不会太糟糕。我不确定需要将哪些属性(如果有)添加到XAML编辑器的属性中才能正确处理它们。