Skip to content

Commit

Permalink
Improve custom easing + more detailed doc about easing : fix #13
Browse files Browse the repository at this point in the history
  • Loading branch information
camille-hdl committed May 1, 2017
1 parent 2c9b624 commit 6f89f21
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 1.2.1
* TS rewrite : TS declaration is available in dist/src/animatePaper.d.ts
* custom easings : you can now pass a `function` `(p: number) => number` to `settings.easing`
* bug fix : negative absolute position supported (relative values must be of string type)
* bug fix : allow 0 duration

Expand Down
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ TypeScript declarations are available as of 1.2.1, in `dist/src/animatePaper.d.t
* `settings.complete` callback takes the `Animation`object as 1st argument.
* Color support for `paper.Group` animation (1.1.*)
* rgb, gray, hsl, hbs Color formats are now supported (1.1.*)
* bug fix : negative absolute position supported (relative values must be of string type)
* bug fix : allow 0 duration
* bug fix : negative absolute position supported (relative values must be of string type) (1.2.*)
* bug fix : allow 0 duration (1.2.*)
* custom easings : you can now pass a `function` `(p: number) => number` to `settings.easing` (1.2.*)

## How to use :
### npm and browserify
Expand Down Expand Up @@ -203,9 +204,24 @@ The `stop` method can take a `goToEnd` argument.
If true, all the animations will take their final value and `complete` callbacks will be called.


### Add custom easing functions
### Easing

You can use `animatePaper.extendEasing(myEasingFunctions)` method to add your own easing functions or override any existing easing.
By default, the supported easing functions are : linear, swing, easeInSine, easeOutSine, easeInOutSine, easeInCirc, easeOutCirc, easeInOutCirc, easeInElastic, easeOutElastic, easeInOutElastic, easeInBack, easeOutBack, easeInOutBack, easeInBounce, easeOutBounce, easeInOutBounce, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInQuart, easeOutQuart, easeInOutQuart, easeInQuint, easeOutQuint, easeInOutQuint, easeInExpo, easeOutExpo, easeInOutExpo.

If you want to use more easing functions, `settings.easing` can take a `(p: number) => number` function as a value, so that you can use your own or use some from an external library such as [bezier-easing](https://github.com/gre/bezier-easing).
```js
animatePaper.animate(item,{
properties: {
/** ... **/
},
settings: {
/** ... **/
easing: BezierEasing(0, 0, 1, 0.5)
}
});
```

Alternatively, you can use the `animatePaper.extendEasing(myEasingFunctions)` method to add your own easing functions or override any existing easing.

The method takes only one argument : an object in which keys are easing names, and values are easing functions:

Expand All @@ -217,6 +233,8 @@ animatePaper.extendEasing({
});
```

Learn more about easing [here](http://easings.net/).

### Extend property hooks

If you want to add support for a new property or override the library's behavior for properties that are already supported,
Expand Down Expand Up @@ -264,7 +282,8 @@ as of 1.2.1 the lib uses TypeScript, so make your changes in src/*.ts then build

## TODOS
* Change how `item.data._animatePaperVals` works to allow multiple animations of the same property at the same time.
* tests
* Change `Tween` so that we garantee values are right at `0`and `1` positions, to avoid problems with imprecise numbers (floating point). See "Negative position" in tests.js.
* Add tests

## Help needed !

Expand Down
104 changes: 104 additions & 0 deletions dev/bezier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/**
* https://github.com/gre/bezier-easing
* BezierEasing - use bezier curve for transition easing function
* by Gaëtan Renaudeau 2014 - 2015 – MIT License
*/

// These values are established by empiricism with tests (tradeoff: performance VS precision)
var NEWTON_ITERATIONS = 4;
var NEWTON_MIN_SLOPE = 0.001;
var SUBDIVISION_PRECISION = 0.0000001;
var SUBDIVISION_MAX_ITERATIONS = 10;

var kSplineTableSize = 11;
var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);

var float32ArraySupported = typeof Float32Array === 'function';

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

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

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

function binarySubdivide (aX, aA, aB, mX1, mX2) {
var currentX, currentT, i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
} else {
aA = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}

function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) {
for (var i = 0; i < NEWTON_ITERATIONS; ++i) {
var currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}

window._bezier = function bezier (mX1, mY1, mX2, mY2) {
if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) {
throw new Error('bezier x values must be in [0, 1] range');
}

// Precompute samples table
var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
if (mX1 !== mY1 || mX2 !== mY2) {
for (var i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
}

function getTForX (aX) {
var intervalStart = 0.0;
var currentSample = 1;
var lastSample = kSplineTableSize - 1;

for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;

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

var initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
} else if (initialSlope === 0.0) {
return guessForT;
} else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}

return function BezierEasing (x) {
if (mX1 === mY1 && mX2 === mY2) {
return x; // linear
}
// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if (x === 0) {
return 0;
}
if (x === 1) {
return 1;
}
return calcBezier(getTForX(x), mY1, mY2);
};
};
5 changes: 3 additions & 2 deletions dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.10.3/paper-core.min.js"></script>
<!-- <script src="../node_modules/paper/dist/paper-core.min.js"></script> -->
<script src="../dist/paper-animate-browser.min.js"></script>
<script src="./bezier.js"></script>
<meta charset="utf-8">
<title>animatePaper.js demo</title>
<script>
Expand Down Expand Up @@ -243,8 +244,8 @@
}
},
settings: {
duration: 1000,
easing: "linear"
duration: 3000,
easing: _bezier(1,-0.76,.15,1.55)
}
});
});
Expand Down
Loading

0 comments on commit 6f89f21

Please sign in to comment.