加速/减速比与KeyFrame相当

Tod*_*ain 9 .net c# vb.net animation xaml

是否有一个公式将AccelerationRatioDecelerationRatio转换为Bezier控制点,以便在a KeySpline中使用SplineDoubleKeyFrame?例如,"缓出"可能是DecelerationRatio=0.5,但这似乎不等于KeySpline="0.0,0.0 0.5,1.0"KeySpline="0.5,0 1,0.5".

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

或者这不是通过a SplineDoubleKeyFrame而是通过EasingDoubleKeyFrame反而实现的(如果是,那么EasingFunction/EasingMode/Other Attributes是什么)?

本质上,我正在尝试<DoubleAnimation Storyboard.TargetName="deceleratedRectangle" Storyboard.TargetProperty="(Rectangle.Width)" DecelerationRatio="0.5" Duration="0:0:10" From="20" To="400" />使用KeyFrames 来实现,因为会有多个帧以相同的属性路径为目标并加速/减速.


更新:根据第7页的Microsoft WPF-Silverlight比较白皮书.pdf:

通过向动画添加AccelerationRatio和DecelerationRatio属性,可以稍微修改线性插值.这些属性实际上为整个动画创建了三个线性插值,以便修改起始和停止速度.例如,设计师将使用这些属性使对象逐渐加速或突然停止.不幸的是,Silverlight没有实现这两个属性,但可以使用带有线性插值的关键帧动画来复制效果.

所以我想这意味着只需要3个关键帧即可完成,但是公式是什么我不知道.


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

<html>
    <head>
        <title>Acceleration or deceleration with Bezier splines</title>
        <script type="text/javascript"><!--
            function calcBezier() {
                var res = new Array();
                var y0 = +document.getElementById("y0").value;
                var y1 = +document.getElementById("y1").value;
                var t0 = +document.getElementById("t0").value;
                var t1 = +document.getElementById("t1").value;
                var ra = +document.getElementById("ra").value;
                var rd = +document.getElementById("rd").value;

                var ta = t0 + ra * (t1 - t0);
                if (ra > 0) res.push("ta = " + ta + "<br />");
                var td = t1 - rd * (t1 - t0);
                if (rd > 0) res.push("td = " + td + "<br />");

                var vm = 2 * (y1 - y0) / (t1 + td - ta - t0);
                res.push("vm = " + vm + "<br />");

                var ya = y0 + vm * (ta - t0) / 2
                if (ra > 0) res.push("ya = " + ya + "<br />");
                var yd = y1 - vm * (t1 - td) / 2
                if (rd > 0) res.push("yd = " + yd + "<br />");

                res.push('&lt;DiscreteDoubleKeyFrame KeyTime="'+t0+'" Value="'+y0+'" /&gt;<br />');

                if (ra > 0) {
                    // y - ya = (t - ta) * vm => t = ta + (y - ya) / vm
                    var p1t = ta - (ya - y0) / vm;
                    // Scale for spline params: (t0,y0):(0,0) and (ta,ya):(1,1)
                    p1t = (p1t - t0) / (ta - t0);
                    // Lift to cubic.
                    res.push('&lt;SplineDoubleKeyFrame KeyTime="'+ta+'" Value="'+ya+'" KeySpline="'+((2*p1t)/3)+',0 '+((2*p1t+1)/3)+','+(1/3)+'" /&gt;<br />');
                }

                if (ra + rd < 1) {
                    res.push('&lt;LinearDoubleKeyFrame KeyTime="'+td+'" Value="'+yd+'" /&gt;<br />');
                }

                if (rd > 0) {
                    var q1y = 1;
                    var q1t = td + (y1 - yd) / vm;
                    q1t = (q1t - td) / (t1 - td);
                    res.push('&lt;SplineDoubleKeyFrame KeyTime="'+t1+'" Value="'+y1+'" KeySpline="'+((2*q1t)/3)+','+(2/3)+' '+((2*q1t+1)/3)+',1" /&gt;<br />');
                }

                document.getElementById("results").innerHTML = res.join("");
            }
        //-->
        </script>
    </head>
    <body>
        <p>Interpolate y from <input id="y0" /> to <input id="y1" />.</p>
        <p>Start time: <input id="t0" />; end time: <input id="t1" />.</p>
        <p>Acceleration ratio: <input id="ra" />; deceleration ratio: <input id="rd" />.</p>
        <p><input type="submit" value="Calculate" onclick="calcBezier();" /></p>
        <p id="results">&nbsp;</p>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

Pet*_*lor 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)生成二次贝塞尔曲线,为[td,t1]生成二次贝塞尔曲线.

对于第一个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).减速贝塞尔的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 + 2Q1)/ 3,(2Q1 + Q2)/ 3,Q2.

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

我知道splines,但不知道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).