2

我的目标是用 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中的显式公式: 三次贝塞尔 Wiki 公式

演示 - 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

我不知道为什么我的尝试失败了,而且我找到的代码有效。我是否错误地执行了公式?我选择了错误的公式吗?还有什么?

4

3 回答 3

3

因此,您的错误既不是传递单个值,也不是传递幂函数。错误是您假设 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 并为您提供明确的贝塞尔曲线。您可以在http://cubic-bezier.com上通过将地址栏中的 x 值设置为 0.333 和 0.666 来实现此目的。但是,一旦您将控制点向左或向右移动,您的结果就会再次不同。

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

于 2017-08-30T16:19:42.533 回答
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
}
于 2018-08-18T16:02:20.837 回答
0

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

Math.pow(base, exponent)
于 2017-04-06T23:55:01.957 回答