useGetGradientTransform.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import {
  2. SharedValue,
  3. interpolate,
  4. useDerivedValue,
  5. } from 'react-native-reanimated';
  6. import { useGetPositionRange } from './useGetPositionRange';
  7. import { useGetBoneDimensions } from './useGetBoneDimensions';
  8. import type {
  9. ICustomViewStyle,
  10. IComponentSize,
  11. ISkeletonProps,
  12. } from '../constants';
  13. type UseGetGradientTransformProps = Pick<
  14. ISkeletonProps,
  15. 'animationDirection'
  16. > & {
  17. componentSize: IComponentSize;
  18. boneLayout: ICustomViewStyle;
  19. animationValue: SharedValue<number>;
  20. };
  21. /**
  22. * Provides the animation values for the gradient transform
  23. * @componentSize is the size of the component
  24. * @boneLayout is the layout of the bone
  25. * @animationDirection is the direction of the animation
  26. * @animationValue is the value of the animation
  27. */
  28. export const useGetGradientTransform = ({
  29. componentSize,
  30. boneLayout,
  31. animationDirection,
  32. animationValue,
  33. }: UseGetGradientTransformProps): SharedValue<{
  34. translateX?: number;
  35. translateY?: number;
  36. rotate?: string;
  37. }> => {
  38. const getBoneDimensions = useGetBoneDimensions(componentSize);
  39. const getPositionRange = useGetPositionRange(componentSize);
  40. return useDerivedValue(() => {
  41. let transform = {};
  42. const { width, height } = getBoneDimensions(boneLayout);
  43. if (
  44. animationDirection === 'verticalTop' ||
  45. animationDirection === 'verticalDown' ||
  46. animationDirection === 'horizontalLeft' ||
  47. animationDirection === 'horizontalRight'
  48. ) {
  49. const interpolatedPosition = interpolate(
  50. animationValue.value,
  51. [0, 1],
  52. getPositionRange({ animationDirection, boneLayout }),
  53. );
  54. if (
  55. animationDirection === 'verticalTop' ||
  56. animationDirection === 'verticalDown'
  57. ) {
  58. transform = { translateY: interpolatedPosition };
  59. } else {
  60. transform = { translateX: interpolatedPosition };
  61. }
  62. } else if (
  63. animationDirection === 'diagonalDownRight' ||
  64. animationDirection === 'diagonalTopRight' ||
  65. animationDirection === 'diagonalDownLeft' ||
  66. animationDirection === 'diagonalTopLeft'
  67. ) {
  68. const diagonal = Math.sqrt(height * height + width * width);
  69. const mainDimension = Math.max(height, width);
  70. const oppositeDimension = mainDimension === width ? height : width;
  71. const diagonalAngle = Math.acos(mainDimension / diagonal);
  72. let rotateAngle =
  73. animationDirection === 'diagonalDownRight' ||
  74. animationDirection === 'diagonalTopLeft'
  75. ? Math.PI / 2 - diagonalAngle
  76. : Math.PI / 2 + diagonalAngle;
  77. const additionalRotate =
  78. animationDirection === 'diagonalDownRight' ||
  79. animationDirection === 'diagonalTopLeft'
  80. ? 2 * diagonalAngle
  81. : -2 * diagonalAngle;
  82. const distanceFactor = (diagonal + oppositeDimension) / 2;
  83. if (mainDimension === width && width !== height)
  84. rotateAngle += additionalRotate;
  85. const sinComponent = Math.sin(diagonalAngle) * distanceFactor;
  86. const cosComponent = Math.cos(diagonalAngle) * distanceFactor;
  87. let xOutputRange: number[];
  88. let yOutputRange: number[];
  89. if (
  90. animationDirection === 'diagonalDownRight' ||
  91. animationDirection === 'diagonalTopLeft'
  92. ) {
  93. xOutputRange =
  94. animationDirection === 'diagonalDownRight'
  95. ? [-sinComponent, sinComponent]
  96. : [sinComponent, -sinComponent];
  97. yOutputRange =
  98. animationDirection === 'diagonalDownRight'
  99. ? [-cosComponent, cosComponent]
  100. : [cosComponent, -cosComponent];
  101. } else {
  102. xOutputRange =
  103. animationDirection === 'diagonalDownLeft'
  104. ? [-sinComponent, sinComponent]
  105. : [sinComponent, -sinComponent];
  106. yOutputRange =
  107. animationDirection === 'diagonalDownLeft'
  108. ? [cosComponent, -cosComponent]
  109. : [-cosComponent, cosComponent];
  110. if (mainDimension === height && width !== height) {
  111. xOutputRange.reverse();
  112. yOutputRange.reverse();
  113. }
  114. }
  115. let translateX = interpolate(animationValue.value, [0, 1], xOutputRange);
  116. let translateY = interpolate(animationValue.value, [0, 1], yOutputRange);
  117. // swapping the translates if width is the main dim
  118. if (mainDimension === width) {
  119. [translateX, translateY] = [translateY, translateX];
  120. }
  121. const rotate = `${rotateAngle}rad`;
  122. transform = { translateX, translateY, rotate };
  123. }
  124. return transform;
  125. });
  126. };