Ray1ee01 commited on
Commit
a51c87f
·
verified ·
1 Parent(s): 21ce175

Upload folder using huggingface_hub

Browse files
modules/chart_engine/template/d3-js/type33_spline_area_chart/spline_area_plain_chart_01.js ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ REQUIREMENTS_BEGIN
3
+ {
4
+ "chart_type": "Spline Area Chart",
5
+ "chart_name": "spline_area_plain_chart_01",
6
+ "required_fields": ["x", "y"],
7
+ "required_fields_type": [["temporal"], ["numerical"]],
8
+ "required_fields_range": [[5, 30], [0, "inf"]],
9
+ "required_fields_icons": [],
10
+ "required_other_icons": ["primary"],
11
+ "required_fields_colors": [],
12
+ "required_other_colors": ["primary"],
13
+ "supported_effects": ["gradient", "opacity"],
14
+ "min_height": 400,
15
+ "min_width": 800,
16
+ "background": "light",
17
+ "icon_mark": "none",
18
+ "icon_label": "none",
19
+ "has_x_axis": "yes",
20
+ "has_y_axis": "no",
21
+ "chart_for": "trend"
22
+ }
23
+ REQUIREMENTS_END
24
+ */
25
+
26
+ function makeChart(containerSelector, data) {
27
+ // 提取数据
28
+ const jsonData = data;
29
+ const chartData = jsonData.data.data;
30
+ const variables = jsonData.variables;
31
+ const typography = jsonData.typography;
32
+ const colors = jsonData.colors || {};
33
+ const colorResolver = chartUtils.color.resolver(jsonData);
34
+ const dataColumns = chartUtils.schema.columns(jsonData);
35
+ const images = jsonData.images || {};
36
+
37
+ // 清空容器
38
+ d3.select(containerSelector).html("");
39
+
40
+ // 获取字段名
41
+ const xField = chartUtils.schema.columnField(dataColumns, 0);
42
+ const yField = chartUtils.schema.columnField(dataColumns, 1);
43
+
44
+ // 设置尺寸和边距
45
+ const width = variables.width;
46
+ const height = variables.height;
47
+ const margin = { top: 120, right: 30, bottom: 120, left: 60 };
48
+ const chartWidth = width - margin.left - margin.right;
49
+ const chartHeight = height - margin.top - margin.bottom;
50
+
51
+ // 创建SVG
52
+ const svg = d3.select(containerSelector)
53
+ .append("svg")
54
+ .attr("width", "100%")
55
+ .attr("height", height)
56
+ .attr("viewBox", `0 0 ${width} ${height}`)
57
+ .attr("style", "max-width: 100%; height: auto;")
58
+ .attr("xmlns", "http://www.w3.org/2000/svg")
59
+ .attr("xmlns:xlink", "http://www.w3.org/1999/xlink");
60
+
61
+ // 创建图表组
62
+ const g = svg.append("g")
63
+ .attr("transform", `translate(${margin.left}, ${margin.top})`);
64
+
65
+ // 确保数据按日期排序
66
+ chartData.sort((a, b) => chartUtils.format.parseDate(a[xField]) - chartUtils.format.parseDate(b[xField]));
67
+
68
+ // 创建x轴比例尺
69
+ const { xScale, xTicks, xFormat, timeSpan } = createXAxisScaleAndTicks(chartData, xField, 0, chartWidth);
70
+
71
+ // 创建y轴比例尺 - 从0开始
72
+ const yMax = d3.max(chartData, d => +d[yField]);
73
+ const yScale = d3.scaleLinear()
74
+ .domain([0, yMax * 1.4]) // 顶部留出10%的空间
75
+ .range([chartHeight, 0]);
76
+
77
+ // 获取主色调
78
+ const primaryColor = colorResolver.other("primary", { fallback: "#a67eb7" }).value;
79
+
80
+ // 创建渐变
81
+ const defs = svg.append("defs");
82
+ const gradientId = "area-gradient";
83
+ const gradient = defs.append("linearGradient")
84
+ .attr("id", gradientId)
85
+ .attr("x1", "0%")
86
+ .attr("y1", "0%")
87
+ .attr("x2", "0%")
88
+ .attr("y2", "100%");
89
+
90
+ gradient.append("stop")
91
+ .attr("offset", "0%")
92
+ .attr("stop-color", primaryColor)
93
+ .attr("stop-opacity", 0.7);
94
+
95
+ gradient.append("stop")
96
+ .attr("offset", "100%")
97
+ .attr("stop-color", primaryColor)
98
+ .attr("stop-opacity", 0);
99
+
100
+
101
+ // 创建面积生成器
102
+ const area = d3.area()
103
+ .x(d => xScale(chartUtils.format.parseDate(d[xField])))
104
+ .y0(chartHeight)
105
+ .y1(d => yScale(+d[yField]))
106
+ .curve(d3.curveBasis);
107
+
108
+ // 绘制面积
109
+ g.append("path")
110
+ .datum(chartData)
111
+ .attr("fill", `url(#${gradientId})`)
112
+ .attr("d", area);
113
+
114
+ // 绘制线条
115
+ const line = d3.line()
116
+ .x(d => xScale(chartUtils.format.parseDate(d[xField])))
117
+ .y(d => yScale(+d[yField]))
118
+ .curve(d3.curveBasis);
119
+
120
+ g.append("path")
121
+ .datum(chartData)
122
+ .attr("fill", "none")
123
+ .attr("stroke", primaryColor)
124
+ .attr("stroke-width", 3)
125
+ .attr("d", line);
126
+
127
+ const sampleLabelIndex = sampleLabels(chartData.length);
128
+
129
+ // 添加数据点和标签 - 优化标签位置,包括首尾点
130
+ chartData.forEach((d, i) => {
131
+ if (!sampleLabelIndex.includes(i)) {
132
+ return;
133
+ }
134
+
135
+ const x = xScale(chartUtils.format.parseDate(d[xField]));
136
+ const y = yScale(+d[yField]);
137
+
138
+ // 计算更好的标签位置,避免遮挡线条
139
+ let labelY = y;
140
+
141
+ // 根据点的位置计算标签位置
142
+ if (i === 0) {
143
+ // 第一个点 - 考虑后一个点
144
+ if (chartData.length > 1) {
145
+ const nextPoint = chartData[i+1];
146
+ const nextY = yScale(nextPoint[yField]);
147
+
148
+ // 计算向前20%的插值
149
+ const forwardY = y + (nextY - y) * 0.2;
150
+
151
+ // 取���个值中的最小值
152
+ labelY = Math.min(y, forwardY) - 4;
153
+ }
154
+ // 确保标签至少在数据点上方30像素
155
+ labelY = Math.min(labelY, y - 4);
156
+ } else if (i === chartData.length - 1) {
157
+ // 最后一个点 - 考虑前一个点
158
+ const prevPoint = chartData[i-1];
159
+ const prevY = yScale(prevPoint[yField]);
160
+
161
+ // 计算向后20%的插值
162
+ const backwardY = y + (prevY - y) * 0.2;
163
+
164
+ // 取两个值中的最小值
165
+ labelY = Math.min(y, backwardY) - 4;
166
+
167
+ // 确保标签至少在数据点上方30像素
168
+ labelY = Math.min(labelY, y - 4);
169
+ } else {
170
+ // 中间点 - 考虑前后两个点
171
+ const prevPoint = chartData[i-1];
172
+ const nextPoint = chartData[i+1];
173
+
174
+ // 计算前一个点的y值
175
+ const prevY = yScale(prevPoint[yField]);
176
+
177
+ // 计算后一个点的y值
178
+ const nextY = yScale(nextPoint[yField]);
179
+
180
+ // 计算向前20%的插值
181
+ const forwardY = y + (nextY - y) * 0.2;
182
+
183
+ // 计算向后20%的插值
184
+ const backwardY = y + (prevY - y) * 0.2;
185
+
186
+ // 取三个值中的最小值(在SVG中,较小的y值表示较高的位置)
187
+ labelY = Math.min(y, forwardY, backwardY) - 4;
188
+
189
+ // 确保标签至少在数据点上方30像素
190
+ labelY = Math.min(labelY, y - 4);
191
+ }
192
+
193
+ // 添加标签背景
194
+ const labelValue = chartUtils.format.integer(d[yField]).text;
195
+ const labelWidth = chartUtils.text.measure(null, labelValue, { fontSize: 14 }).width + 20;
196
+ const labelHeight = 25;
197
+
198
+ g.append("rect")
199
+ .attr("x", x - labelWidth / 2)
200
+ .attr("y", labelY - labelHeight - 15)
201
+ .attr("width", labelWidth)
202
+ .attr("height", labelHeight)
203
+ .attr("rx", 5)
204
+ .attr("ry", 5)
205
+ .attr("fill", primaryColor)
206
+ .attr("opacity", 0.9);
207
+
208
+ // 添加标签文本
209
+ g.append("text")
210
+ .attr("x", x)
211
+ .attr("y", labelY - 15 - labelHeight / 2)
212
+ .attr("text-anchor", "middle")
213
+ .attr("dominant-baseline", "middle")
214
+ .attr("fill", "#fff")
215
+ .attr("font-weight", "bold")
216
+ .style("font-size", "14px")
217
+ .text(labelValue);
218
+
219
+ // 添加倒三角形 - 连接到标签
220
+ const triangleSize = 8;
221
+ g.append("path")
222
+ .attr("d", `M${x-triangleSize/2},${labelY-15} L${x+triangleSize/2},${labelY-15} L${x},${labelY-15+triangleSize} Z`)
223
+ .attr("fill", primaryColor);
224
+
225
+ });
226
+
227
+ // 添加X轴刻度文本
228
+ xTicks.forEach(tick => {
229
+ const x = xScale(tick);
230
+
231
+ g.append("text")
232
+ .attr("x", x)
233
+ .attr("y", chartHeight + 25)
234
+ .attr("text-anchor", "middle")
235
+ .attr("fill", colorResolver.text({ fallback: "#333333" }).value)
236
+ .style("font-size", "14px")
237
+ .text(xFormat(tick));
238
+ });
239
+
240
+ return svg.node();
241
+ }