0%

HighCharts源码解析之Class体系

HighCharts源码解析之Class体系

HighCharts是一个默认使用SVG渲染器的图表引擎,其对内部的DOM元素都设置了Class属性以描述其类别和样式。起到了类似ECharts theme的效果。

image-20241127170443027

图表中的Dom结构及Class示例

以下是封面图的部分DOM结构以及Class名称。HighCharts基本对图表内的所有图元都定义了Class属性。其中的命名规律为以highcharts-作为前缀、辅以语义化的说明。

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
<figure class="highcharts-figure">
<div id="container" ...>
<div class="highcharts-announcer-container">
<div class="highcharts-container" ...>
<div class="highcharts-a11y-proxy-container-before" ...></div>
<svg version="1.1" class="highcharts-root" ...>
<rect class="highcharts-background" ...></rect>
<rect class="highcharts-plot-background" ...></rect>
<g class="highcharts-grid highcharts-xaxis-grid" ...>
<path class="highcharts-grid-line" ...></path>
<path class="highcharts-grid-line" ...></path>
<path class="highcharts-grid-line" ...></path>
...
</g>
<g class="highcharts-grid highcharts-yaxis-grid">
<path class="highcharts-grid-line" ...></path>
<path class="highcharts-grid-line" ...></path>
<path class="highcharts-grid-line" ...></path>
...
</g>
<rect class="highcharts-plot-border" ...></rect>
<g class="highcharts-axis highcharts-xaxis" ...>
<path class="highcharts-tick"></path>
<path class="highcharts-tick"></path>
<path class="highcharts-tick"></path>
。。。
</g>
<g class="highcharts-axis highcharts-yaxis" ...>
<text class="highcharts-axis-title" ...>Exchange rate</text>
<path class="highcharts-axis-line" ...></path>
</g>
<g class="highcharts-series-group" ...>
<g
class="highcharts-series highcharts-series-0 highcharts-area-series"
...
>
<path class="highcharts-area" ...></path>
<path class="highcharts-graph" ...></path>
<path class="highcharts-tracker-line" ...></path>
</g>
<g
class="highcharts-markers highcharts-series-0 highcharts-area-series highcharts-tracker"
...
>
<path
class="highcharts-halo highcharts-color-undefined"
...
></path>
<path class="highcharts-point" ...></path>
</g>
</g>
<g class="highcharts-exporting-group" ...>
<g
class="highcharts-no-tooltip highcharts-button highcharts-contextbutton"
...
>
<rect class="highcharts-button-box" ...></rect>
<path class="highcharts-button-symbol" ...></path>
</g>
</g>
<text class="highcharts-title">...</text>
<text class="highcharts-subtitle" ...>...</text>
<text class="highcharts-caption" ...></text>
<g
class="highcharts-no-tooltip highcharts-button highcharts-reset-zoom"
...
>
<rect class="highcharts-button-box" ...></rect>
</g>
<g class="highcharts-axis-labels highcharts-xaxis-labels" ...>
<text>...</text>
</g>
<g class="highcharts-axis-labels highcharts-yaxis-labels" ...>
<text>...</text>
</g>
<text class="highcharts-credits"></text>
<g
class="highcharts-label highcharts-tooltip highcharts-color-undefined"
...
>
<path
class="highcharts-label-box highcharts-tooltip-box"
...
></path>
<text></text>
</g>
</svg>
<div class="highcharts-a11y-proxy-container-after" ...>
<div
class="highcharts-a11y-proxy-group highcharts-a11y-proxy-group-zoom"
>
<button
class="highcharts-a11y-proxy-element highcharts-no-tooltip"
...
></button>
</div>
<div
class="highcharts-a11y-proxy-group highcharts-a11y-proxy-group-chartMenu"
>
<button
class="highcharts-a11y-proxy-element highcharts-no-tooltip"
...
></button>
</div>
</div>
</div>
<div id="highcharts-screen-reader-region-after-0" ...>
<div></div>
</div>
</div>
<p class="highcharts-description highcharts-linked-description"></p>
</figure>

HighChartsClass定义

Class都定义在了highcharts.css相关文件里, https://github1s.com/highcharts/highcharts/blob/master/css/highcharts.css。

使用css变量定义了一些主题颜色。并在各个class中使用了这些css变量。

CLass的添加

在创建具体图元的时候由负责创建该图元的方法通过链式调用的方式为其添加class名称。以下是两个简单示例:

1
2
3
4
5
6
7
if (!chartBackground) {
chart.chartBackground = chartBackground = renderer.rect()
.addClass('highcharts-background')
.add();
verb = 'attr';
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
series.tracker = tracker = renderer.path(trackerPath)
.attr({
visibility: series.visible ? 'inherit' : 'hidden',
zIndex: 2
})
.addClass(
trackByArea ?
'highcharts-tracker-area' :
'highcharts-tracker-line'
)
.add(series.group);

if (!chart.styledMode) {
tracker.attr({
'stroke-linecap': 'round',
'stroke-linejoin': 'round', // #1225
stroke: TRACKER_FILL,
fill: trackByArea ? TRACKER_FILL : 'none',
'stroke-width': series.graph.strokeWidth() +
(trackByArea ? 0 : 2 * snap)
});
}

总结

给每个图元都创建Class,并通过语意化的Class名称可以达到两个目的。

  1. 可以更容易的感知和分辨图元所表述具体内容,比如区分是轴线,还是图形。
  2. 开辟了一条新的方式来来控制图元样式。

这两个特性都在一定程度上提升了图表的灵活性,无论是通过querySelector来获取图元还是直接对其Class做覆盖都能在HighCharts的逻辑之外对图表的样式做出修改。

通过多种不同的体系【Class体系,Style体系、 SVG属性体系】同时去控制图表的样式,在提高灵活性的同时无疑会增加其维护难度。需要更清晰和明确的规范去做出约束和限制。这里笔者并没有找到明确规范仅从具体表现上来推断各体系负责的部分如下:

  • Class体系: 负责最基础和通用的样式内容,比如会对字体颜色、线条颜色等根据主题颜色做出描述。在其他体系未对相同属性做出描述时,Class体系将提供基础的默认样式。
  • Style体系:一些具体的样式例如colorfont-sizefillcursorpointer-events等。
  • SVG属性体系:元素的布局信息例如z-indexxy、以及一些filter等。

笔者在不同的图元上均有发现不符合上述规范的地方。 一方面可能是不同图元上有不同的规范导致的,另一方面也可能是其本身的规范不够清晰导致的。

以上各体系所负责的内容表述不是十分准确,仅能作为大致参考。