UNPKG

css-doodle

Version:

A web component for visual art & creative coding

75 lines (63 loc) 2.18 kB
import calc from './calc.js'; const EPSILON = 1e-6; const MAX_NEWTON = 8; const MAX_BISECT = 20; function cubicBezier(p1x, p1y, p2x, p2y) { const cx = 3 * p1x, bx = 3 * (p2x - p1x) - cx, ax = 1 - cx - bx; const cy = 3 * p1y, by = 3 * (p2y - p1y) - cy, ay = 1 - cy - by; const bezier = (a, b, c, t) => ((a * t + b) * t + c) * t; const deriv = (a, b, c, t) => (3 * a * t + 2 * b) * t + c; function solve(x) { let t = x; for (let i = 0; i < MAX_NEWTON; i++) { const err = bezier(ax, bx, cx, t) - x; if (Math.abs(err) < EPSILON) return t; const d = deriv(ax, bx, cx, t); if (Math.abs(d) < EPSILON) break; t = Math.max(0, Math.min(1, t - err / d)); } let lo = 0, hi = 1; for (let i = 0; i < MAX_BISECT; i++) { t = (lo + hi) / 2; const val = bezier(ax, bx, cx, t); if (Math.abs(val - x) < EPSILON) break; val < x ? lo = t : hi = t; } return t; } return x => x <= 0 ? 0 : x >= 1 ? 1 : bezier(ay, by, cy, solve(x)); } function createCustomEasing(formula) { return t => calc(formula, { t }); } export const linear = t => t; export const ease = cubicBezier(0.25, 0.1, 0.25, 1); export const easeIn = cubicBezier(0.42, 0, 1, 1); export const easeOut = cubicBezier(0, 0, 0.58, 1); export const easeInOut = cubicBezier(0.42, 0, 0.58, 1); const easingFunctions = { ease, easeIn, easeOut, easeInOut, linear }; function normalize(str) { return str.trim().toLowerCase().replace(/[-\s]/g, '') .replace(/(?<=ease|in|out)(in|out)/gi, s => s[0].toUpperCase() + s.slice(1)); } function parseCubicBezier(str) { if (!/^cubic-bezier\s*\(/i.test(str)) { return null; } const values = str.match(/-?[\d.]+/g)?.map(Number); if (values?.length === 4 && values.every(n => !isNaN(n))) { return values; } return null; } export function getEasingFunction(easing) { if (typeof easing === 'function') return easing; const str = String(easing).trim(); const preset = easingFunctions[normalize(str)]; if (preset) return preset; const bezierValues = parseCubicBezier(str); if (bezierValues) { return cubicBezier(...bezierValues); } return createCustomEasing(str); }