animare
Version:
Advanced animation library for modern JavaScript.
11 lines • 2.71 kB
JavaScript
/**
* 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)
const NEWTON_ITERATIONS=4,NEWTON_MIN_SLOPE=0.001,SUBDIVISION_PRECISION=0.0000001,SUBDIVISION_MAX_ITERATIONS=10,kSplineTableSize=11,kSampleStepSize=1.0/(kSplineTableSize-1.0);const A=(aA1,aA2)=>1.0-3.0*aA2+3.0*aA1;const B=(aA1,aA2)=>3.0*aA2-6.0*aA1;const C=aA1=>3.0*aA1;// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
const calcBezier=(aT,aA1,aA2)=>((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.
const getSlope=(aT,aA1,aA2)=>3.0*A(aA1,aA2)*aT*aT+2.0*B(aA1,aA2)*aT+C(aA1);function binarySubdivide(aX,aA,aB,mX1,mX2){let 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(let i=0;i<NEWTON_ITERATIONS;++i){const currentSlope=getSlope(aGuessT,mX1,mX2);if(currentSlope===0.0)return aGuessT;const currentX=calcBezier(aGuessT,mX1,mX2)-aX;aGuessT-=currentX/currentSlope;}return aGuessT;}const LinearEasing=x=>x;export default function cubicBezier(mX1,mY1,mX2,mY2){if(!(0<=mX1&&mX1<=1&&0<=mX2&&mX2<=1))throw new Error('/n/n⛔ [animare] ➡️ [ease] ➡️ [cubicBezier] : bezier x values must be in [0, 1] range. !!\n\n');if(mX1===mY1&&mX2===mY2)return LinearEasing;// Precompute samples table
const sampleValues=typeof Float32Array==='function'?new Float32Array(kSplineTableSize):new Array(kSplineTableSize);for(let i=0;i<kSplineTableSize;++i)sampleValues[i]=calcBezier(i*kSampleStepSize,mX1,mX2);function getTForX(aX){let intervalStart=0.0,currentSample=1;const lastSample=kSplineTableSize-1;for(;currentSample!==lastSample&&sampleValues[currentSample]<=aX;++currentSample)intervalStart+=kSampleStepSize;--currentSample;// Interpolate to provide an initial guess for t
const dist=(aX-sampleValues[currentSample])/(sampleValues[currentSample+1]-sampleValues[currentSample]),guessForT=intervalStart+dist*kSampleStepSize,initialSlope=getSlope(guessForT,mX1,mX2);if(initialSlope>=NEWTON_MIN_SLOPE)return newtonRaphsonIterate(aX,guessForT,mX1,mX2);if(initialSlope===0.0)return guessForT;return binarySubdivide(aX,intervalStart,intervalStart+kSampleStepSize,mX1,mX2);}return t=>{// Because JavaScript number are imprecise, we should guarantee the extremes are right.
if(t===0||t===1)return t;return calcBezier(getTForX(t),mY1,mY2);};}