加速/减速比与KeyFrame相当

时间:2011-01-19 18:17:40

标签: c# .net vb.net xaml animation

是否有公式将AccelerationRatioDecelerationRatio转换为贝塞尔控制点,以便在KeySpline的{​​{1}}中使用?例如,“缓出”可能是SplineDoubleKeyFrame,但这似乎与DecelerationRatio=0.5KeySpline="0.0,0.0 0.5,1.0"不等。

这会涉及多个KeySpline="0.5,0 1,0.5"来实现SplineDoubleKeyFrame吗?或者他们是一个特殊的公式,使他们在一个框架中等效?

或者这不是通过DecelerationRatio=0.5而是通过SplineDoubleKeyFrame来实现的(如果是,那么EasingFunction / EasingMode /其他属性是什么)?

基本上,我正在尝试使用KeyFrames实现EasingDoubleKeyFrame,因为会有多个帧定位到相同的属性路径并加速/减速它。


更新:根据第7页的Microsoft WPF-Silverlight Comparison Whitepaper.pdf

  

线性插值   可以通过添加稍微修改   AccelerationRatio和   DecelerationRatio属性为   动画。这些属性   基本上创建三个线性   整个插值   动画为了修改   启动和停止速度。对于   例如,设计师会使用这些   属性逐渐拥有一个对象   提速或突然停止。   不幸的是,Silverlight没有   实现这两个属性,但是   效果可以重复使用   具有线性插值的关键帧动画。

所以我猜这意味着可以只用3个关键帧完成,但公式是什么我不知道。


解决方案:对于其他可能需要的人,发布由Peter Taylor制作的ECMAScript解决方案:

<DoubleAnimation Storyboard.TargetName="deceleratedRectangle" Storyboard.TargetProperty="(Rectangle.Width)"  DecelerationRatio="0.5" Duration="0:0:10" From="20" To="400" />

2 个答案:

答案 0 :(得分:8)

一般解决方案

更新:使用微积分的几何解释作为一条线下的区域,我已经研究了如何简化推导。

因此,我们在时间t0从y0插入到时间t1的y1,加速比ra和减速比rd。比率的定义给出了我们停止加速的时间,ta = t0 + ra *(t1-t0),并且我们开始减速,td = t1-rd *(t1-t0)。

我理解你引用的文件是指从t0到ta的恒定加速度,以及从td到t1的恒定减速度。我们将达到的最大速度为vm。

 Speed
   |        _____________________________________
vm +       /|                                   |\
   |      /                                       \
   |     /  |                                   |  \
   |    /                                           \
   |   /    |                                   |    \
   |  /                                               \
   | /      |                                   |      \
   |/                                                   \
   +--------+-----------------------------------+--------+---- Time
   t0       ta                                  td       t1

然后平行四边形的面积是从t0到t1的距离,即y1-y0。平行四边形的面积是高度与平行边的平均值的乘积。所以

y1 - y0 = vm *((t1 - t0)+(td - ta))/ 2

vm = 2 *(y1 - y0)/(t1 + td - ta - t0)

只使用末尾三角形的区域,我们可以找到当我们停止加速时我们走了多远,ya = y(ta),当我们开始减速时,yd = y(td)。

ya = y0 + vm *(ta - t0)/ 2

yd = y1 - vm *(t1 - td)/ 2

最后,我们为[t0,ta],直线(ta,ya) - (td,yd)生成二次Bezier,为[td,t1]生成二次Bezier。

对于第一个Bezier,我们有明显的控制点P0 =(t0,y0)和P2 =(ta,ya)。为了找到P1,我们使用P0-P1与曲线相切并且P1-P2与曲线相切的属性(通常,对于阶数-n曲线,P0-P1和P(n-1)-Pn是相切的) 。因此P1位于y = y0与中段直线的交点处。类似地,对于另一个贝塞尔:Q0 =(td,yd),Q2 =(t1,y1),Q1位于y = y1与中段直线的交点处。


工作示例:

没有淡入(加速比= 0),减速比= 0.5,t0 = 0,t1 = 10(秒),y0 = 20,y1 = 400.我认为这符合您的具体问题。

ta = 0(我们可以省略第一个二次贝塞尔曲线); td = t1 - 0.5 *(t1 - t0)= 5.

vm = 2 *(y1 - y0)/(t1 + td - ta - t0)= 2 *(400 - 20)/(10 + 5 - 0 - 0)= 2 * 380/15 = 152/3 〜= 50.67。

忽略你,因为我们正在跳过Bezier。

yd = y1 - vm *(t1 - td)/ 2 = 400 - 152/3 *(10-5)/ 2 = 400 - 380/3 = 820 / 3~ = 273.3

因此直线从(t = 0,y = 20)变为(t = 5,y = 273.3)。减速Bezier的Q0 =(5,273.3),Q2 =(10,400)。

为了找到Q1,我们将直线延伸到y = 400。该线具有方程y - 20 =(t - 0)*(273.3 - 20)/(5 - 0),因此t = 5 *(400 - 20)/(273.3 - 20)= 7.5。

所以我们有直线(0,20) - (5,273.3)和带控制点(5,273.3),(7.5,400)和(10,400)的二次贝塞尔曲线。

将其转换为关键帧

然而,有一个轻微的障碍,这是微软没有设计给我们二次样条。我们必须将二次Q0,Q1,Q2提升到立方Q0,(Q0 + 2 Q1)/ 3,(2 Q1 + Q2)/ 3,Q2。

我们还必须将控制点重新调整为0-1。如果我们首先应用重新缩放,我们有(0,0) - (0.5,1) - (1,1)。所以立方是(0,0) - (0.333,0.667) - (0.667,1) - (1,1)。

我知道样条,但不知道WPF。我认为以下会做你想做的事:

<DiscreteDoubleKeyFrame KeyTime="0:0:0" Value="20" />
<LinearDoubleKeyFrame KeyTime="0:0:5" Value="273.333" />
<SplineDoubleKeyFrame KeyTime="0:0:10" Value="400" KeySpline="0.333,0.667,0.667,1"/>

转型的普遍性

重新缩放第一个贝塞尔曲线,我们将(t0,y0)映射到(0,0)和(ta,ya)到(1,1)。因此,我们将(P1.t,P1.y)映射到((P1.t-t0)/(ta-t0),(P1.y-y0)/(ya-y0))。但是P1在y = y0与梯度直线vm到(ta,ya)的交点处,因此具有方程(y-ya)=(t-ta)* vm。所以P1.y = y0和P1.t = ta +(y0-ya)/ vm = ta - (ya-y0)/ vm。插入我们的身份ya = y0 + vm *(ta - t0)/ 2,得到P1.t = ta - (vm *(ta - t0)/ 2)/ vm = ta - (ta - t0)/ 2 = (ta + t0)/ 2。

如此重新缩放我们将P1映射到(0.5,0)。因此,当我们将其提升为三次贝塞尔曲线时,中间控制点始终为(1 / 3,0)和(2 / 3,1 / 3)。

同样,减速样条曲线的缩放中间点始终为(1 / 3,2 / 3)和(2 / 3,1)。

答案 1 :(得分:2)

我希望我的问题是对的。

我在WPF项目中测试了您的示例,并使用DoubleAnimationUsingKeyFrames和SplineDoubleKeyFrame创建了类似的效果。这是我在故事板中所做的(这是减速):

<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="rectangle">
    <SplineDoubleKeyFrame KeyTime="0" Value="1"/>
    <SplineDoubleKeyFrame KeyTime="0:0:3" Value="3.45" KeySpline="0,0.52,0.51,1"/>
</DoubleAnimationUsingKeyFrames>

诀窍是为SplineDoubleKeyFrame设置Bezier。如果您想要平滑的加速/减速,请尝试使用更接近线性形式的贝塞尔曲线。

此外:Acceleration / DecelerationRatio适用于整个动画,但KeySpline仅适用于KeyFrame。如果您有一个由多个KeyFrame组成的动画,请考虑这一点。