背景
绘制柱状图、折线图的时候,需要根据数据,动态生成刻度的区间
需求
刻度美观、数据最大的柱状图落在顶部的区间,刻度的数量是固定的。
结果预览
固定5个刻度区间,数据最大值与刻度之间的偏移对比如下
输入 | 每块偏移 | 第1段 | 第2段 | 第3段 | 第4段 | 第5段 |
---|---|---|---|---|---|---|
4 | 1 | 1 | 2 | 3 | 4 | 5 |
27 | 6 | 6 | 12 | 18 | 24 | 30 |
174 | 35 | 35 | 70 | 105 | 140 | 175 |
13500 | 3000 | 3000 | 6000 | 9000 | 12000 | 15000 |
思路
区间之间的偏移量,为了美观,定义如下
单位数偏移量1~9,参考图表4、27输入
两位数开始:偏移量第二位是5的倍数,第二位以后0,如10、15、20...95 ;三位数100、150、200...950,参考图表174、13500输入
实现算法
核心就是根据最大值/区间
得出偏移量,再循环从美观的偏移量里面找到离该值最近的一个大偏移量。
代码是基于java,但是对平台的依赖很少,很容易改改就移植到ios、.net上
/**
* 根据位数创建第一个为1的数字
* <p>
* 2700 =>1000
*
* @param delta
* @return
*/
public static int refreshNumber(double delta) {
int i = 1;
while (delta >= 10) {
delta /= 10;
i *= 10;
}
return i;
}
/**
* 获取刻度的区间
*
* @param rangeCount 一共几个刻度从底部0开始算,比如0,20,40,60,80,100算6个,之所以不算5个是为了兼容后续的优化
* @param maxValue 最大值
* @return
*/
public static List<Integer> getChartRange(int rangeCount, double maxValue) {
int resultDelta = 0;
boolean isFound = false;
double delta = maxValue / (rangeCount - 1);
if (delta == 0) delta = 1;
//从1开始,跳过重复计算,比如delta是2700,则shift从1000开始
int shift = refreshNumber(delta);
while (true) {
//个位数从1~9
//从10位开始,从10~95 以此类推100~950
for (float i = 0; i < 10; i += 0.5f) {
//个位数时9.5会强转成9,相当于9计算了两次,因此不会产生浮点刻度的问题
resultDelta = (int) (i * shift);
//找到离自己最大的一个推荐刻度
if (delta <= resultDelta) {
isFound = true;
break;
}
}
if (isFound) break;
shift *= 10;
}
List<Integer> list = new ArrayList<>(rangeCount);
for (int i = 0; i < rangeCount; i++) {
list.add(i * resultDelta);
}
return list;
}
后续优化
- 对浮点型支持,这个很好做,主要是把
shift
改成0.1f就好了。 - 对于差异比较小的大数据进行对比,比如一组数据:331000,331100,331200;这个如果从0开始算的话,视觉上差异很小,这个时候如果换成从331000开始计算,这样对比差异就很直观。方法里我预留了第0个的位置的初衷就是如此。