最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Recreating CSS3 transitions Cubic-Bezier curve - Stack Overflow

programmeradmin1浏览0评论

In CSS3 transitions, you can specify a timing function as 'cubic-bezier:(0.25, 0.3, 0.8, 1.0)' In that string, you are only specifying the XY for points P1 and P2 along the curve, as P0 and P3 are always (0.0, 0.0), and (1.0, 1.0) respectively.

According to Apple's site: x [is] expressed as a fraction of the overall duration and y expressed as a fraction of the overall change

My question is how can this be mapped back to a traditional 1 dimensional T value in javascript?

--

From Apple docs on animating with transitions

In CSS3 transitions, you can specify a timing function as 'cubic-bezier:(0.25, 0.3, 0.8, 1.0)' In that string, you are only specifying the XY for points P1 and P2 along the curve, as P0 and P3 are always (0.0, 0.0), and (1.0, 1.0) respectively.

According to Apple's site: x [is] expressed as a fraction of the overall duration and y expressed as a fraction of the overall change

My question is how can this be mapped back to a traditional 1 dimensional T value in javascript?

--

From Apple docs on animating with transitions

Share Improve this question edited Jul 27, 2012 at 23:26 1dayitwillmake asked Jul 27, 2012 at 23:06 1dayitwillmake1dayitwillmake 2,3393 gold badges27 silver badges35 bronze badges 1
  • The length is a function of the t value. Not really sure how you'd go about getting that though. – Wex Commented Jul 27, 2012 at 23:09
Add a comment  | 

3 Answers 3

Reset to default 22

Browsing through webkit-source a bit, the following code will give the correct T value for the implicit curve used in CSS3 transitions:

Visual demo (codepen.io)

Hope this helps someone!

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) );
}

You want to find the [0,1] value for any time value t [0,1]? There's a well-defined equation for a cubic bezier curve. Wikipedia page: http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B.C3.A9zier_curves

So I don't have to type out their (probably LaTeX-formatted) formula, I copy-pasted the same formula from http://local.wasp.uwa.edu.au/~pbourke/geometry/bezier/index2.html . This also has a C implementation which, on a quick read-through, should be easy to port to javascript:

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

What he's calling mu on that page is your time variable t.

Edit: If you don't want to do the math it looks like someone already wrote a small utility library in javascript to do basic bezier curve math: https://github.com/sporritt/jsBezier . pointOnCurve(curve, location) looks like just what you're asking for.

I have try and search a lot of time and forms and definetly i have reached one simple and fast. The trick is get the cubic bezier function in this form: P(u) = u^3(c0 + 3c1 -3c2 +c3) + u^2(3c0 -6c1 +3c2) + u(-3c0 +3c1) + c0 where ci are the control points. The other part is search y from x with a binary search.

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;
}

}

发布评论

评论列表(0)

  1. 暂无评论