给定(t,p0,p1,p2,p3)时,立方贝塞尔曲线未返回正确的Y值

时间:2017-04-06 23:45:59

标签: javascript css animation cubic-bezier

我的目标是用JavaScript编写一个动画,执行一个轻松的样式贝塞尔曲线动画 (例如在http://cubic-bezier.com/#.42,0,.58,1

我想出了以下脚本来计算给定“x”值(时间)的y值:

function CalculateBezierPoint(t, p0, p1, p2, p3) {
  var y = ((1-t)*(1-t)*(1-t)*p0) + (3*(1-t)*(1-t)*t*p1) + (3*(1-t)*t*t*p2) + (t*t*t*p3);
  return y;
}

使用Wikipedia中的显式公式: Cubic Bezier Wiki Formula

演示 - https://codepen.io/anon/pen/QpRzBg

然而,打印报表显示Y值在上升之前会下降,而它应该只会上升:

0.42
0.3228427793606603
0.3119941308275725
0.3025864871426283
0.29458762995005683
0.2879653408940873
0.28268740161894895
0.27872159376887096
0.27603569898808256
0.27459749892081287
0.2743747752112911
0.2753353095037466
0.27744688344240837
0.28067727867150566
0.2849942768352678
0.29675920854370297
0.3041427053768346
0.3124839317215477
0.3217506690862967
0.33191069937460593
0.34293180410763435
0.35478176492961133

我确实设法找到其他似乎有用的代码,这是输出:

0
0.009480343767040133
0.0246451904411195
0.03199616010201068
0.040680303103589804
0.05080871722437687
0.062492500242891866
0.07584274993765482
0.0909705640871857
0.10798704047000454
0.12700327686463134
0.14813037104958607
0.17147942080338874
0.19716152390455938
0.225287778131618
0.25596928126308455
0.28931713107747903
0.3254424253533215
0.36445626186913194
0.4064697384034303
0.4515939527347367
0.499940002641571

演示 - https://codepen.io/anon/pen/evabrr

两个演示都使用相同的输入:p0 = .42, p1 = 0, p2 = .58, p3 = 1

我不知道为什么我的尝试失败了,我找到的代码也有效。我执行错误的公式了吗?我选择了错误的配方吗?还有别的吗?

3 个答案:

答案 0 :(得分:2)

所以你的错误既没有传递单个值也没有传递幂函数。错误是你假设x = t。

  

我想出了以下脚本来计算给定“x”值(时间)的y值:

如果x = t,那么你所拥有的是一条明确的贝塞尔曲线,而不是参数贝塞尔曲线。显然是指y是x的函数(即y = f(x))。与参数方程相反,其中x和y都是t的函数(即x = f(t)和y = f(t))。

测试这是真的情况的一种方法是,将x值设置为[0,1 / 3,2 / 3,1]。均匀间隔的x值确保x = t并且将为您提供明确的贝塞尔曲线。您可以通过将地址栏中的x值设置为0.333和0.666来在http://cubic-bezier.com上实现此目的。但只要您将控制点移动到左侧或右侧,结果就会再次发生变化。

为了获得相同的效果,它会更复杂一些。你必须在给定的x求解t,然后从t计算y。求解t有点复杂,但可以用newton-raphson方法近似。此链接可以更好地解释如何实现它:http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/

答案 1 :(得分:1)

晚些时候参加聚会,但是:您的评论“我认为我实际上在犯一个大错误-P应该是点值。它应该有(x,y),我只提供一个数字”是正确的。

贝塞尔曲线是参数函数,其中x和y(或3D中的x,y和z)都是t的函数。您只计算曲线的一半,因此需要修改CalculateBezierPoint函数以返回x / y坐标,而不仅仅是返回y坐标:

calculateBezierPoint(t, xvalues, yvalues) {
  return new Point(
    x = calculateBezierDim(t, x1, x2, x3, x4),
    y = calculateBezierDim(t, y1, y2, y3,y4)
  );
}

calculateBezierDim(t, vals) {
  a=vals[0], b=vals[1], c=vals[2], d=vals[3];
  mt = 1-t;
  t2 = t*t;
  mt2 = mt*mt;
  return a * mt*mt2 + 3 * b * mt2 * t + 3 * c * mt * t2 + d * t2 * t;
}

(当然要适应您的编程语言和数据类型)。

然后您可以绘制该x / y坐标。

step = some small value
S = calculateBezierPoint(0, xvals, yvals)
for(t=step; t<1+step; t+=step) {
  E = calculateBezierPoint(t, xvals, yvals)
  drawLine(S.x, S.y, E.x, E.y)
  S = E
}

答案 2 :(得分:0)

我认为公式中可能存在错误,运算符优先级。我会尝试在适当的地方使用指数函数,这会使事情更容易调试。

Math.pow(base, exponent)