constraints.mjs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import { progress } from 'motion-utils';
  2. import { calcLength } from '../../../projection/geometry/delta-calc.mjs';
  3. import { clamp } from '../../../utils/clamp.mjs';
  4. import { mixNumber } from '../../../utils/mix/number.mjs';
  5. /**
  6. * Apply constraints to a point. These constraints are both physical along an
  7. * axis, and an elastic factor that determines how much to constrain the point
  8. * by if it does lie outside the defined parameters.
  9. */
  10. function applyConstraints(point, { min, max }, elastic) {
  11. if (min !== undefined && point < min) {
  12. // If we have a min point defined, and this is outside of that, constrain
  13. point = elastic
  14. ? mixNumber(min, point, elastic.min)
  15. : Math.max(point, min);
  16. }
  17. else if (max !== undefined && point > max) {
  18. // If we have a max point defined, and this is outside of that, constrain
  19. point = elastic
  20. ? mixNumber(max, point, elastic.max)
  21. : Math.min(point, max);
  22. }
  23. return point;
  24. }
  25. /**
  26. * Calculate constraints in terms of the viewport when defined relatively to the
  27. * measured axis. This is measured from the nearest edge, so a max constraint of 200
  28. * on an axis with a max value of 300 would return a constraint of 500 - axis length
  29. */
  30. function calcRelativeAxisConstraints(axis, min, max) {
  31. return {
  32. min: min !== undefined ? axis.min + min : undefined,
  33. max: max !== undefined
  34. ? axis.max + max - (axis.max - axis.min)
  35. : undefined,
  36. };
  37. }
  38. /**
  39. * Calculate constraints in terms of the viewport when
  40. * defined relatively to the measured bounding box.
  41. */
  42. function calcRelativeConstraints(layoutBox, { top, left, bottom, right }) {
  43. return {
  44. x: calcRelativeAxisConstraints(layoutBox.x, left, right),
  45. y: calcRelativeAxisConstraints(layoutBox.y, top, bottom),
  46. };
  47. }
  48. /**
  49. * Calculate viewport constraints when defined as another viewport-relative axis
  50. */
  51. function calcViewportAxisConstraints(layoutAxis, constraintsAxis) {
  52. let min = constraintsAxis.min - layoutAxis.min;
  53. let max = constraintsAxis.max - layoutAxis.max;
  54. // If the constraints axis is actually smaller than the layout axis then we can
  55. // flip the constraints
  56. if (constraintsAxis.max - constraintsAxis.min <
  57. layoutAxis.max - layoutAxis.min) {
  58. [min, max] = [max, min];
  59. }
  60. return { min, max };
  61. }
  62. /**
  63. * Calculate viewport constraints when defined as another viewport-relative box
  64. */
  65. function calcViewportConstraints(layoutBox, constraintsBox) {
  66. return {
  67. x: calcViewportAxisConstraints(layoutBox.x, constraintsBox.x),
  68. y: calcViewportAxisConstraints(layoutBox.y, constraintsBox.y),
  69. };
  70. }
  71. /**
  72. * Calculate a transform origin relative to the source axis, between 0-1, that results
  73. * in an asthetically pleasing scale/transform needed to project from source to target.
  74. */
  75. function calcOrigin(source, target) {
  76. let origin = 0.5;
  77. const sourceLength = calcLength(source);
  78. const targetLength = calcLength(target);
  79. if (targetLength > sourceLength) {
  80. origin = progress(target.min, target.max - sourceLength, source.min);
  81. }
  82. else if (sourceLength > targetLength) {
  83. origin = progress(source.min, source.max - targetLength, target.min);
  84. }
  85. return clamp(0, 1, origin);
  86. }
  87. /**
  88. * Rebase the calculated viewport constraints relative to the layout.min point.
  89. */
  90. function rebaseAxisConstraints(layout, constraints) {
  91. const relativeConstraints = {};
  92. if (constraints.min !== undefined) {
  93. relativeConstraints.min = constraints.min - layout.min;
  94. }
  95. if (constraints.max !== undefined) {
  96. relativeConstraints.max = constraints.max - layout.min;
  97. }
  98. return relativeConstraints;
  99. }
  100. const defaultElastic = 0.35;
  101. /**
  102. * Accepts a dragElastic prop and returns resolved elastic values for each axis.
  103. */
  104. function resolveDragElastic(dragElastic = defaultElastic) {
  105. if (dragElastic === false) {
  106. dragElastic = 0;
  107. }
  108. else if (dragElastic === true) {
  109. dragElastic = defaultElastic;
  110. }
  111. return {
  112. x: resolveAxisElastic(dragElastic, "left", "right"),
  113. y: resolveAxisElastic(dragElastic, "top", "bottom"),
  114. };
  115. }
  116. function resolveAxisElastic(dragElastic, minLabel, maxLabel) {
  117. return {
  118. min: resolvePointElastic(dragElastic, minLabel),
  119. max: resolvePointElastic(dragElastic, maxLabel),
  120. };
  121. }
  122. function resolvePointElastic(dragElastic, label) {
  123. return typeof dragElastic === "number"
  124. ? dragElastic
  125. : dragElastic[label] || 0;
  126. }
  127. export { applyConstraints, calcOrigin, calcRelativeAxisConstraints, calcRelativeConstraints, calcViewportAxisConstraints, calcViewportConstraints, defaultElastic, rebaseAxisConstraints, resolveAxisElastic, resolveDragElastic, resolvePointElastic };