Skip to content

Commit 31cf033

Browse files
committed
simpler library: just expose a (a,b,c,d)=>number=>number function
It is also a performance boost see below. Performance before: BezierEasing: instanciation x 147,240 ops/sec ±5.24% (73 runs sampled) BezierEasing: call x 2,807,525 ops/sec ±1.18% (88 runs sampled) BezierEasing: instanciation + call x 124,917 ops/sec ±4.78% (83 runs sampled) Performance after: BezierEasing: instanciation x 1,043,725 ops/sec ±1.46% (82 runs sampled) BezierEasing: call x 7,866,642 ops/sec ±0.93% (85 runs sampled) BezierEasing: instanciation + call x 803,051 ops/sec ±1.58% (74 runs sampled)
1 parent 5479c4a commit 31cf033

11 files changed

+103
-269
lines changed

.eslintrc

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"env": {
3+
"node": true
4+
},
5+
"extends": "eslint:recommended",
6+
"rules": {
7+
"indent": [
8+
2,
9+
2
10+
],
11+
"linebreak-style": [
12+
2,
13+
"unix"
14+
],
15+
"quotes": [
16+
2,
17+
"single"
18+
],
19+
"semi": [
20+
2,
21+
"always"
22+
]
23+
}
24+
}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

.jshintrc

-22
This file was deleted.

README.md

+4-22
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ Usage
1515
```javascript
1616
var easing = BezierEasing(0, 0, 1, 0.5);
1717
// easing allows to project x in [0.0,1.0] range onto the bezier-curve defined by the 4 points (see schema below).
18-
console.log(easing.get(0.0)); // 0.0
19-
console.log(easing.get(0.5)); // 0.3125
20-
console.log(easing.get(1.0)); // 1.0
18+
console.log(easing(0.0)); // 0.0
19+
console.log(easing(0.5)); // 0.3125
20+
console.log(easing(1.0)); // 1.0
2121
```
2222

2323
(this schema is from the CSS spec)
@@ -33,25 +33,7 @@ It is the equivalent to [CSS Transitions' `transition-timing-function`](http://w
3333

3434

3535
In the same way you can define in CSS `cubic-bezier(0.42, 0, 0.58, 1)`,
36-
with BezierEasing, you can define it using `BezierEasing(0.42, 0, 0.58, 1)` which have the `.get` function taking an X and computing the Y interpolated easing value (see schema).
37-
38-
39-
Predefined BezierEasing functions
40-
---
41-
42-
**bezier-easing** also define a mapping from existing CSS `transition-timing-function` :
43-
44-
```javscript
45-
BezierEasing.css = {
46-
"ease": BezierEasing.ease = BezierEasing(0.25, 0.1, 0.25, 1.0),
47-
"linear": BezierEasing.linear = BezierEasing(0.00, 0.0, 1.00, 1.0),
48-
"ease-in": BezierEasing.easeIn = BezierEasing(0.42, 0.0, 1.00, 1.0),
49-
"ease-out": BezierEasing.easeOut = BezierEasing(0.00, 0.0, 0.58, 1.0),
50-
"ease-in-out": BezierEasing.easeInOut = BezierEasing(0.42, 0.0, 0.58, 1.0)
51-
};
52-
```
53-
54-
There is also a `toCSS()` method that returns the transition-timing-function value string (so the library is agnostic).
36+
with BezierEasing, you can define it using `BezierEasing(0.42, 0, 0.58, 1)` which have the `` function taking an X and computing the Y interpolated easing value (see schema).
5537

5638
License
5739
-------

benchmark.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ var suite = new Benchmark.Suite();
55

66
var random = Math.random;
77

8-
var bezier = BezierEasing([ 0.2, 0.3, 1.0, 0.5 ]);
8+
var bezier = BezierEasing(0.2, 0.3, 1.0, 0.5);
99

1010
suite
1111
.add('BezierEasing: instanciation', function() {
12-
BezierEasing([ random(), random(), random(), random() ]);
12+
BezierEasing(random(), random(), random(), random());
1313
})
1414
.add('BezierEasing: call', function() {
15-
bezier.get(random());
15+
bezier(random());
1616
})
1717
.add('BezierEasing: instanciation + call', function() {
18-
var bezier = BezierEasing([ random(), random(), random(), random() ]);
19-
bezier.get(random());
18+
var bezier = BezierEasing(random(), random(), random(), random());
19+
bezier(random());
2020
})
2121
// add listeners
2222
.on('cycle', function(event) {

bower.json

-32
This file was deleted.

build.js

-1
This file was deleted.

index.js

+37-111
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22
* BezierEasing - use bezier curve for transition easing function
33
* by Gaëtan Renaudeau 2014 - 2015 – MIT License
44
*
5-
* Credits: is based on Firefox's nsSMILKeySpline.cpp
6-
* Usage:
7-
* var spline = BezierEasing([ 0.25, 0.1, 0.25, 1.0 ])
8-
* spline.get(x) => returns the easing value | x must be in [0, 1] range
9-
*
5+
* @providesModule bezier
106
*/
117

128
// These values are established by empiricism with tests (tradeoff: performance VS precision)
@@ -18,21 +14,17 @@ var SUBDIVISION_MAX_ITERATIONS = 10;
1814
var kSplineTableSize = 11;
1915
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
2016

21-
var float32ArraySupported = typeof Float32Array === "function";
17+
var float32ArraySupported = typeof Float32Array === 'function';
2218

2319
function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; }
2420
function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; }
2521
function C (aA1) { return 3.0 * aA1; }
2622

2723
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
28-
function calcBezier (aT, aA1, aA2) {
29-
return ((A(aA1, aA2)*aT + B(aA1, aA2))*aT + C(aA1))*aT;
30-
}
24+
function calcBezier (aT, aA1, aA2) { return((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; }
3125

3226
// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2.
33-
function getSlope (aT, aA1, aA2) {
34-
return 3.0 * A(aA1, aA2)*aT*aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
35-
}
27+
function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); }
3628

3729
function binarySubdivide (aX, aA, aB, mX1, mX2) {
3830
var currentX, currentT, i = 0;
@@ -49,111 +41,42 @@ function binarySubdivide (aX, aA, aB, mX1, mX2) {
4941
}
5042

5143
function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
52-
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
53-
var currentSlope = getSlope(aGuessT, mX1, mX2);
54-
if (currentSlope === 0.0) return aGuessT;
55-
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
56-
aGuessT -= currentX / currentSlope;
57-
}
58-
return aGuessT;
44+
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
45+
var currentSlope = getSlope(aGuessT, mX1, mX2);
46+
if (currentSlope === 0.0) {
47+
return aGuessT;
48+
}
49+
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
50+
aGuessT -= currentX / currentSlope;
51+
}
52+
return aGuessT;
5953
}
6054

61-
/**
62-
* points is an array of [ mX1, mY1, mX2, mY2 ]
63-
*/
64-
function BezierEasing (points, b, c, d) {
65-
if (arguments.length === 4) {
66-
return new BezierEasing([ points, b, c, d ]);
67-
}
68-
if (!(this instanceof BezierEasing)) return new BezierEasing(points);
69-
70-
if (!points || points.length !== 4) {
71-
throw new Error("BezierEasing: points must contains 4 values");
55+
module.exports = function bezier (mX1, mY1, mX2, mY2) {
56+
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
57+
throw new Error('bezier x values must be in [0, 1] range');
7258
}
73-
for (var i=0; i<4; ++i) {
74-
if (typeof points[i] !== "number" || isNaN(points[i]) || !isFinite(points[i])) {
75-
throw new Error("BezierEasing: points should be integers.");
76-
}
77-
}
78-
if (points[0] < 0 || points[0] > 1 || points[2] < 0 || points[2] > 1) {
79-
throw new Error("BezierEasing x values must be in [0, 1] range.");
80-
}
81-
82-
this._str = "BezierEasing("+points+")";
83-
this._css = "cubic-bezier("+points+")";
84-
this._p = points;
85-
this._mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
86-
this._precomputed = false;
87-
88-
this.get = this.get.bind(this);
89-
}
90-
91-
BezierEasing.prototype = {
9259

93-
get: function (x) {
94-
var mX1 = this._p[0],
95-
mY1 = this._p[1],
96-
mX2 = this._p[2],
97-
mY2 = this._p[3];
98-
if (!this._precomputed) this._precompute();
99-
if (mX1 === mY1 && mX2 === mY2) return x; // linear
100-
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
101-
if (x === 0) return 0;
102-
if (x === 1) return 1;
103-
return calcBezier(this._getTForX(x), mY1, mY2);
104-
},
105-
106-
getPoints: function() {
107-
return this._p;
108-
},
109-
110-
toString: function () {
111-
return this._str;
112-
},
113-
114-
toCSS: function () {
115-
return this._css;
116-
},
117-
118-
// Private part
119-
120-
_precompute: function () {
121-
var mX1 = this._p[0],
122-
mY1 = this._p[1],
123-
mX2 = this._p[2],
124-
mY2 = this._p[3];
125-
this._precomputed = true;
126-
if (mX1 !== mY1 || mX2 !== mY2)
127-
this._calcSampleValues();
128-
},
129-
130-
_calcSampleValues: function () {
131-
var mX1 = this._p[0],
132-
mX2 = this._p[2];
60+
// Precompute samples table
61+
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
62+
if (mX1 !== mY1 || mX2 !== mY2) {
13363
for (var i = 0; i < kSplineTableSize; ++i) {
134-
this._mSampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
64+
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
13565
}
136-
},
137-
138-
/**
139-
* getTForX chose the fastest heuristic to determine the percentage value precisely from a given X projection.
140-
*/
141-
_getTForX: function (aX) {
142-
var mX1 = this._p[0],
143-
mX2 = this._p[2],
144-
mSampleValues = this._mSampleValues;
66+
}
14567

68+
function getTForX (aX) {
14669
var intervalStart = 0.0;
14770
var currentSample = 1;
14871
var lastSample = kSplineTableSize - 1;
14972

150-
for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
73+
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
15174
intervalStart += kSampleStepSize;
15275
}
15376
--currentSample;
15477

15578
// Interpolate to provide an initial guess for t
156-
var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample+1] - mSampleValues[currentSample]);
79+
var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
15780
var guessForT = intervalStart + dist * kSampleStepSize;
15881

15982
var initialSlope = getSlope(guessForT, mX1, mX2);
@@ -165,15 +88,18 @@ BezierEasing.prototype = {
16588
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
16689
}
16790
}
168-
};
16991

170-
// CSS mapping
171-
BezierEasing.css = {
172-
"ease": BezierEasing.ease = BezierEasing(0.25, 0.1, 0.25, 1.0),
173-
"linear": BezierEasing.linear = BezierEasing(0.00, 0.0, 1.00, 1.0),
174-
"ease-in": BezierEasing.easeIn = BezierEasing(0.42, 0.0, 1.00, 1.0),
175-
"ease-out": BezierEasing.easeOut = BezierEasing(0.00, 0.0, 0.58, 1.0),
176-
"ease-in-out": BezierEasing.easeInOut = BezierEasing(0.42, 0.0, 0.58, 1.0)
92+
return function (x) {
93+
if (mX1 === mY1 && mX2 === mY2) {
94+
return x; // linear
95+
}
96+
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
97+
if (x === 0) {
98+
return 0;
99+
}
100+
if (x === 1) {
101+
return 1;
102+
}
103+
return calcBezier(getTForX(x), mY1, mY2);
104+
};
177105
};
178-
179-
module.exports = BezierEasing;

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
"build": "browserify --standalone BezierEasing index.js | uglifyjs -cm > build.js"
2222
},
2323
"devDependencies": {
24-
"assert": "1.0.x",
25-
"benchmark": "^1.0.0",
26-
"browserify": "^10.2.4",
27-
"budo": "^4.0.0",
28-
"mocha": "1.16.x",
29-
"uglify-js": "^2.4.23"
24+
"assert": "^1.3.0",
25+
"benchmark": "^2.1.0",
26+
"browserify": "^13.0.0",
27+
"budo": "^8.1.0",
28+
"mocha": "^2.4.5",
29+
"uglify-js": "^2.6.2"
3030
},
3131
"repository": {
3232
"type": "git",

0 commit comments

Comments
 (0)