【问题标题】:Cubic Bezier curve not returning proper Y value, given (t, p0, p1, p2, p3)三次贝塞尔曲线未返回正确的 Y 值,给定 (t, p0, p1, p2, p3)
【发布时间】:2017-04-06 23:45:59
【问题描述】:

我的目标是用 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 的显式公式:

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

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

【问题讨论】:

  • 我认为我实际上犯了一个大错误 - P 应该是点值.. 它应该有 (x,y) 而我只提供一个数字?

标签: javascript css animation cubic-bezier


【解决方案1】:

所以你的错误既不是传递单个值也不是幂函数。错误在于您假设 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/

【讨论】:

    【解决方案2】:

    聚会迟到了,但是:您的评论“我认为我实际上犯了一个大错误——P 应该是点值.. 它应该有 (x,y) 而我只提供一个数字”是正确的。

    Bezier 曲线是一个参数函数,其中 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
    }
    

    【讨论】:

      【解决方案3】:

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

      Math.pow(base, exponent)
      

      【讨论】:

      • 是的,这很有可能.. 只是在这里尝试使用Math.pow 来实现它:codepen.io/anon/pen/xqNMpG 并得到了与我第一次尝试错误时非常相似(但不完全相同)的输出。
      猜你喜欢
      • 1970-01-01
      • 2011-03-10
      • 1970-01-01
      • 2012-07-27
      • 1970-01-01
      • 1970-01-01
      • 2016-10-05
      • 2012-01-19
      • 1970-01-01
      相关资源
      最近更新 更多