retween
v0.1.2
Published
A functional reimagining of the Shifty interpolation kernel
Downloads
2
Readme
retween
A functional take on the interpolation kernel of Shifty. Ideally something highly-performant that Shifty proper and/or Rekapi could use. Optimized for fast repeated execution of similarly-shaped interpolations. Explicitly not optimized for end-user ease-of-use; it should generally be wrapped with something else.
Functional inspiration taken from Redux.
Design notes
Interpolators
createInterpolator: (state, easing) => interpolator
Creates and returns an interpolator function for a state shaped like
state
and using the easing functions in easing
. The interpolator
function has the signature:
interpolator: (fromState, toState, position, outputState = {}) => outputState
fromState
and toState
must be objects with the same shape as the
state
argument to createInterpolator
, and all values being
number
s. position
must be a number between 0.0
and 1.0
inclusive. The interpolated result is copied into the object provided
in outputState
and returned.
Preprocessors
A preprocessor is a function with the following signature:
preprocessor: (inputState, inputEasing) => [outputState, outputEasing, decoder]
inputState
is an object with zero or more properties, and
inputEasing
is an object containing easing functions for properties
in inputState
. The preprocessor must return an outputState
which
is "closer" to a state accepted by an interpolator
, an
outputEasing
for the outputState
, and a decoder
function which
will perform a reverse transformation on a state shaped like
outputState
to produce a state shaped like inputShape
. The
preprocessor should pass properties it does not deal with through
verbatim.
The signature of decoder
is:
decoder: (outputState) => inputState
Composing preprocessors
Preprocessors may be composed with the following function:
composePreprocessors: (...preprocessors) => preprocessor
This will compose the preprocessors in the ...preprocessors
arguments right-to-left, returning a new preprocessor
which performs
the composed transformation.
Usage strawman
import { createInterpolator, linear } from 'retween';
const fromState = { x: 1.0, y: 3.0 };
const toState = { x: 4.0, y: 1.0 };
const easing = { x: linear, y: linear };
const interpolator = createInterpolator(toState, easing);
interpolator(fromState, toState, 0.5); // { x: 2.5, y: 2.0 }
// Works for any states shaped like toState:
interpolator({ x: 0, y: 0 }, { x: 1, y: 1 }, 0.25); // { x: 0.25, y: 0.25 }
import { tokenPreprocessor } from 'retween-token-preprocessor';
const customEasing = (pos) -> Math.pow(pos, 2);
const [ tokenState, tokenEasing, tokenDecoder ] =
tokenPreprocessor({ transform: 'translateX(10px) rotate(10deg)' },
{ transform: [ linear, customEasing ] });
// tokenState ~= {
// transform__0: 10,
// transform__1: 10,
// }
// tokenEasing ~= {
// transform__0: [Function linear],
// transform__1: [Function customEasing],
// }
tokenDecoder({ transform__0: 2.3, transform__1: 57 });
// 'translateX(2.3px) rotate(57deg)'
import { easingFunctions } from 'retween-easing-functions';
import { createEasingPreprocessor } from 'retween-easing-preprocessor';
const easingPreprocessor =
createEeasingPreprocessor({ ...easingFunctions, customEasing });
const [ easedState, easedEasing, easedDecoder ]
= easingPreprocessor(fromState, { x: 'customEasing', y: 'easeInOutSine' });
// easedState === fromState
// easedEasing ~= {
// x: [Function customEasing],
// y: [Function easeInOutSine],
// }
// easedDecoder === identity
import { composePreprocessors } from 'retween';
const easedTokenPP =
composePreprocessors(easingPreprocessor, tokenPreprocessor);
const [ easedTokenState, easedTokenEasing, easedTokenDecoder ] =
easedTokenPP({ transform: 'translateX(10px) rotate(10deg)' },
{ transform: [ 'linear', 'customEasing' ] });
// easedTokenState ~= {
// transform__0: 10,
// transform__1: 10,
// }
// easedTokenEasing ~= {
// transform__0: [Function linear],
// transform__1: [Function customEasing],
// }
// easedTokenDecoder === tokenDecoder
License
MIT.