├── .eslintrc
├── README.md
├── example.js
├── ipo_example.png
├── package.json
└── src
├── checkPoints.js
├── computeBeziers.js
└── index.js
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "indent": [
4 | 2,
5 | 2
6 | ],
7 | "quotes": [
8 | 2,
9 | "double"
10 | ],
11 | "linebreak-style": [
12 | 2,
13 | "unix"
14 | ],
15 | "semi": [
16 | 2,
17 | "always"
18 | ]
19 | },
20 | "env": {
21 | "node": true,
22 | "browser": true
23 | },
24 | "extends": "eslint:recommended"
25 | }
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | IPO
2 | ===
3 |
4 | *IPO*, short for *InterPOlation*, is an easing library allowing to describe complex easings in JSON (multiple point over time can be placed).
5 |
6 | > Inspired from Blender's IPO idea, it is made for animating a value in many ways over time.
7 |
8 | Screenshot running [example.js](./example.js):
9 |
10 | 
11 |
12 | ```json
13 | [
14 | { "p": [100, 100], "upper": [10, 20] },
15 | { "p": [200, 190], "lower": [-10, 0], "upper": [10, 0] },
16 | { "p": [250, 100], "upper": [30, 0] },
17 | { "p": [280, 140] },
18 | { "p": [350, 160] },
19 | { "p": [400, 50], "lower": [-50, 40], "upper": [100, 0] },
20 | { "p": [600, 250], "lower": [-140, 0], "upper": [ 40, -40 ] }
21 | ]
22 | ```
23 |
24 | *(the white dots are the interpolated values, the red point are the control points and their handles, the yellow curve is the SVG continuous curve.)*
25 |
26 | API
27 | ===
28 |
29 | ```js
30 | var IPO = require("ipo");
31 | var ipo = IPO([ ...points... ]);
32 | ipo(42); // Get the curve Y value at X=42
33 | ```
34 |
35 | ### Format
36 |
37 | `points` is:
38 |
39 | - an Array of Point, where each Point is an object with
40 | - a position `p` which is a `[x, y]`
41 | - (option) `lower`: relative position of a bezier handle for the lower curve interpolation
42 | - (option) `upper`: same for the upper curve interpolation.
43 |
44 |
45 | Under the hood
46 | ==============
47 |
48 | - [`bezier-easing`](https://github.com/gre/bezier-easing) is used to perform bezier interpolation. It provides efficient computation with various optimization techniques.
49 |
--------------------------------------------------------------------------------
/example.js:
--------------------------------------------------------------------------------
1 | var IPO = require(".");
2 |
3 | // for the little story, i've written this library in the airplane on my way to NY
4 | // and everyone is sleeping... this is why everything is dark !!!
5 | document.body.style.background = "black";
6 |
7 | var points = [
8 | { "p": [100, 100], "upper": [10, 20] },
9 | { "p": [200, 190], "lower": [-10, 0], "upper": [10, 0] },
10 | { "p": [250, 100], "upper": [30, 0] },
11 | { "p": [280, 140] },
12 | { "p": [350, 160] },
13 | { "p": [400, 50], "lower": [-50, 40], "upper": [100, 0] },
14 | { "p": [600, 250], "lower": [-140, 0], "upper": [ 40, -40 ] }
15 | ];
16 | var ipo = IPO(points);
17 |
18 | // Quick draw in SVG
19 |
20 | function addPoints (a, b) {
21 | return [ a[0] + b[0], a[1] + b[1] ];
22 | }
23 | function project (p) {
24 | return [ 1 * p[0], 300 - p[1] ];
25 | }
26 |
27 | var svg = "";
61 |
62 | document.body.innerHTML = svg;
63 |
--------------------------------------------------------------------------------
/ipo_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gre/ipo/79d79a796b8e3345976196d147d63fe95314805d/ipo_example.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ipo",
3 | "version": "2.0.0",
4 | "description": "easing library allowing to describe complex easings in JSON",
5 | "tags": [
6 | "easing",
7 | "bezier",
8 | "curve",
9 | "ipo",
10 | "interpolation",
11 | "animation"
12 | ],
13 | "main": "src/index.js",
14 | "author": "Gaëtan Renaudeau",
15 | "license": "ISC",
16 | "dependencies": {
17 | "bezier-easing": "2.0.x",
18 | "invariant": "2.2.1"
19 | },
20 | "devDependencies": {
21 | "eslint": "latest"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/checkPoints.js:
--------------------------------------------------------------------------------
1 | var invariant = require("invariant");
2 |
3 | module.exports = function (points) {
4 | invariant(points instanceof Array && points.length>0, "points is a non-empty array");
5 | var curX = -Infinity;
6 | for (var i=0; i curX, "points must be sorted by `p[0]` (x position)");
10 | curX = x;
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/computeBeziers.js:
--------------------------------------------------------------------------------
1 | var invariant = require("invariant");
2 | var BezierEasing = require("bezier-easing");
3 |
4 | module.exports = function (points) {
5 | var prev = points[0], point;
6 | var beziers = [];
7 | for (var i=1; i pX) {
18 | // There is no 2 points to interpolate between, it is just point's y unless an lower/upper is defined,
19 | // in that case, we do a linear interpolation on edges.
20 | var edge = x > pX && point.upper || x < pX && point.lower;
21 | return point.p[1] + (edge && edge[1] !== 0 ? (x-pX) * edge[1] / edge[0] : 0);
22 | }
23 | var bezier = beziers[i - 1];
24 | if (!bezier) return prev.p[1]; // this happens when two following points have the same Y.
25 | var a = prev.p;
26 | var b = point.p;
27 | var w = b[0] - a[0];
28 | var h = b[1] - a[1];
29 | // Get the bezier's value and map it to the points' domain
30 | return a[1] + h * bezier((x - a[0]) / w);
31 | };
32 | };
33 |
--------------------------------------------------------------------------------