cubic-bezier.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /*!
  2. * (C) Ionic http://ionicframework.com - MIT License
  3. */
  4. /**
  5. * Based on:
  6. * https://stackoverflow.com/questions/7348009/y-coordinate-for-a-given-x-cubic-bezier
  7. * https://math.stackexchange.com/questions/26846/is-there-an-explicit-form-for-cubic-b%C3%A9zier-curves
  8. */
  9. /**
  10. * EXPERIMENTAL
  11. * Given a cubic-bezier curve, get the x value (time) given
  12. * the y value (progression).
  13. * Ex: cubic-bezier(0.32, 0.72, 0, 1);
  14. * P0: (0, 0)
  15. * P1: (0.32, 0.72)
  16. * P2: (0, 1)
  17. * P3: (1, 1)
  18. *
  19. * If you give a cubic bezier curve that never reaches the
  20. * provided progression, this function will return an empty array.
  21. */
  22. const getTimeGivenProgression = (p0, p1, p2, p3, progression) => {
  23. return solveCubicBezier(p0[1], p1[1], p2[1], p3[1], progression).map((tValue) => {
  24. return solveCubicParametricEquation(p0[0], p1[0], p2[0], p3[0], tValue);
  25. });
  26. };
  27. /**
  28. * Solve a cubic equation in one dimension (time)
  29. */
  30. const solveCubicParametricEquation = (p0, p1, p2, p3, t) => {
  31. const partA = 3 * p1 * Math.pow(t - 1, 2);
  32. const partB = -3 * p2 * t + 3 * p2 + p3 * t;
  33. const partC = p0 * Math.pow(t - 1, 3);
  34. return t * (partA + t * partB) - partC;
  35. };
  36. /**
  37. * Find the `t` value for a cubic bezier using Cardano's formula
  38. */
  39. const solveCubicBezier = (p0, p1, p2, p3, refPoint) => {
  40. p0 -= refPoint;
  41. p1 -= refPoint;
  42. p2 -= refPoint;
  43. p3 -= refPoint;
  44. const roots = solveCubicEquation(p3 - 3 * p2 + 3 * p1 - p0, 3 * p2 - 6 * p1 + 3 * p0, 3 * p1 - 3 * p0, p0);
  45. return roots.filter((root) => root >= 0 && root <= 1);
  46. };
  47. const solveQuadraticEquation = (a, b, c) => {
  48. const discriminant = b * b - 4 * a * c;
  49. if (discriminant < 0) {
  50. return [];
  51. }
  52. else {
  53. return [(-b + Math.sqrt(discriminant)) / (2 * a), (-b - Math.sqrt(discriminant)) / (2 * a)];
  54. }
  55. };
  56. const solveCubicEquation = (a, b, c, d) => {
  57. if (a === 0) {
  58. return solveQuadraticEquation(b, c, d);
  59. }
  60. b /= a;
  61. c /= a;
  62. d /= a;
  63. const p = (3 * c - b * b) / 3;
  64. const q = (2 * b * b * b - 9 * b * c + 27 * d) / 27;
  65. if (p === 0) {
  66. return [Math.pow(-q, 1 / 3)];
  67. }
  68. else if (q === 0) {
  69. return [Math.sqrt(-p), -Math.sqrt(-p)];
  70. }
  71. const discriminant = Math.pow(q / 2, 2) + Math.pow(p / 3, 3);
  72. if (discriminant === 0) {
  73. return [Math.pow(q / 2, 1 / 2) - b / 3];
  74. }
  75. else if (discriminant > 0) {
  76. return [
  77. Math.pow(-(q / 2) + Math.sqrt(discriminant), 1 / 3) - Math.pow(q / 2 + Math.sqrt(discriminant), 1 / 3) - b / 3,
  78. ];
  79. }
  80. const r = Math.sqrt(Math.pow(-(p / 3), 3));
  81. const phi = Math.acos(-(q / (2 * Math.sqrt(Math.pow(-(p / 3), 3)))));
  82. const s = 2 * Math.pow(r, 1 / 3);
  83. return [
  84. s * Math.cos(phi / 3) - b / 3,
  85. s * Math.cos((phi + 2 * Math.PI) / 3) - b / 3,
  86. s * Math.cos((phi + 4 * Math.PI) / 3) - b / 3,
  87. ];
  88. };
  89. export { getTimeGivenProgression as g };