// 其他工具函数 // 来源: u-charts.ts /** * H5 事件接口 */ export interface H5Event { offsetX: number; offsetY: number; mp: { changedTouches: Array<{ x: number; y: number }>; }; } /** * 触摸点接口 */ export interface TouchPoint { x?: number; y?: number; clientX?: number; clientY?: number; pageX?: number; pageY?: number; } /** * 图表选项接口(用于 getTouches) */ export interface ChartOptions { rotate?: boolean; height: number; pix: number; width?: number; } /** * DOM 事件接口 */ export interface DOMEvent { currentTarget?: { offsetTop?: number; }; } /** * 兼容 H5 点击事件 * 将 H5 的 offsetX/offsetY 转换为小程序格式的 changedTouches * @param e - H5 事件对象 * @returns 转换后的事件对象 */ export function getH5Offset(e: { offsetX: number; offsetY: number }): H5Event { (e as H5Event).mp = { changedTouches: [] }; (e as H5Event).mp.changedTouches.push({ x: e.offsetX, y: e.offsetY }); return e as H5Event; } /** * 曲线控制点接口 */ export interface CurveControlPoints { ctrA: { x: number | null; y: number | null; }; ctrB: { x: number | null; y: number | null; }; } /** * 点数组接口(用于 createCurveControlPoints) */ export interface PointArray extends Array<{ x: number; y: number }> {} /** * 创建曲线控制点 * 用于绘制平滑曲线(贝塞尔曲线) * @param points - 点数组 * @param i - 当前点索引 * @returns 曲线控制点 */ export function createCurveControlPoints(points: PointArray, i: number): CurveControlPoints { function isNotMiddlePoint(pts: PointArray, idx: number): boolean { if (pts[idx - 1] && pts[idx + 1]) { return pts[idx].y >= Math.max(pts[idx - 1].y, pts[idx + 1].y) || pts[idx].y <= Math.min(pts[idx - 1].y, pts[idx + 1].y); } else { return false; } } function isNotMiddlePointX(pts: PointArray, idx: number): boolean { if (pts[idx - 1] && pts[idx + 1]) { return pts[idx].x >= Math.max(pts[idx - 1].x, pts[idx + 1].x) || pts[idx].x <= Math.min(pts[idx - 1].x, pts[idx + 1].x); } else { return false; } } let a = 0.2; let b = 0.2; let pAx: number | null = null; let pAy: number | null = null; let pBx: number | null = null; let pBy: number | null = null; if (i < 1) { pAx = points[0].x + (points[1].x - points[0].x) * a; pAy = points[0].y + (points[1].y - points[0].y) * a; } else { pAx = points[i].x + (points[i + 1].x - points[i - 1].x) * a; pAy = points[i].y + (points[i + 1].y - points[i - 1].y) * a; } if (i > points.length - 3) { let last = points.length - 1; pBx = points[last].x - (points[last].x - points[last - 1].x) * b; pBy = points[last].y - (points[last].y - points[last - 1].y) * b; } else { pBx = points[i + 1].x - (points[i + 2].x - points[i].x) * b; pBy = points[i + 1].y - (points[i + 2].y - points[i].y) * b; } if (isNotMiddlePoint(points, i + 1)) { pBy = points[i + 1].y; } if (isNotMiddlePoint(points, i)) { pAy = points[i].y; } if (isNotMiddlePointX(points, i + 1)) { pBx = points[i + 1].x; } if (isNotMiddlePointX(points, i)) { pAx = points[i].x; } if (pAy >= Math.max(points[i].y, points[i + 1].y) || pAy <= Math.min(points[i].y, points[i + 1].y)) { pAy = points[i].y; } if (pBy >= Math.max(points[i].y, points[i + 1].y) || pBy <= Math.min(points[i].y, points[i + 1].y)) { pBy = points[i + 1].y; } if (pAx >= Math.max(points[i].x, points[i + 1].x) || pAx <= Math.min(points[i].x, points[i + 1].x)) { pAx = points[i].x; } if (pBx >= Math.max(points[i].x, points[i + 1].x) || pBx <= Math.min(points[i].x, points[i + 1].x)) { pBx = points[i + 1].x; } return { ctrA: { x: pAx, y: pAy }, ctrB: { x: pBx, y: pBy } }; } /** * 获取触摸点坐标 * 支持小程序和 H5 两种格式 * @param touches - 触摸点数组或单个触摸点 * @param opts - 图表选项 * @param e - DOM 事件对象 * @returns 触摸点坐标 */ export function getTouches( touches: TouchPoint | TouchPoint[], opts: ChartOptions, e: DOMEvent ): TouchPoint { let x: number, y: number; const touch = Array.isArray(touches) ? touches[0] : touches; if (touch.clientX) { if (opts.rotate) { y = opts.height - touch.clientX! * opts.pix; x = (touch.pageY! - (e.currentTarget?.offsetTop || 0) - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix; } else { x = touch.clientX! * opts.pix; y = (touch.pageY! - (e.currentTarget?.offsetTop || 0) - (opts.height / opts.pix / 2) * (opts.pix - 1)) * opts.pix; } } else { if (opts.rotate) { y = opts.height - touch.x! * opts.pix; x = touch.y! * opts.pix; } else { x = touch.x! * opts.pix; y = touch.y! * opts.pix; } } return { x: x, y: y }; }