misc.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. // 其他工具函数
  2. // 来源: u-charts.ts
  3. /**
  4. * H5 事件接口
  5. */
  6. export interface H5Event {
  7. offsetX: number;
  8. offsetY: number;
  9. mp: {
  10. changedTouches: Array<{ x: number; y: number }>;
  11. };
  12. }
  13. /**
  14. * 触摸点接口
  15. */
  16. export interface TouchPoint {
  17. x?: number;
  18. y?: number;
  19. clientX?: number;
  20. clientY?: number;
  21. pageX?: number;
  22. pageY?: number;
  23. }
  24. /**
  25. * 图表选项接口(用于 getTouches)
  26. */
  27. export interface ChartOptions {
  28. rotate?: boolean;
  29. height: number;
  30. pix: number;
  31. width?: number;
  32. }
  33. /**
  34. * DOM 事件接口
  35. */
  36. export interface DOMEvent {
  37. currentTarget?: {
  38. offsetTop?: number;
  39. };
  40. }
  41. /**
  42. * 兼容 H5 点击事件
  43. * 将 H5 的 offsetX/offsetY 转换为小程序格式的 changedTouches
  44. * @param e - H5 事件对象
  45. * @returns 转换后的事件对象
  46. */
  47. export function getH5Offset(e: { offsetX: number; offsetY: number }): H5Event {
  48. (e as H5Event).mp = {
  49. changedTouches: []
  50. };
  51. (e as H5Event).mp.changedTouches.push({
  52. x: e.offsetX,
  53. y: e.offsetY
  54. });
  55. return e as H5Event;
  56. }
  57. /**
  58. * 曲线控制点接口
  59. */
  60. export interface CurveControlPoints {
  61. ctrA: {
  62. x: number | null;
  63. y: number | null;
  64. };
  65. ctrB: {
  66. x: number | null;
  67. y: number | null;
  68. };
  69. }
  70. /**
  71. * 点数组接口(用于 createCurveControlPoints)
  72. */
  73. export interface PointArray extends Array<{ x: number; y: number }> {}
  74. /**
  75. * 创建曲线控制点
  76. * 用于绘制平滑曲线(贝塞尔曲线)
  77. * @param points - 点数组
  78. * @param i - 当前点索引
  79. * @returns 曲线控制点
  80. */
  81. export function createCurveControlPoints(points: PointArray, i: number): CurveControlPoints {
  82. function isNotMiddlePoint(pts: PointArray, idx: number): boolean {
  83. if (pts[idx - 1] && pts[idx + 1]) {
  84. return pts[idx].y >= Math.max(pts[idx - 1].y, pts[idx + 1].y) || pts[idx].y <= Math.min(pts[idx - 1].y,
  85. pts[idx + 1].y);
  86. } else {
  87. return false;
  88. }
  89. }
  90. function isNotMiddlePointX(pts: PointArray, idx: number): boolean {
  91. if (pts[idx - 1] && pts[idx + 1]) {
  92. return pts[idx].x >= Math.max(pts[idx - 1].x, pts[idx + 1].x) || pts[idx].x <= Math.min(pts[idx - 1].x,
  93. pts[idx + 1].x);
  94. } else {
  95. return false;
  96. }
  97. }
  98. let a = 0.2;
  99. let b = 0.2;
  100. let pAx: number | null = null;
  101. let pAy: number | null = null;
  102. let pBx: number | null = null;
  103. let pBy: number | null = null;
  104. if (i < 1) {
  105. pAx = points[0].x + (points[1].x - points[0].x) * a;
  106. pAy = points[0].y + (points[1].y - points[0].y) * a;
  107. } else {
  108. pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a;
  109. pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a;
  110. }
  111. if (i > points.length - 3) {
  112. let last = points.length - 1;
  113. pBx = points[last].x - (points[last].x - points[last - 1].x) * b;
  114. pBy = points[last].y - (points[last].y - points[last - 1].y) * b;
  115. } else {
  116. pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b;
  117. pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b;
  118. }
  119. if (isNotMiddlePoint(points, i + 1)) {
  120. pBy = points[i + 1].y;
  121. }
  122. if (isNotMiddlePoint(points, i)) {
  123. pAy = points[i].y;
  124. }
  125. if (isNotMiddlePointX(points, i + 1)) {
  126. pBx = points[i + 1].x;
  127. }
  128. if (isNotMiddlePointX(points, i)) {
  129. pAx = points[i].x;
  130. }
  131. if (pAy >= Math.max(points[i].y, points[i + 1].y) || pAy <= Math.min(points[i].y, points[i + 1].y)) {
  132. pAy = points[i].y;
  133. }
  134. if (pBy >= Math.max(points[i].y, points[i + 1].y) || pBy <= Math.min(points[i].y, points[i + 1].y)) {
  135. pBy = points[i + 1].y;
  136. }
  137. if (pAx >= Math.max(points[i].x, points[i + 1].x) || pAx <= Math.min(points[i].x, points[i + 1].x)) {
  138. pAx = points[i].x;
  139. }
  140. if (pBx >= Math.max(points[i].x, points[i + 1].x) || pBx <= Math.min(points[i].x, points[i + 1].x)) {
  141. pBx = points[i + 1].x;
  142. }
  143. return {
  144. ctrA: {
  145. x: pAx,
  146. y: pAy
  147. },
  148. ctrB: {
  149. x: pBx,
  150. y: pBy
  151. }
  152. };
  153. }
  154. /**
  155. * 获取触摸点坐标
  156. * 支持小程序和 H5 两种格式
  157. * @param touches - 触摸点数组或单个触摸点
  158. * @param opts - 图表选项
  159. * @param e - DOM 事件对象
  160. * @returns 触摸点坐标
  161. */
  162. export function getTouches(
  163. touches: TouchPoint | TouchPoint[],
  164. opts: ChartOptions,
  165. e: DOMEvent
  166. ): TouchPoint {
  167. let x: number, y: number;
  168. const touch = Array.isArray(touches) ? touches[0] : touches;
  169. if (touch.clientX) {
  170. if (opts.rotate) {
  171. y = opts.height - touch.clientX! * opts.pix;
  172. x = (touch.pageY! - (e.currentTarget?.offsetTop || 0) - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix;
  173. } else {
  174. x = touch.clientX! * opts.pix;
  175. y = (touch.pageY! - (e.currentTarget?.offsetTop || 0) - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix;
  176. }
  177. } else {
  178. if (opts.rotate) {
  179. y = opts.height - touch.x! * opts.pix;
  180. x = touch.y! * opts.pix;
  181. } else {
  182. x = touch.x! * opts.pix;
  183. y = touch.y! * opts.pix;
  184. }
  185. }
  186. return {
  187. x: x,
  188. y: y
  189. };
  190. }