retween v0.1.2
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) => interpolatorCreates 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 = {}) => outputStatefromState and toState must be objects with the same shape as the
state argument to createInterpolator, and all values being
numbers. 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) => inputStateComposing preprocessors
Preprocessors may be composed with the following function:
composePreprocessors: (...preprocessors) => preprocessorThis 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 === tokenDecoderLicense
MIT.