utils.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import { animationType } from './types';
  2. import { maxTouchTime, defaultAnimationConfig } from './variables';
  3. /**
  4. * 是否为移动端设备
  5. */
  6. export const isMobile: boolean = window.navigator.userAgent.includes('Mobile');
  7. /**
  8. * 获取图片合适的大小
  9. * @param naturalWidth 图片真实宽度
  10. * @param naturalHeight 图片真实高度
  11. * @return 图片合适的大小
  12. */
  13. export const getSuitableImageSize = (
  14. naturalWidth: number,
  15. naturalHeight: number,
  16. ): {
  17. width: number;
  18. height: number;
  19. } => {
  20. let width = 0;
  21. let height = 0;
  22. const { innerWidth, innerHeight } = window;
  23. if (naturalWidth < innerWidth && naturalHeight < innerHeight) {
  24. width = naturalWidth;
  25. height = naturalHeight;
  26. } else if (naturalWidth < innerWidth && naturalHeight >= innerHeight) {
  27. width = (naturalWidth / naturalHeight) * innerHeight;
  28. height = innerHeight;
  29. } else if (naturalWidth >= innerWidth && naturalHeight < innerHeight) {
  30. width = innerWidth;
  31. height = (naturalHeight / naturalWidth) * innerWidth;
  32. } else if (
  33. naturalWidth >= innerWidth &&
  34. naturalHeight >= innerHeight &&
  35. naturalWidth / naturalHeight > innerWidth / innerHeight
  36. ) {
  37. width = innerWidth;
  38. height = (naturalHeight / naturalWidth) * innerWidth;
  39. } else {
  40. width = (naturalWidth / naturalHeight) * innerHeight;
  41. height = innerHeight;
  42. }
  43. return {
  44. width: Math.floor(width),
  45. height: Math.floor(height),
  46. };
  47. };
  48. export const getPositionOnScale = ({
  49. x,
  50. y,
  51. pageX,
  52. pageY,
  53. fromScale,
  54. toScale,
  55. }: {
  56. x: number;
  57. y: number;
  58. pageX: number;
  59. pageY: number;
  60. fromScale: number;
  61. toScale: number;
  62. }): {
  63. x: number;
  64. y: number;
  65. scale: number;
  66. } => {
  67. const { innerWidth, innerHeight } = window;
  68. let endScale = toScale;
  69. let nextX = x;
  70. let nextY = y;
  71. // 缩放限制
  72. if (toScale < 0.5) {
  73. endScale = 0.5;
  74. } else if (toScale > 5) {
  75. endScale = 5;
  76. } else {
  77. const centerPageX = innerWidth / 2;
  78. const centerPageY = innerHeight / 2;
  79. // 坐标偏移
  80. const lastPositionX = centerPageX + x;
  81. const lastPositionY = centerPageY + y;
  82. // 放大偏移量
  83. const offsetScale = endScale - fromScale;
  84. if (offsetScale > 0) {
  85. const scale = Math.abs(endScale / fromScale - 1);
  86. nextX = pageX - (pageX - lastPositionX) * Math.pow(2, scale) - centerPageX;
  87. nextY = pageY - (pageY - lastPositionY) * Math.pow(2, scale) - centerPageY;
  88. } else {
  89. nextX = pageX - (pageX - lastPositionX) / (1 - offsetScale) - centerPageX;
  90. nextY = pageY - (pageY - lastPositionY) / (1 - offsetScale) - centerPageY;
  91. }
  92. }
  93. return {
  94. x: nextX,
  95. y: nextY,
  96. scale: endScale,
  97. };
  98. };
  99. export const slideToPosition = ({
  100. x,
  101. y,
  102. lastX,
  103. lastY,
  104. touchedTime,
  105. }: {
  106. x: number;
  107. y: number;
  108. lastX: number;
  109. lastY: number;
  110. touchedTime: number;
  111. }): {
  112. endX: number;
  113. endY: number;
  114. } & animationType => {
  115. const moveTime = Date.now() - touchedTime;
  116. const speedX = (x - lastX) / moveTime;
  117. const speedY = (y - lastY) / moveTime;
  118. const maxSpeed = Math.max(speedX, speedY);
  119. const slideTime = moveTime < maxTouchTime ? Math.abs(maxSpeed) * 10 + 400 : 0;
  120. return {
  121. endX: Math.floor(x + speedX * slideTime),
  122. endY: Math.floor(y + speedY * slideTime),
  123. animation: {
  124. stiffness: 170,
  125. damping: 32,
  126. },
  127. };
  128. };
  129. /**
  130. * 跳转到合适的图片偏移量
  131. */
  132. export const jumpToSuitableOffset = ({
  133. x,
  134. y,
  135. lastX,
  136. lastY,
  137. width,
  138. height,
  139. scale,
  140. touchedTime,
  141. hasMove,
  142. }: {
  143. x: number;
  144. y: number;
  145. lastX: number;
  146. lastY: number;
  147. width: number;
  148. height: number;
  149. scale: number;
  150. touchedTime: number;
  151. hasMove: boolean;
  152. }): {
  153. x: number;
  154. y: number;
  155. } & animationType => {
  156. // 没有移动图片
  157. if (!hasMove) {
  158. return {
  159. x,
  160. y,
  161. animation: defaultAnimationConfig,
  162. };
  163. }
  164. const { innerWidth, innerHeight } = window;
  165. // 图片超出的长度
  166. const outOffsetX = (width * scale - innerWidth) / 2;
  167. const outOffsetY = (height * scale - innerHeight) / 2;
  168. // 滑动到结果的位置
  169. const { endX, endY, animation } = slideToPosition({ x, y, lastX, lastY, touchedTime });
  170. let currentX = endX;
  171. let currentY = endY;
  172. if (width * scale <= innerWidth) {
  173. currentX = 0;
  174. } else if (endX > 0 && outOffsetX - endX <= 0) {
  175. currentX = outOffsetX;
  176. } else if (endX < 0 && outOffsetX + endX <= 0) {
  177. currentX = -outOffsetX;
  178. }
  179. if (height * scale <= innerHeight) {
  180. currentY = 0;
  181. } else if (endY > 0 && outOffsetY - endY <= 0) {
  182. currentY = outOffsetY;
  183. } else if (endY < 0 && outOffsetY + endY <= 0) {
  184. currentY = -outOffsetY;
  185. }
  186. const isSlide = currentX === endX || currentY === endY;
  187. return {
  188. x: currentX,
  189. y: currentY,
  190. animation: isSlide ? defaultAnimationConfig : animation,
  191. };
  192. };