13. 横向类目轴(x轴)文字标签的跨度计算

类目轴标签过多将重叠显示。
为避免重叠,需要等间隔地隐藏部分标签。
不妨称其为”跨度”。

有两种思路来计算”跨度”:

  1. 粗略计算
  2. 精确计算


粗略计算

请用搜索引擎查找各类图表的图片,观察其类目轴标签文字的个数,有如下结果:
不同标签的文字个数相近,没有很大偏差。外观上达到美观、整齐的效果。

因此,计算思路为:

  1. 根据应用场景,事先确定标签文字最大个数
  2. 计算一个标签占据的宽度 w
    *参见【09. 度量文字宽高】
  3. 标签个数 = x轴长度 / w
  4. 根据[标签个数]计算标签跨度

这种思路简单易行,能满足大多数应用场景。
推荐优先考虑!


精确计算(穷举法)

类目轴标签间隔

  1. 计算所有标签的文字宽度
    *参见【09. 度量文字宽高】
  2. 计算单位类目占用的宽度 unit = x轴长度 / 类目个数
  3. 每个标签的中点和刻度对齐。
    初始状态下,标签跨度为0.
    在此前提下,判断1,2标签是否重叠。
  4. 如果不重叠,判断2,3标签是否重叠
  5. 如果重叠,说明标签跨度需要增加1(0+1=1)
  6. 在标签间隔为1的状态下,重新比较1,3标签是否重叠。
  7. 如果不重叠,判断3,5标签是否重叠
  8. 如果重叠,说明标签跨度需要增加1(1+1=2)
  9. 在标签间隔为2的状态下,重新比较1,4标签是否重叠。
  10. 如果不重叠,判断4,7标签是否重叠
  11. 以此类推,直至比较到最后

此方法需要递归比较,数据量大的时候,需要更多的时间来计算。
且递归深度受js语言自身限制,达到一定深度会报错。


示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>横向类目轴(x轴)文字标签的跨度计算</title>
</head>
<body>
<div id="A1" style="position:relative; width: 600px; height:300px; "></div>

<script>

var labels = [ '黑龙江' ,
'吉林' ,
'辽宁' ,
'内蒙古' ,
'新疆' ,
'西藏' ,
'青海' ,
'甘肃' ,
'陕西' ,
'山西' ,
'河南' ,
'河北' ,
'湖南' ,
'湖北' ] ,
font = '14px Microsoft YaHei' ,
size = [] ,
i , j ,
len = labels.length ,
x , y ,
w ,
label ,
axisWidth = 400 ,
unit = axisWidth / len ,
distance ,
categoryInterval ;


//动态生成canvas对象,并加入到div中
var parentDiv = document.getElementById( 'A1' );
var canvas = document.createElement('canvas');
canvas.width = parentDiv.clientWidth ;
canvas.height = parentDiv.clientHeight ;
parentDiv.appendChild(canvas);

//获取上下文,设置字体,这是度量文字宽度的必备条件
var context = canvas.getContext('2d');
context.font = font ;

//度量label宽度,并记录到数组中
size.length = 0 ;
for(i=0; i<len; i++)
{
label = labels[i] ;
w = context.measureText(label).width;
size[i] = w ;
}

//计算标签间隔
computeCategoryInterval(0) ;

//将结果绘制在canvas上,用于确认
paint() ;





//计算标签跨度
function computeCategoryInterval(interval)
{
i = 0 ;
j = interval + 1 ;

//循环遍历数组
while(i <len && j <len)
{
//比较相邻标签是否重叠
distance = unit*(j-i) - (size[j]+size[i])/2 ;

if(distance<0)
{
//如果重叠,间隔加一,然后递归
computeCategoryInterval( interval+1) ;
//结束本循环
return ;
}else{
//如果不重叠,为下次循环确定数组索引
i = j;
j = i + interval + 1 ;
}
}

//如果遍历结束,获取间隔
categoryInterval = interval ;
}


//绘制轴线,标签,用于确认计算结果
function paint()
{
context.save() ;

//绘制轴线
context.beginPath();
context.moveTo(50, 50);
context.lineTo(50+axisWidth, 50);
context.stroke();

//绘制刻度和标签
context.textBaseline = 'middle' ;
context.textAlign = 'center' ;

len = labels.length ;
for( i=0 ; i<len ; i+=categoryInterval +1)
{
x = 50 + unit * i ;

context.beginPath();
context.moveTo(x, 45);
context.lineTo(x , 50);
context.stroke();

context.fillText( labels[i], x, 60 ) ;
}

context.restore();
}

</script>

</body>
</html>