import { AxisSourceData } from '../../models/axis'; import { getFixedFloat, getFloatFixedLen } from '@utils/number'; interface ComputedAxisTickOptions { splitNumber?: number; // 期望的坐标轴刻度数量 // min?: number | string | Function, // max?: number | string | Function, // minInterval?: number, // maxInterval?: number, // interval?: number, } interface AxisTickInfo { axisSourceDatas: AxisSourceData[], options?: ComputedAxisTickOptions, isTickFromZero: Boolean } const defaultComputedAxisTickOptions: ComputedAxisTickOptions = { splitNumber: 5, }; // 计算标准刻度间隔 const computeStandardTickGap = (t) => { let standardTickGap = t; if (t <= 0.1) { standardTickGap = 0.1; } else if (t <= 0.2) { standardTickGap = 0.2; } else if (t <= 0.25) { standardTickGap = 0.25; } else if (t <= 0.5) { standardTickGap = 0.5; } else if (t < 1) { standardTickGap = 1; } else { standardTickGap = computeStandardTickGap(t / 10) * 10; } return standardTickGap; }; /** * TODO: valueAxis目前计算出了展示出的ticks数组,但是未来需要提供一条完整ticks(包含展示和隐藏的,通过isSampled的字段标识) */ const computeAxisTickData = (axisTickInfo: AxisTickInfo) => { const { axisSourceDatas, options = {}, isTickFromZero, } = axisTickInfo; const { splitNumber } = { ...defaultComputedAxisTickOptions, ...options }; // 原始数据中的最小值和最大值 const data = axisSourceDatas.map(item => +item.value || 0); let [minValue, maxValue] = (() => { const DEFAULT_INTERVAL = [0, 1]; if(data?.length > 1) return [Math.min(...data), Math.max(...data)] else if(data?.length === 1) { if(data?.[0] === 0) return DEFAULT_INTERVAL; return data?.[0] >= 0 ? [0, data?.[0]] : [data?.[0], 0]; }else { return DEFAULT_INTERVAL; } })(); // 刻度间隔,即两个刻度之间的步数 const rawTickGap = (maxValue - minValue) / (splitNumber - 1); // 计算原始刻度间隔的数量级 let mag = 10 ** Math.floor(Math.log10(rawTickGap)); if (mag === rawTickGap) { mag = rawTickGap; } else { mag *= 10; } let tickGap = Number((rawTickGap / mag).toFixed(6)) || 0; const stepLength = computeStandardTickGap(tickGap); tickGap = Number((stepLength * mag).toFixed(6)) || 0; /* 至此,通过一番折腾,可以计算出一个标准的刻度间隔tickGap */ /** * 最小值 * 特定情况下,需要强制min=0(如Bar,area图等) */ let tempMin = 0; if (minValue < 0) { while (tempMin > minValue) { tempMin -= tickGap; } } else { while (tempMin + tickGap < minValue) { tempMin += tickGap; } } minValue = isTickFromZero ? 0 : tempMin; // 刻度数 let tickCount = 1; while (tickCount * tickGap + minValue < maxValue) { tickCount += 1; } // 最大值 maxValue = minValue + tickCount * tickGap; // tick数组 let standardTicks: any[] = []; let i = 0; const fixedLength = getFloatFixedLen(tickGap) || 1; // 小数点后位数 while (i <= tickCount) { const tick = getFixedFloat(minValue + i * tickGap, fixedLength); standardTicks.push(tick); i += 1; } standardTicks = Array.from(new Set(standardTicks)); /** * 异常情况处理 * 1.isAxisFromZero为true 范围[0,tickMax] * 2.data 全部为0 */ if (minValue === 0 && maxValue === 0) { return { standardTicks: [0, 0.2, 0.4, 0.6, 0.8, 1], tickMax: 1, tickMin: 0, }; } return { standardTicks, tickMax: maxValue, tickMin: minValue, }; }; /** * 是否强制valueAxis minValue=0 * FIXME: area图也需要支持,但目前属于line图的一种,后续考虑如何支持 */ const checkIsTickFromZero = (series) => { const seriesType = series[0].type; return ['bar'].includes(seriesType); }; export { computeAxisTickData, checkIsTickFromZero };