2012-07-27 83 views
5

在CSS3转换中,您可以将定时函数指定为'cubic-bezier:(0.25,0.3,0.8,1.0)' 在该字符串中,您只指定点P1的XY和P2沿着曲线,因为P0和P3总是分别为(0.0,0.0)和(1.0,1.0)。重新创建CSS3转换Cubic-Bezier曲线

根据苹果公司的网站: X [是]表示为总时间的一小部分和y表示为总体变化的一小部分

我的问题是如何将这些被映射回传统的一维T值在JavaScript?

-

From Apple docs on animating with transitions
enter image description here

+0

的'length'是't'值的函数。不太确定你会怎样去解决这个问题。 – Wex 2012-07-27 23:09:59

回答

16

通过WebKit的浏览源一点,下面的代码会给出正确的t值在CSS3过渡使用隐式曲线:

Visual demo (codepen.io)

希望这可以帮助别人!

function loop(){ 
    var t = (now - animationStartTime)/(animationDuration*1000); 

    var curve = new UnitBezier(Bx, By, Cx, Cy); 
    var t1 = curve.solve(t, UnitBezier.prototype.epsilon); 
    var s1 = 1.0-t1; 

    // Lerp using solved T 
    var finalPosition.x = (startPosition.x * s1) + (endPosition.x * t1); 
    var finalPosition.y = (startPosition.y * s1) + (endPosition.y * t1); 
} 


/** 
* Solver for cubic bezier curve with implicit control points at (0,0) and (1.0, 1.0) 
*/ 
function UnitBezier(p1x, p1y, p2x, p2y) { 
    // pre-calculate the polynomial coefficients 
    // First and last control points are implied to be (0,0) and (1.0, 1.0) 
    this.cx = 3.0 * p1x; 
    this.bx = 3.0 * (p2x - p1x) - this.cx; 
    this.ax = 1.0 - this.cx -this.bx; 

    this.cy = 3.0 * p1y; 
    this.by = 3.0 * (p2y - p1y) - this.cy; 
    this.ay = 1.0 - this.cy - this.by; 
} 

UnitBezier.prototype.epsilon = 1e-6; // Precision 
UnitBezier.prototype.sampleCurveX = function(t) { 
    return ((this.ax * t + this.bx) * t + this.cx) * t; 
} 
UnitBezier.prototype.sampleCurveY = function (t) { 
    return ((this.ay * t + this.by) * t + this.cy) * t; 
} 
UnitBezier.prototype.sampleCurveDerivativeX = function (t) { 
    return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx; 
} 


UnitBezier.prototype.solveCurveX = function (x, epsilon) { 
    var t0; 
    var t1; 
    var t2; 
    var x2; 
    var d2; 
    var i; 

    // First try a few iterations of Newton's method -- normally very fast. 
    for (t2 = x, i = 0; i < 8; i++) { 
     x2 = this.sampleCurveX(t2) - x; 
     if (Math.abs (x2) < epsilon) 
      return t2; 
     d2 = this.sampleCurveDerivativeX(t2); 
     if (Math.abs(d2) < epsilon) 
      break; 
     t2 = t2 - x2/d2; 
    } 

    // No solution found - use bi-section 
    t0 = 0.0; 
    t1 = 1.0; 
    t2 = x; 

    if (t2 < t0) return t0; 
    if (t2 > t1) return t1; 

    while (t0 < t1) { 
     x2 = this.sampleCurveX(t2); 
     if (Math.abs(x2 - x) < epsilon) 
      return t2; 
     if (x > x2) t0 = t2; 
     else t1 = t2; 

     t2 = (t1 - t0) * .5 + t0; 
    } 

    // Give up 
    return t2; 
} 

// Find new T as a function of Y along curve X 
UnitBezier.prototype.solve = function (x, epsilon) { 
    return this.sampleCurveY(this.solveCurveX(x, epsilon)); 
} 
0

你想找到的任何时间值t [0,1]中的[0,1]值?对于三次贝塞尔曲线有一个明确定义的等式。维基百科页面:http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves

所以我不必键入他们的(可能是乳胶格式)的公式,我从http://local.wasp.uwa.edu.au/~pbourke/geometry/bezier/index2.html复制粘贴相同的公式。这也有一个C语言的实现当中,对快速通读,应该很容易地移植到JavaScript的:

B(u) = P0 * (1 - u)3 + P1 * 3 * u * (1 - u)2 + P2 * 3 * u2 * (1 - u) + P3 * u3

什么,他在该网页上调用亩是你的时间变量t。

编辑:如果你不想做数学,它看起来像有人已经写了一个JavaScript的小实用程序库来做基本的贝塞尔曲线数学:https://github.com/sporritt/jsBezier。 pointOnCurve(曲线,位置)看起来就像你所要求的。

+0

这给出沿曲线的点P,但不是如何将该点转换回被移动物体的最终位置。 (我没有沿曲线移动物体) – 1dayitwillmake 2012-07-28 01:29:09

-1

我尝试和搜索了很多时间和形式,并definetly我已达到一个简单和快速。诀窍是以这种形式得到三次贝塞尔函数:P(u)= u^3(c0 + 3c1-3c2 + c3)+ u^2(3c0-6c1 + 3c2)+ u(-3c0 + 3c1)+ c0 其中ci是控制点。 另一部分是用二进制搜索从x中搜索y。

static public class CubicBezier { 
    private BezierCubic bezier = new BezierCubic(); 
    public CubicBezier(float x1, float y1, float x2, float y2) { 
     bezier.set(new Vector3(0,0,0), new Vector3(x1,y1,0), new Vector3(x2,y2,0), new Vector3(1,1,1)); 
    } 
    public float get(float t) { 
     float l=0, u=1, s=(u+l)*0.5f; 
     float x = bezier.getValueX(s); 
     while (Math.abs(t-x) > 0.0001f) { 
      if (t > x) { l = s; } 
      else  { u = s; } 
      s = (u+l)*0.5f; 
      x = bezier.getValueX(s); 
     } 
     return bezier.getValueY(s); 
    } 
}; 

public class BezierCubic { 
private float[][] cpoints = new float[4][3]; 
private float[][] polinom = new float[4][3]; 

public BezierCubic() {} 

public void set(Vector3 c0, Vector3 c1, Vector3 c2, Vector3 c3) { 
    setPoint(0, c0); 
    setPoint(1, c1); 
    setPoint(2, c2); 
    setPoint(3, c3); 
    generate(); 
} 

public float getValueX(float u) { 
    return getValue(0, u); 
} 

public float getValueY(float u) { 
    return getValue(1, u); 
} 

public float getValueZ(float u) { 
    return getValue(2, u); 
} 

private float getValue(int i, float u) { 
    return ((polinom[0][i]*u + polinom[1][i])*u + polinom[2][i])*u + polinom[3][i]; 
} 

private void generate() { 
    for (int i=0; i<3; i++) { 
     float c0 = cpoints[0][i], c1 = cpoints[1][i], c2 = cpoints[2][i], c3 = cpoints[3][i]; 
     polinom[0][i] = c0 + 3*(c1 - c2) + c3; 
     polinom[1][i] = 3*(c0 - 2*c1 + c2); 
     polinom[2][i] = 3*(-c0 + c1); 
     polinom[3][i] = c0; 
    } 
} 

private void setPoint(int i, Vector3 v) { 
    cpoints[i][0] = v.x; 
    cpoints[i][1] = v.y; 
    cpoints[i][2] = v.z; 
} 

}