@cloudcome/utils-core
Version:
cloudcome core utils
152 lines (151 loc) • 5.08 kB
JavaScript
const NEWTON_ITERATIONS = 4;
const NEWTON_MIN_SLOPE = 1e-3;
const SUBDIVISION_PRECISION = 1e-7;
const SUBDIVISION_MAX_ITERATIONS = 10;
const kSplineTableSize = 11;
const kSampleStepSize = 1 / (kSplineTableSize - 1);
const float32ArraySupported = typeof Float32Array === "function";
function A(aA1, aA2) {
return 1 - 3 * aA2 + 3 * aA1;
}
function B(aA1, aA2) {
return 3 * aA2 - 6 * aA1;
}
function C(aA1) {
return 3 * aA1;
}
function calcBezier(aT, aA1, aA2) {
return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
}
function getSlope(aT, aA1, aA2) {
return 3 * A(aA1, aA2) * aT * aT + 2 * B(aA1, aA2) * aT + C(aA1);
}
function binarySubdivide(aX, aA, aB, mX1, mX2) {
let currentX;
let currentT;
let i = 0;
let aBFinal = aB;
let aAFinal = aA;
do {
currentT = aAFinal + (aBFinal - aAFinal) / 2;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0) {
aBFinal = currentT;
} else {
aAFinal = currentT;
}
} while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
return currentT;
}
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
let aGuessTFinal = aGuessT;
for (let i = 0; i < NEWTON_ITERATIONS; ++i) {
const currentSlope = getSlope(aGuessTFinal, mX1, mX2);
if (currentSlope === 0) {
return aGuessTFinal;
}
const currentX = calcBezier(aGuessTFinal, mX1, mX2) - aX;
aGuessTFinal -= currentX / currentSlope;
}
return aGuessTFinal;
}
function LinearEasing(x) {
return x;
}
function createEasingFn(x1, y1, x2, y2) {
if (!(0 <= x1 && x1 <= 1 && 0 <= x2 && x2 <= 1)) {
throw new Error("bezier x values must be in [0, 1] range");
}
if (x1 === y1 && x2 === y2) {
return LinearEasing;
}
const sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, x1, x2);
}
function getTForX(aX) {
let intervalStart = 0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
const dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
const initialSlope = getSlope(guessForT, x1, x2);
if (initialSlope >= NEWTON_MIN_SLOPE) {
return newtonRaphsonIterate(aX, guessForT, x1, x2);
}
if (initialSlope === 0) {
return guessForT;
}
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, x1, x2);
}
return function easingFunc(x) {
if (x === 0 || x === 1) {
return x;
}
return calcBezier(getTForX(x), y1, y2);
};
}
const easingEase = createEasingFn(0.25, 0.1, 0.25, 1);
const easingLinear = createEasingFn(0, 0, 1, 1);
const easingSnap = createEasingFn(0, 1, 0.5, 1);
const easingIn = createEasingFn(0.42, 0, 1, 1);
const easingOut = createEasingFn(0, 0, 0.58, 1);
const easingInOut = createEasingFn(0.42, 0, 0.58, 1);
const easingInQuad = createEasingFn(0.55, 0.085, 0.68, 0.53);
const easingInCubic = createEasingFn(0.55, 0.055, 0.675, 0.19);
const easingInQuart = createEasingFn(0.895, 0.03, 0.685, 0.22);
const easingInQuint = createEasingFn(0.755, 0.05, 0.855, 0.06);
const easingInSine = createEasingFn(0.47, 0, 0.745, 0.715);
const easingInExpo = createEasingFn(0.95, 0.05, 0.795, 0.035);
const easingInCirc = createEasingFn(0.6, 0.04, 0.98, 0.335);
const easingInBack = createEasingFn(0.6, -0.28, 0.735, 0.045);
const easingOutQuad = createEasingFn(0.25, 0.46, 0.45, 0.94);
const easingOutCubic = createEasingFn(0.215, 0.61, 0.355, 1);
const easingOutQuart = createEasingFn(0.165, 0.84, 0.44, 1);
const easingOutQuint = createEasingFn(0.23, 1, 0.32, 1);
const easingOutSine = createEasingFn(0.39, 0.575, 0.565, 1);
const easingOutExpo = createEasingFn(0.19, 1, 0.22, 1);
const easingOutCirc = createEasingFn(0.075, 0.82, 0.165, 1);
const easingOutBack = createEasingFn(0.175, 0.885, 0.32, 1.275);
const easingInOutQuart = createEasingFn(0.77, 0, 0.175, 1);
const easingInOutQuint = createEasingFn(0.86, 0, 0.07, 1);
const easingInOutSine = createEasingFn(0.445, 0.05, 0.55, 0.95);
const easingInOutExpo = createEasingFn(1, 0, 0, 1);
const easingInOutCirc = createEasingFn(0.785, 0.135, 0.15, 0.86);
const easingInOutBack = createEasingFn(0.68, -0.55, 0.265, 1.55);
export {
createEasingFn,
easingEase,
easingIn,
easingInBack,
easingInCirc,
easingInCubic,
easingInExpo,
easingInOut,
easingInOutBack,
easingInOutCirc,
easingInOutExpo,
easingInOutQuart,
easingInOutQuint,
easingInOutSine,
easingInQuad,
easingInQuart,
easingInQuint,
easingInSine,
easingLinear,
easingOut,
easingOutBack,
easingOutCirc,
easingOutCubic,
easingOutExpo,
easingOutQuad,
easingOutQuart,
easingOutQuint,
easingOutSine,
easingSnap
};
//# sourceMappingURL=easing.mjs.map