cubic-bezier.mjs 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import { noop } from 'motion-utils';
  2. /*
  3. Bezier function generator
  4. This has been modified from Gaëtan Renaudeau's BezierEasing
  5. https://github.com/gre/bezier-easing/blob/master/src/index.js
  6. https://github.com/gre/bezier-easing/blob/master/LICENSE
  7. I've removed the newtonRaphsonIterate algo because in benchmarking it
  8. wasn't noticiably faster than binarySubdivision, indeed removing it
  9. usually improved times, depending on the curve.
  10. I also removed the lookup table, as for the added bundle size and loop we're
  11. only cutting ~4 or so subdivision iterations. I bumped the max iterations up
  12. to 12 to compensate and this still tended to be faster for no perceivable
  13. loss in accuracy.
  14. Usage
  15. const easeOut = cubicBezier(.17,.67,.83,.67);
  16. const x = easeOut(0.5); // returns 0.627...
  17. */
  18. // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
  19. const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
  20. t;
  21. const subdivisionPrecision = 0.0000001;
  22. const subdivisionMaxIterations = 12;
  23. function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
  24. let currentX;
  25. let currentT;
  26. let i = 0;
  27. do {
  28. currentT = lowerBound + (upperBound - lowerBound) / 2.0;
  29. currentX = calcBezier(currentT, mX1, mX2) - x;
  30. if (currentX > 0.0) {
  31. upperBound = currentT;
  32. }
  33. else {
  34. lowerBound = currentT;
  35. }
  36. } while (Math.abs(currentX) > subdivisionPrecision &&
  37. ++i < subdivisionMaxIterations);
  38. return currentT;
  39. }
  40. function cubicBezier(mX1, mY1, mX2, mY2) {
  41. // If this is a linear gradient, return linear easing
  42. if (mX1 === mY1 && mX2 === mY2)
  43. return noop;
  44. const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
  45. // If animation is at start/end, return t without easing
  46. return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
  47. }
  48. export { cubicBezier };