import { AxisOptions, AxisPointerOptions, AxisPointerCustomOptions, DataItem } from '../../models'; import AtomComponent from '../../atomComponent'; import { merge, pointDetector, type } from '../../utils'; import { EventName, SeriesCategory } from '../../constants'; import { BaseToken } from '../../theme/index'; const name = 'axisPointer'; export default class AxisPointer extends AtomComponent { private static readonly defaultOptions: AxisPointerOptions = { show: true, type: 'line', }; protected seriesCategory = SeriesCategory.NONE; private options: AxisPointerOptions & AxisPointerCustomOptions; private selectedIndex = -1; private xAxisOptions: AxisOptions; private yAxisOptions: AxisOptions; private data: DataItem[]; constructor(chart, options: AxisPointerOptions) { super({ name, chart }); this.options = this.processOptions(AxisPointer.defaultOptions, options); const [data] = chart.dataset; this.data = data; this.bindInteraction(); } render() { this.drawAxisPointer(); // this.drawAxisPointerOfShadowType(); } private bindInteraction() { const { chart, canvas } = this; const callback = (e) => { const { data } = this; const { size: { width, height }, } = canvas; const { pointsInCartesian: point } = e; if (!(point.x >= 0 && point.x <= width && point.y >= 0 && point.y <= height)) return; let selectedIndex = 0; const { isVertical = true } = this.options; if (isVertical) { // 纵向 const xAxisTickGap = width / data.length; selectedIndex = data.findIndex((_, index) => pointDetector.isInRect(point, { x: 0 + index * xAxisTickGap, y: 0, width: xAxisTickGap, height, })); } else { // 横向 const yAxisTickGap = height / data.length; selectedIndex = data.findIndex((_, index) => pointDetector.isInRect(point, { x: 0, y: 0 + index * yAxisTickGap, width, height: yAxisTickGap, })); } if (selectedIndex < 0 || selectedIndex >= data.length) { return; } this.selectedIndex = selectedIndex; this.$emit(EventName.Repaint, { dirtyComps: [this] }); }; chart.getInteration().on(['tap', 'pan'], callback); } private drawAxisPointer() { const { selectedIndex, options } = this; if (selectedIndex < 0) return; this.xAxisOptions = this.chart?.getOption()?.xAxis[0]; this.yAxisOptions = this.chart?.getOption()?.yAxis[0]; // TODO: 多y轴时,这里需要做配置合并处理 switch (options.type) { case 'line': return this.drawAxisPointerOfLineType(); case 'shadow': return this.drawAxisPointerOfShadowType(); } } private drawAxisPointerOfLineType() { const { data, canvas: { size }, selectedIndex, xAxisOptions, } = this; const boundaryGap = !type.isEmpty(xAxisOptions) ? xAxisOptions.boundaryGap : true; const xAxisTickGapCount = data.length - (boundaryGap ? 0 : 1); const xAxisTickGap = size.width / xAxisTickGapCount; this.painter.draw('Line', { x1: xAxisTickGap * selectedIndex + (boundaryGap ? xAxisTickGap / 2 : 0), y1: 0, x2: xAxisTickGap * selectedIndex + (boundaryGap ? xAxisTickGap / 2 : 0), y2: size.height, type: 'dashed', strokeStyle: BaseToken?.COLOR?.GRAY_5 ?? '#aaa', }); } private drawAxisPointerOfShadowType() { const { data, canvas: { size }, selectedIndex, xAxisOptions, yAxisOptions, } = this; const { isVertical = true } = this.options; if (isVertical) { // 竖向 const boundaryGap = !type.isEmpty(xAxisOptions) ? xAxisOptions.boundaryGap : true; // boundaryGap 为true不是应该-1? const xAxisTickGapCount = data.length - (boundaryGap ? 0 : 1); const xAxisTickGap = size.width / xAxisTickGapCount; this.painter.draw('Rect', { x: xAxisTickGap * selectedIndex + (boundaryGap ? 0 : -xAxisTickGap / 2), y: 0, width: xAxisTickGap, height: size.height, fillStyle: BaseToken?.COLOR?.GRAY_OPACITY_5 ?? 'rgba(191, 224, 249, 0.2)', }); } else { // 横向 const boundaryGap = !type.isEmpty(yAxisOptions) ? yAxisOptions[0].boundaryGap : true; const yAxisTickGapCount = data.length - (boundaryGap ? 0 : 1); const yAxisTickGap = size.height / yAxisTickGapCount; this.painter.draw('Rect', { x: 0, y: yAxisTickGap * selectedIndex + (boundaryGap ? 0 : -yAxisTickGap / 2), width: size.width, height: yAxisTickGap, fillStyle: BaseToken?.COLOR?.GRAY_OPACITY_5 ?? 'rgba(191, 224, 249, 0.2)', }); } } private processOptions(defaultOptions, userOptions) { const options = merge(defaultOptions, userOptions); return options; } }