Skip to content

Commit

Permalink
Add support for interpolation precision; change spring() format
Browse files Browse the repository at this point in the history
This changes the format of `spring(val, [stiffness, damping])` to
`spring(val, {stiffness, damping, onRest, precision})`.

Docs of this breaking change coming soon, but tldr: second argument now
an object (still optional). `{stiffness: 120, damping: 12}` is clearer
than the previous `[120, 12]`. onRest is for the future end callback
(name subject to change, no implementation yet). Precision is the
rounding of interpolating value. All alltributes are optionals and have
sensible defaults.

More on precision: #100. We don't actually round the numbers; just a
check on whether the destination delta and speed is smaller than a
certain threshold. Rounding the number during interpolation _might not_
be very useful. The main concern is that we can optimize by not
rendering, when the number's so small that it doesn't change. But I'm
not sure the probability of every (rounded) value in `style` not
changing, at any frame, compared to the last, is high enough to justify
the overhead of checking it every frame. We'll see.
  • Loading branch information
chenglou committed Jan 19, 2016
1 parent eca4fa3 commit 1be045c
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 30 deletions.
3 changes: 3 additions & 0 deletions src/Types.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export type SpringConfig = {
val: number,
stiffness: number,
damping: number,
precision: number,
onRest?: (() => void),
};
export type Style = {
[key: string]: number | SpringConfig,
Expand All @@ -13,6 +15,7 @@ export type CurrentStyle = {
export type Velocity = {
[key: string]: number,
};
// TODO: gather all the public types here
export type TransitionStyles = {
[key: string]: Style,
};
2 changes: 2 additions & 0 deletions src/makeMotion.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export default function makeMotion(React: Object): Object {
propsStyle[key].val,
propsStyle[key].stiffness,
propsStyle[key].damping,
propsStyle[key].precision,
);

newLastIdealStyle[key] = interpolated[0];
Expand All @@ -204,6 +205,7 @@ export default function makeMotion(React: Object): Object {
propsStyle[key].val,
propsStyle[key].stiffness,
propsStyle[key].damping,
propsStyle[key].precision,
);

newCurrentStyle[key] =
Expand Down
2 changes: 2 additions & 0 deletions src/makeStaggeredMotion.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export default function makeStaggeredMotion(React: Object): Object {
destStyle[key].val,
destStyle[key].stiffness,
destStyle[key].damping,
destStyle[key].precision,
);

newLastIdealStyle[key] = interpolated[0];
Expand All @@ -235,6 +236,7 @@ export default function makeStaggeredMotion(React: Object): Object {
destStyle[key].val,
destStyle[key].stiffness,
destStyle[key].damping,
destStyle[key].precision,
);

newCurrentStyle[key] =
Expand Down
2 changes: 2 additions & 0 deletions src/makeTransitionMotion.js
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ export default function makeTransitionMotion(React: Object): Object {
destStyle[key].val,
destStyle[key].stiffness,
destStyle[key].damping,
destStyle[key].precision,
);

newLastIdealStyle[key] = interpolated[0];
Expand All @@ -425,6 +426,7 @@ export default function makeTransitionMotion(React: Object): Object {
destStyle[key].val,
destStyle[key].stiffness,
destStyle[key].damping,
destStyle[key].precision,
);

newCurrentStyle[key] =
Expand Down
9 changes: 4 additions & 5 deletions src/presets.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* @flow */
// [stiffness, damping]
export default {
noWobble: [170, 26], // the default
gentle: [120, 14],
wobbly: [180, 12],
stiff: [210, 20],
noWobble: {stiffness: 170, damping: 26}, // the default
gentle: {stiffness: 120, damping: 14},
wobbly: {stiffness: 180, damping: 12},
stiff: {stiffness: 210, damping: 20},
};
22 changes: 14 additions & 8 deletions src/spring.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
import presets from './presets';
import type {SpringConfig} from './Types';

export default function spring(
val: number,
config?: [number, number] = presets.noWobble): SpringConfig {
return {
val,
stiffness: config[0],
damping: config[1],
};
type SpringHelperConfig = {
stiffness?: number,
damping?: number,
onRest?: () => void,
precision?: number,
};

const defaultConfig = {
...presets.noWobble,
precision: 0.001,
};

export default function spring(val: number, config?: SpringHelperConfig): SpringConfig {
return {...defaultConfig, ...config, val};
}
13 changes: 6 additions & 7 deletions src/stepper.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/* @flow */
const errorMargin = 0.001;

export default function stepper(
frameRate: number,
secondPerFrame: number,
x: number,
v: number,
destX: number,
k: number,
b: number): [number, number] {
b: number,
precision: number): [number, number] {
// Spring stiffness, in kg / s^2

// for animations, destX is really spring length (spring at rest). initial
Expand All @@ -22,10 +21,10 @@ export default function stepper(
// let a = (Fspring + Fdamper) / mass;
const a = Fspring + Fdamper;

const newV = v + a * frameRate;
const newX = x + newV * frameRate;
const newV = v + a * secondPerFrame;
const newX = x + newV * secondPerFrame;

if (Math.abs(newV) < errorMargin && Math.abs(newX - destX) < errorMargin) {
if (Math.abs(newV) < precision && Math.abs(newX - destX) < precision) {
return [destX, 0];
}

Expand Down
13 changes: 10 additions & 3 deletions test/makeMotion-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,9 @@ describe('Motion', () => {
const App = React.createClass({
render() {
return (
<Motion defaultStyle={{a: 0}} style={{a: spring(10, [100, 50])}}>
<Motion
defaultStyle={{a: 0}}
style={{a: spring(10, {stiffness: 100, damping: 50, precision: 16})}}>
{({a}) => {
count.push(a);
return null;
Expand All @@ -206,12 +208,17 @@ describe('Motion', () => {
});
TestUtils.renderIntoDocument(<App />);

expect(count).toEqual([0]);
mockRaf.step(2);
mockRaf.step(99);
expect(count).toEqual([
0,
0.2777777777777778,
0.5941358024691358,
0.9081361454046639,
1.213021309632678,
1.5079182450697726,
1.7929588941684615,
2.0684390330691236,
10,
]);
});

Expand Down
11 changes: 8 additions & 3 deletions test/makeStaggeredMotion-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('StaggeredMotion', () => {
return (
<StaggeredMotion
defaultStyles={[{a: 0}]}
styles={() => [{a: spring(10, [100, 50])}]}>
styles={() => [{a: spring(10, {stiffness: 100, damping: 50, precision: 16})}]}>
{([{a}]) => {
count.push(a);
return null;
Expand All @@ -95,12 +95,17 @@ describe('StaggeredMotion', () => {
});
TestUtils.renderIntoDocument(<App />);

expect(count).toEqual([0]);
mockRaf.step(2);
mockRaf.step(99);
expect(count).toEqual([
0,
0.2777777777777778,
0.5941358024691358,
0.9081361454046639,
1.213021309632678,
1.5079182450697726,
1.7929588941684615,
2.0684390330691236,
10,
]);
});

Expand Down
12 changes: 8 additions & 4 deletions test/makeTransitionMotion-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('TransitionMotion', () => {
return (
<TransitionMotion
defaultStyles={{key: {a: 0}}}
styles={{key: {a: spring(10, [100, 50])}}}>
styles={{key: {a: spring(10, {stiffness: 100, damping: 50, precision: 16})}}}>
{({key: {a}}) => {
count.push(a);
return null;
Expand All @@ -92,13 +92,17 @@ describe('TransitionMotion', () => {
});
TestUtils.renderIntoDocument(<App />);

expect(count).toEqual([0]);
// Move "time" by 8 steps, which is equivalent to 8 calls to `raf`
mockRaf.step(2);
mockRaf.step(99);
expect(count).toEqual([
0,
0.2777777777777778,
0.5941358024691358,
0.9081361454046639,
1.213021309632678,
1.5079182450697726,
1.7929588941684615,
2.0684390330691236,
10,
]);
});

Expand Down

1 comment on commit 1be045c

@appsforartists
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 👏

Please sign in to comment.