@wil-works/evoke-charts 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -4
- package/dist/3d.mjs +4992 -0
- package/dist/{ai-BmXDig6M.js → ai-cRi95pS2.js} +1053 -617
- package/dist/ai.mjs +2 -2
- package/dist/evoke-charts.css +197 -31
- package/dist/index.mjs +401 -204
- package/dist/palette-lwVAltky.js +117 -0
- package/dist/schema-CM4hKlpc.js +1102 -0
- package/package.json +4 -2
|
@@ -0,0 +1,1102 @@
|
|
|
1
|
+
//#region src/types.js
|
|
2
|
+
var UP_COLOR = "#dc2626";
|
|
3
|
+
var DOWN_COLOR = "#16a34a";
|
|
4
|
+
var VOLUME_SERIES_NAME = "成交量";
|
|
5
|
+
var DEFAULT_I18N_ZH$1 = {
|
|
6
|
+
tooltip: {
|
|
7
|
+
open: "开",
|
|
8
|
+
close: "收",
|
|
9
|
+
high: "高",
|
|
10
|
+
low: "低",
|
|
11
|
+
total: "总计",
|
|
12
|
+
average: "平均"
|
|
13
|
+
},
|
|
14
|
+
legend: {
|
|
15
|
+
show: "显示",
|
|
16
|
+
hide: "隐藏"
|
|
17
|
+
},
|
|
18
|
+
noData: "暂无数据"
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 系列色令牌槽位 — 每槽按顺序尝试读取专用数据色令牌(--ev-color-series-N),
|
|
22
|
+
* 槽 1 回退 --ev-color-primary(品牌主色跟随);
|
|
23
|
+
* 任一槽无令牌时该槽回落内置成套色板(与语义状态色解耦的数据色板)。
|
|
24
|
+
* CHART_COLORS 仅作为 SSR / 无 DOM 环境的静态兜底。
|
|
25
|
+
*/
|
|
26
|
+
var SERIES_COLOR_SLOTS = [
|
|
27
|
+
["--ev-color-series-1", "--ev-color-primary"],
|
|
28
|
+
["--ev-color-series-2"],
|
|
29
|
+
["--ev-color-series-3"],
|
|
30
|
+
["--ev-color-series-4"],
|
|
31
|
+
["--ev-color-series-5"],
|
|
32
|
+
["--ev-color-series-6"],
|
|
33
|
+
["--ev-color-series-7"],
|
|
34
|
+
["--ev-color-series-8"]
|
|
35
|
+
];
|
|
36
|
+
function readChartToken(name, fallback) {
|
|
37
|
+
try {
|
|
38
|
+
if (typeof document === "undefined") return fallback;
|
|
39
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return fallback;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function getSeriesColors$1(fallbackColors) {
|
|
45
|
+
return SERIES_COLOR_SLOTS.map((tokens, i) => {
|
|
46
|
+
for (const t of tokens) {
|
|
47
|
+
const v = readChartToken(t, "");
|
|
48
|
+
if (v) return v;
|
|
49
|
+
}
|
|
50
|
+
return fallbackColors[i];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
var CHART_COLORS$1 = {
|
|
54
|
+
/**
|
|
55
|
+
* 主色板 — 品牌蓝锚定的成套数据色板(与语义状态色解耦):
|
|
56
|
+
* 色相按取色顺序相邻差 ≥ 30°,明度带 44–68,青绿/金黄/天蓝/珊瑚/紫/石板
|
|
57
|
+
* 取材 AntV 经典系并经品牌蓝调协调;暗色板同色相提亮。
|
|
58
|
+
*/
|
|
59
|
+
primary: [
|
|
60
|
+
"#175DFF",
|
|
61
|
+
"#5AD8A6",
|
|
62
|
+
"#F6BD16",
|
|
63
|
+
"#6DC8EC",
|
|
64
|
+
"#E8684A",
|
|
65
|
+
"#9270CA",
|
|
66
|
+
"#FF9D4D",
|
|
67
|
+
"#5D7092"
|
|
68
|
+
],
|
|
69
|
+
/** 浅色系列 — primary 的 light-9/light-8/light-7 阶梯 */
|
|
70
|
+
light: [
|
|
71
|
+
"#e8f0ff",
|
|
72
|
+
"#d0e1ff",
|
|
73
|
+
"#b9d2ff"
|
|
74
|
+
],
|
|
75
|
+
/** 暗色模式系列 — 同色相提亮(对齐 evoke 暗色主色) */
|
|
76
|
+
dark: [
|
|
77
|
+
"#4d8bff",
|
|
78
|
+
"#5ad8a6",
|
|
79
|
+
"#f6bd16",
|
|
80
|
+
"#6dc8ec",
|
|
81
|
+
"#f08568",
|
|
82
|
+
"#a585e8",
|
|
83
|
+
"#ff9d4d",
|
|
84
|
+
"#8da3bf"
|
|
85
|
+
],
|
|
86
|
+
/** 热力图默认色阶(天亮蓝由浅到深) */
|
|
87
|
+
heatmapScale: [
|
|
88
|
+
"#f0f5ff",
|
|
89
|
+
"#c6d9ff",
|
|
90
|
+
"#8ab0ff",
|
|
91
|
+
"#4d86ff",
|
|
92
|
+
"#175DFF",
|
|
93
|
+
"#0040c2"
|
|
94
|
+
]
|
|
95
|
+
};
|
|
96
|
+
function formatValue(value, options) {
|
|
97
|
+
if (value === null || value === void 0 || Number.isNaN(value)) return "-";
|
|
98
|
+
const { decimals = 0, thousandSeparator = ",", prefix = "", suffix = "", abbreviate = false } = options || {};
|
|
99
|
+
let formatted;
|
|
100
|
+
if (abbreviate) {
|
|
101
|
+
const abs = Math.abs(value);
|
|
102
|
+
if (abs >= 1e8) formatted = (value / 1e8).toFixed(2) + "亿";
|
|
103
|
+
else if (abs >= 1e4) formatted = (value / 1e4).toFixed(2) + "万";
|
|
104
|
+
else if (abs >= 1e3) formatted = (value / 1e3).toFixed(2) + "K";
|
|
105
|
+
else formatted = value.toFixed(decimals);
|
|
106
|
+
} else {
|
|
107
|
+
const fixed = value.toFixed(decimals);
|
|
108
|
+
if (thousandSeparator && decimals === 0) formatted = fixed.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
|
|
109
|
+
else if (thousandSeparator) {
|
|
110
|
+
const [intPart, decPart] = fixed.split(".");
|
|
111
|
+
formatted = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator) + (decPart ? "." + decPart : "");
|
|
112
|
+
} else formatted = fixed;
|
|
113
|
+
}
|
|
114
|
+
return prefix + formatted + suffix;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/palettes.js
|
|
118
|
+
var CHART_PALETTES = [
|
|
119
|
+
{
|
|
120
|
+
id: "classic",
|
|
121
|
+
name: "经典",
|
|
122
|
+
scene: "当前缺省色板的固化版,品牌蓝锚定;想保住默认观感不随换肤时用",
|
|
123
|
+
light: [
|
|
124
|
+
"#175DFF",
|
|
125
|
+
"#5AD8A6",
|
|
126
|
+
"#F6BD16",
|
|
127
|
+
"#6DC8EC",
|
|
128
|
+
"#E8684A",
|
|
129
|
+
"#9270CA",
|
|
130
|
+
"#FF9D4D",
|
|
131
|
+
"#5D7092"
|
|
132
|
+
],
|
|
133
|
+
dark: [
|
|
134
|
+
"#4d8bff",
|
|
135
|
+
"#5ad8a6",
|
|
136
|
+
"#f6bd16",
|
|
137
|
+
"#6dc8ec",
|
|
138
|
+
"#f08568",
|
|
139
|
+
"#a585e8",
|
|
140
|
+
"#ff9d4d",
|
|
141
|
+
"#8da3bf"
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
id: "aurora",
|
|
146
|
+
name: "极光",
|
|
147
|
+
scene: "科技 SaaS / 数据产品——冷调现代,蓝绿紫主打的仪表盘观感",
|
|
148
|
+
light: [
|
|
149
|
+
"#3B82F6",
|
|
150
|
+
"#10B981",
|
|
151
|
+
"#F59E0B",
|
|
152
|
+
"#8B5CF6",
|
|
153
|
+
"#EC4899",
|
|
154
|
+
"#06B6D4",
|
|
155
|
+
"#F97316",
|
|
156
|
+
"#64748B"
|
|
157
|
+
],
|
|
158
|
+
dark: [
|
|
159
|
+
"#60A5FA",
|
|
160
|
+
"#34D399",
|
|
161
|
+
"#FBBF24",
|
|
162
|
+
"#A78BFA",
|
|
163
|
+
"#F472B6",
|
|
164
|
+
"#22D3EE",
|
|
165
|
+
"#FB923C",
|
|
166
|
+
"#94A3B8"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "sunset",
|
|
171
|
+
name: "落日",
|
|
172
|
+
scene: "消费零售 / 生活方式 / 营销复盘——暖橙珊瑚主导,热情有温度",
|
|
173
|
+
light: [
|
|
174
|
+
"#F97316",
|
|
175
|
+
"#E11D48",
|
|
176
|
+
"#EAB308",
|
|
177
|
+
"#14B8A6",
|
|
178
|
+
"#8B5CF6",
|
|
179
|
+
"#FB7185",
|
|
180
|
+
"#0EA5E9",
|
|
181
|
+
"#A8A29E"
|
|
182
|
+
],
|
|
183
|
+
dark: [
|
|
184
|
+
"#FB923C",
|
|
185
|
+
"#FB7185",
|
|
186
|
+
"#FACC15",
|
|
187
|
+
"#2DD4BF",
|
|
188
|
+
"#A78BFA",
|
|
189
|
+
"#FDA4AF",
|
|
190
|
+
"#38BDF8",
|
|
191
|
+
"#BDB4AC"
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "morandi",
|
|
196
|
+
name: "莫兰迪",
|
|
197
|
+
scene: "人文 / 咨询报告 / 印刷排版——低饱和灰调,长文配图不抢字",
|
|
198
|
+
light: [
|
|
199
|
+
"#A3AEBB",
|
|
200
|
+
"#C2A878",
|
|
201
|
+
"#B08890",
|
|
202
|
+
"#86A69D",
|
|
203
|
+
"#9A8FA8",
|
|
204
|
+
"#B7C4A0",
|
|
205
|
+
"#C98F6B",
|
|
206
|
+
"#8A8F98"
|
|
207
|
+
],
|
|
208
|
+
dark: [
|
|
209
|
+
"#B4BEC9",
|
|
210
|
+
"#D4BC8E",
|
|
211
|
+
"#C29AA3",
|
|
212
|
+
"#97B5AC",
|
|
213
|
+
"#ACA2BC",
|
|
214
|
+
"#C5D0AF",
|
|
215
|
+
"#D8A685",
|
|
216
|
+
"#9CA1A9"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: "forest",
|
|
221
|
+
name: "林间",
|
|
222
|
+
scene: "健康 / 环保 / 农业供应链——自然绿主导,ESG 与可持续叙事",
|
|
223
|
+
light: [
|
|
224
|
+
"#16A34A",
|
|
225
|
+
"#0EA5E9",
|
|
226
|
+
"#CA8A04",
|
|
227
|
+
"#7C3AED",
|
|
228
|
+
"#DC2626",
|
|
229
|
+
"#0D9488",
|
|
230
|
+
"#D97706",
|
|
231
|
+
"#64748B"
|
|
232
|
+
],
|
|
233
|
+
dark: [
|
|
234
|
+
"#22C55E",
|
|
235
|
+
"#38BDF8",
|
|
236
|
+
"#EAB308",
|
|
237
|
+
"#8B5CF6",
|
|
238
|
+
"#EF4444",
|
|
239
|
+
"#14B8A6",
|
|
240
|
+
"#F59E0B",
|
|
241
|
+
"#94A3B8"
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: "ink",
|
|
246
|
+
name: "墨蓝",
|
|
247
|
+
scene: "金融 / 政企 / 严肃年报——克制深稳,深蓝为骨、暖色点缀",
|
|
248
|
+
light: [
|
|
249
|
+
"#1E40AF",
|
|
250
|
+
"#0F766E",
|
|
251
|
+
"#B45309",
|
|
252
|
+
"#6D28D9",
|
|
253
|
+
"#BE123C",
|
|
254
|
+
"#0369A1",
|
|
255
|
+
"#4D7C0F",
|
|
256
|
+
"#6B7280"
|
|
257
|
+
],
|
|
258
|
+
dark: [
|
|
259
|
+
"#6E9BF7",
|
|
260
|
+
"#2DD4BF",
|
|
261
|
+
"#D97706",
|
|
262
|
+
"#8B5CF6",
|
|
263
|
+
"#FB7185",
|
|
264
|
+
"#38BDF8",
|
|
265
|
+
"#84CC16",
|
|
266
|
+
"#9CA3AF"
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: "candy",
|
|
271
|
+
name: "糖果",
|
|
272
|
+
scene: "营销活动 / 年轻品牌 / 大屏展示——高饱和明快,视觉抓人",
|
|
273
|
+
light: [
|
|
274
|
+
"#EC4899",
|
|
275
|
+
"#8B5CF6",
|
|
276
|
+
"#F59E0B",
|
|
277
|
+
"#22C55E",
|
|
278
|
+
"#06B6D4",
|
|
279
|
+
"#EF4444",
|
|
280
|
+
"#6366F1",
|
|
281
|
+
"#9CA3AF"
|
|
282
|
+
],
|
|
283
|
+
dark: [
|
|
284
|
+
"#F472B6",
|
|
285
|
+
"#A78BFA",
|
|
286
|
+
"#FBBF24",
|
|
287
|
+
"#4ADE80",
|
|
288
|
+
"#22D3EE",
|
|
289
|
+
"#F87171",
|
|
290
|
+
"#818CF8",
|
|
291
|
+
"#A1A1AA"
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
];
|
|
295
|
+
/** 按 id 取色系在指定明暗模式下的 8 槽色值;未知 id 返回 null */
|
|
296
|
+
function resolveChartPalette(id, isDark) {
|
|
297
|
+
const palette = CHART_PALETTES.find((p) => p.id === id);
|
|
298
|
+
if (!palette) return null;
|
|
299
|
+
return (isDark ? palette.dark : palette.light).slice(0, 8);
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
//#region src/3d/core/color.js
|
|
303
|
+
/**
|
|
304
|
+
* 颜色工具 — 解析 / 明度调整 / 插值 / 面着色 / 设计令牌读取
|
|
305
|
+
*
|
|
306
|
+
* 面着色走 HSL 明度缩放而非「向白色混合」:混合白色会同时拉低饱和度,
|
|
307
|
+
* 同一根柱子的顶面与侧面容易糊成一片灰;只动明度则色相与饱和度守恒,
|
|
308
|
+
* 深色系色板上也能保住辨识度。
|
|
309
|
+
*/
|
|
310
|
+
/** 解析 #rgb / #rrggbb / #rrggbbaa / rgb() / rgba() / hsl() 为通道对象;失败返回 null */
|
|
311
|
+
function parseColor(color) {
|
|
312
|
+
if (typeof color !== "string") return null;
|
|
313
|
+
const s = color.trim();
|
|
314
|
+
if (!s) return null;
|
|
315
|
+
if (s.charCodeAt(0) === 35) {
|
|
316
|
+
const hex = s.slice(1);
|
|
317
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
318
|
+
const r = parseInt(hex[0] + hex[0], 16);
|
|
319
|
+
const g = parseInt(hex[1] + hex[1], 16);
|
|
320
|
+
const b = parseInt(hex[2] + hex[2], 16);
|
|
321
|
+
const a = hex.length === 4 ? parseInt(hex[3] + hex[3], 16) / 255 : 1;
|
|
322
|
+
if ([
|
|
323
|
+
r,
|
|
324
|
+
g,
|
|
325
|
+
b
|
|
326
|
+
].some(Number.isNaN)) return null;
|
|
327
|
+
return {
|
|
328
|
+
r,
|
|
329
|
+
g,
|
|
330
|
+
b,
|
|
331
|
+
a
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (hex.length === 6 || hex.length === 8) {
|
|
335
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
336
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
337
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
338
|
+
const a = hex.length === 8 ? parseInt(hex.slice(6, 8), 16) / 255 : 1;
|
|
339
|
+
if ([
|
|
340
|
+
r,
|
|
341
|
+
g,
|
|
342
|
+
b
|
|
343
|
+
].some(Number.isNaN)) return null;
|
|
344
|
+
return {
|
|
345
|
+
r,
|
|
346
|
+
g,
|
|
347
|
+
b,
|
|
348
|
+
a
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
const fn = s.match(/^(rgba?|hsla?)\(([^)]+)\)$/i);
|
|
354
|
+
if (!fn) return null;
|
|
355
|
+
const parts = fn[2].split(/[,\s/]+/).filter(Boolean);
|
|
356
|
+
if (parts.length < 3) return null;
|
|
357
|
+
const nums = parts.map((p) => p.endsWith("%") ? parseFloat(p) / 100 : parseFloat(p));
|
|
358
|
+
if (nums.slice(0, 3).some((n) => !Number.isFinite(n))) return null;
|
|
359
|
+
const alpha = parts.length > 3 ? clamp01(Number.isFinite(nums[3]) ? nums[3] : 1) : 1;
|
|
360
|
+
if (fn[1].toLowerCase().startsWith("hsl")) return {
|
|
361
|
+
...hslToRgb(nums[0], clamp01(nums[1]), clamp01(nums[2])),
|
|
362
|
+
a: alpha
|
|
363
|
+
};
|
|
364
|
+
return {
|
|
365
|
+
r: clampByte(nums[0]),
|
|
366
|
+
g: clampByte(nums[1]),
|
|
367
|
+
b: clampByte(nums[2]),
|
|
368
|
+
a: alpha
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function clampByte(v) {
|
|
372
|
+
if (!Number.isFinite(v)) return 0;
|
|
373
|
+
return Math.max(0, Math.min(255, Math.round(v)));
|
|
374
|
+
}
|
|
375
|
+
function clamp01(v) {
|
|
376
|
+
if (!Number.isFinite(v)) return 0;
|
|
377
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
378
|
+
}
|
|
379
|
+
function rgbToHex(r, g, b) {
|
|
380
|
+
const to = (v) => clampByte(v).toString(16).padStart(2, "0");
|
|
381
|
+
return `#${to(r)}${to(g)}${to(b)}`;
|
|
382
|
+
}
|
|
383
|
+
/** 转为带透明度的 rgba() 字符串;无法解析时原样返回,避免画布拿到非法 fillStyle */
|
|
384
|
+
function toRgba(color, alpha = 1) {
|
|
385
|
+
const c = parseColor(color);
|
|
386
|
+
if (!c) return color;
|
|
387
|
+
return `rgba(${c.r},${c.g},${c.b},${clamp01(alpha) * c.a})`;
|
|
388
|
+
}
|
|
389
|
+
function rgbToHsl(r, g, b) {
|
|
390
|
+
const rn = clampByte(r) / 255;
|
|
391
|
+
const gn = clampByte(g) / 255;
|
|
392
|
+
const bn = clampByte(b) / 255;
|
|
393
|
+
const max = Math.max(rn, gn, bn);
|
|
394
|
+
const min = Math.min(rn, gn, bn);
|
|
395
|
+
const l = (max + min) / 2;
|
|
396
|
+
const d = max - min;
|
|
397
|
+
if (d < 1e-9) return {
|
|
398
|
+
h: 0,
|
|
399
|
+
s: 0,
|
|
400
|
+
l
|
|
401
|
+
};
|
|
402
|
+
const s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
403
|
+
let h;
|
|
404
|
+
if (max === rn) h = ((gn - bn) / d + (gn < bn ? 6 : 0)) / 6;
|
|
405
|
+
else if (max === gn) h = ((bn - rn) / d + 2) / 6;
|
|
406
|
+
else h = ((rn - gn) / d + 4) / 6;
|
|
407
|
+
return {
|
|
408
|
+
h: h * 360,
|
|
409
|
+
s,
|
|
410
|
+
l
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function hslToRgb(h, s, l) {
|
|
414
|
+
const hh = (h % 360 + 360) % 360 / 360;
|
|
415
|
+
const ss = clamp01(s);
|
|
416
|
+
const ll = clamp01(l);
|
|
417
|
+
if (ss < 1e-9) {
|
|
418
|
+
const v = clampByte(ll * 255);
|
|
419
|
+
return {
|
|
420
|
+
r: v,
|
|
421
|
+
g: v,
|
|
422
|
+
b: v
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
const q = ll < .5 ? ll * (1 + ss) : ll + ss - ll * ss;
|
|
426
|
+
const p = 2 * ll - q;
|
|
427
|
+
const channel = (t) => {
|
|
428
|
+
let tt = t;
|
|
429
|
+
if (tt < 0) tt += 1;
|
|
430
|
+
if (tt > 1) tt -= 1;
|
|
431
|
+
if (tt < 1 / 6) return p + (q - p) * 6 * tt;
|
|
432
|
+
if (tt < 1 / 2) return q;
|
|
433
|
+
if (tt < 2 / 3) return p + (q - p) * (2 / 3 - tt) * 6;
|
|
434
|
+
return p;
|
|
435
|
+
};
|
|
436
|
+
return {
|
|
437
|
+
r: clampByte(channel(hh + 1 / 3) * 255),
|
|
438
|
+
g: clampByte(channel(hh) * 255),
|
|
439
|
+
b: clampByte(channel(hh - 1 / 3) * 255)
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* 明度缩放:factor > 1 提亮、< 1 压暗,色相与饱和度守恒
|
|
444
|
+
* 结果明度钳制在 [0.06, 0.94],防止出现纯黑面或纯白面导致形体感丢失
|
|
445
|
+
*/
|
|
446
|
+
function adjustLightness(color, factor) {
|
|
447
|
+
const c = parseColor(color);
|
|
448
|
+
if (!c) return color;
|
|
449
|
+
const { h, s, l } = rgbToHsl(c.r, c.g, c.b);
|
|
450
|
+
const rgb = hslToRgb(h, s, Math.max(.06, Math.min(.94, l * (Number.isFinite(factor) ? factor : 1))));
|
|
451
|
+
if (c.a < 1) return toRgba(rgbToHex(rgb.r, rgb.g, rgb.b), c.a);
|
|
452
|
+
return rgbToHex(rgb.r, rgb.g, rgb.b);
|
|
453
|
+
}
|
|
454
|
+
/** 线性混合:t=0 取 a,t=1 取 b */
|
|
455
|
+
function mixColor(a, b, t) {
|
|
456
|
+
const ca = parseColor(a);
|
|
457
|
+
const cb = parseColor(b);
|
|
458
|
+
if (!ca) return b;
|
|
459
|
+
if (!cb) return a;
|
|
460
|
+
const k = clamp01(t);
|
|
461
|
+
return rgbToHex(ca.r + (cb.r - ca.r) * k, ca.g + (cb.g - ca.g) * k, ca.b + (cb.b - ca.b) * k);
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* 向目标色混色相但保留自身透明度 — 雾化专用:
|
|
465
|
+
* 墙面这类半透明薄纱只换色相,不会因为混入不透明的背景色而变实。
|
|
466
|
+
*/
|
|
467
|
+
function mixHue(color, target, t) {
|
|
468
|
+
const c = parseColor(color);
|
|
469
|
+
const g = parseColor(target);
|
|
470
|
+
if (!c || !g) return color;
|
|
471
|
+
const k = clamp01(t);
|
|
472
|
+
const hex = rgbToHex(c.r + (g.r - c.r) * k, c.g + (g.g - c.g) * k, c.b + (g.b - c.b) * k);
|
|
473
|
+
return c.a < 1 ? toRgba(hex, c.a) : hex;
|
|
474
|
+
}
|
|
475
|
+
function lighten(color, amount) {
|
|
476
|
+
return adjustLightness(color, 1 + clamp01(amount));
|
|
477
|
+
}
|
|
478
|
+
function darken(color, amount) {
|
|
479
|
+
return adjustLightness(color, 1 - clamp01(amount));
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* 面着色 — 兰伯特余弦驱动明度缩放
|
|
483
|
+
* lambert 为法线与光线的点积(已由调用方归一)。
|
|
484
|
+
* ambient 是暗面下限,intensity 是明暗落差;两者共同决定形体的立体感强度。
|
|
485
|
+
*/
|
|
486
|
+
function shadeColor(color, lambert, options = {}) {
|
|
487
|
+
return adjustLightness(color, (Number.isFinite(options.ambient) ? options.ambient : .58) + (Number.isFinite(options.intensity) ? options.intensity : .82) * clamp01((lambert + 1) / 2));
|
|
488
|
+
}
|
|
489
|
+
/** 多色阶取样:colors 为色带、t ∈ [0,1],用于曲面高度映射 */
|
|
490
|
+
function gradientAt(colors, t) {
|
|
491
|
+
if (!Array.isArray(colors) || !colors.length) return "#888888";
|
|
492
|
+
if (colors.length === 1) return colors[0];
|
|
493
|
+
const pos = clamp01(t) * (colors.length - 1);
|
|
494
|
+
const i = Math.min(colors.length - 2, Math.floor(pos));
|
|
495
|
+
return mixColor(colors[i], colors[i + 1], pos - i);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* 读取设计令牌 — 与 evoke 生态共用 --ev-* 命名空间,换肤自动跟随
|
|
499
|
+
* 无 DOM(SSR / 无头测试)时直接回落,绝不抛错
|
|
500
|
+
*/
|
|
501
|
+
function readToken(name, fallback) {
|
|
502
|
+
try {
|
|
503
|
+
if (typeof document === "undefined" || !document.documentElement) return fallback;
|
|
504
|
+
return getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback;
|
|
505
|
+
} catch {
|
|
506
|
+
return fallback;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/** 判断当前是否暗色主题 — 与 evoke 生态一致,以 html.dark 为准 */
|
|
510
|
+
function isDarkMode() {
|
|
511
|
+
try {
|
|
512
|
+
if (typeof document === "undefined" || !document.documentElement) return false;
|
|
513
|
+
return document.documentElement.classList.contains("dark");
|
|
514
|
+
} catch {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
//#endregion
|
|
519
|
+
//#region src/3d/types.js
|
|
520
|
+
/**
|
|
521
|
+
* 主题与常量 — 内置明暗结构色、i18n
|
|
522
|
+
*
|
|
523
|
+
* 系列色与令牌读取直接复用 evoke-charts 主体系的实现(../types.js):
|
|
524
|
+
* 同一套 --ev-color-series-N 槽位、同一套静态兜底色板,二维与三维必然同色。
|
|
525
|
+
* 本文件只补三维独有部分:结构色(墙面/网格)与三维图型清单。
|
|
526
|
+
*/
|
|
527
|
+
/** 结构色(非数据元素)——明暗两套,与缺省色板同源 */
|
|
528
|
+
var STRUCTURE_LIGHT = {
|
|
529
|
+
backgroundColor: "transparent",
|
|
530
|
+
/** 雾化基准色:背景透明时远景向它混合(空气透视的目标色) */
|
|
531
|
+
backdropColor: "#ffffff",
|
|
532
|
+
textColor: "#1f2937",
|
|
533
|
+
textColorSecondary: "#6b7280",
|
|
534
|
+
gridColor: "rgba(31, 41, 55, 0.10)",
|
|
535
|
+
wallColor: "rgba(31, 41, 55, 0.035)",
|
|
536
|
+
borderColor: "rgba(31, 41, 55, 0.16)",
|
|
537
|
+
highlightColor: "rgba(23, 93, 255, 0.16)"
|
|
538
|
+
};
|
|
539
|
+
var STRUCTURE_DARK = {
|
|
540
|
+
backgroundColor: "transparent",
|
|
541
|
+
backdropColor: "#1f2937",
|
|
542
|
+
textColor: "#e5e7eb",
|
|
543
|
+
textColorSecondary: "#9ca3af",
|
|
544
|
+
gridColor: "rgba(229, 231, 235, 0.12)",
|
|
545
|
+
wallColor: "rgba(229, 231, 235, 0.05)",
|
|
546
|
+
borderColor: "rgba(229, 231, 235, 0.20)",
|
|
547
|
+
highlightColor: "rgba(77, 139, 255, 0.24)"
|
|
548
|
+
};
|
|
549
|
+
/** 共享主体系的色板与槽位读取(兼容别名,三维内部引用不动) */
|
|
550
|
+
var CHART_COLORS = CHART_COLORS$1;
|
|
551
|
+
function getSeriesColors(fallbackColors) {
|
|
552
|
+
return getSeriesColors$1(fallbackColors);
|
|
553
|
+
}
|
|
554
|
+
var DEFAULT_I18N_ZH = {
|
|
555
|
+
tooltip: {
|
|
556
|
+
x: "X",
|
|
557
|
+
y: "Y",
|
|
558
|
+
z: "Z",
|
|
559
|
+
value: "数值",
|
|
560
|
+
total: "总计",
|
|
561
|
+
percent: "占比"
|
|
562
|
+
},
|
|
563
|
+
legend: {
|
|
564
|
+
show: "显示",
|
|
565
|
+
hide: "隐藏"
|
|
566
|
+
},
|
|
567
|
+
noData: "暂无数据"
|
|
568
|
+
};
|
|
569
|
+
var DEFAULT_I18N_EN = {
|
|
570
|
+
tooltip: {
|
|
571
|
+
x: "X",
|
|
572
|
+
y: "Y",
|
|
573
|
+
z: "Z",
|
|
574
|
+
value: "Value",
|
|
575
|
+
total: "Total",
|
|
576
|
+
percent: "Share"
|
|
577
|
+
},
|
|
578
|
+
legend: {
|
|
579
|
+
show: "Show",
|
|
580
|
+
hide: "Hide"
|
|
581
|
+
},
|
|
582
|
+
noData: "No Data"
|
|
583
|
+
};
|
|
584
|
+
function resolveI18n(options, isDark) {
|
|
585
|
+
const i18n = options && typeof options.i18n === "object" ? options.i18n : null;
|
|
586
|
+
const base = options && options.lang === "en" ? DEFAULT_I18N_EN : DEFAULT_I18N_ZH;
|
|
587
|
+
if (!i18n) return base;
|
|
588
|
+
return {
|
|
589
|
+
...base,
|
|
590
|
+
...i18n,
|
|
591
|
+
tooltip: {
|
|
592
|
+
...base.tooltip,
|
|
593
|
+
...i18n.tooltip || {}
|
|
594
|
+
},
|
|
595
|
+
legend: {
|
|
596
|
+
...base.legend,
|
|
597
|
+
...i18n.legend || {}
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
/** 支持的图型清单 — schema 枚举与运行时分发的唯一事实源 */
|
|
602
|
+
var CHART3D_TYPES = [
|
|
603
|
+
"bar3d",
|
|
604
|
+
"line3d",
|
|
605
|
+
"scatter3d",
|
|
606
|
+
"surface3d",
|
|
607
|
+
"pie3d"
|
|
608
|
+
];
|
|
609
|
+
/** 进场/补间缓动 — 与 evoke-charts 的 easings 同名同义 */
|
|
610
|
+
var easings = {
|
|
611
|
+
linear: (t) => t,
|
|
612
|
+
easeIn: (t) => t * t,
|
|
613
|
+
easeOut: (t) => 1 - (1 - t) * (1 - t),
|
|
614
|
+
easeInOut: (t) => t < .5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2,
|
|
615
|
+
easeOutBack: (t) => {
|
|
616
|
+
return 1 + 2.70158 * (t - 1) ** 3 + 1.70158 * (t - 1) ** 2;
|
|
617
|
+
},
|
|
618
|
+
easeOutExpo: (t) => t >= 1 ? 1 : 1 - 2 ** (-10 * t)
|
|
619
|
+
};
|
|
620
|
+
function resolveEasing(name) {
|
|
621
|
+
return easings[name] || easings.easeOut;
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* 组装主题
|
|
625
|
+
* 优先级与 evoke-charts 一致:customTheme > options.palette > --ev-* 令牌 > 内置
|
|
626
|
+
* (colors 字段单独裁定:customTheme.colors > paletteColors > 令牌槽位 > 内置色板)
|
|
627
|
+
*/
|
|
628
|
+
function getTheme(options = {}, customTheme = null, paletteColors = null) {
|
|
629
|
+
const isDark = isDarkMode();
|
|
630
|
+
const structure = isDark ? STRUCTURE_DARK : STRUCTURE_LIGHT;
|
|
631
|
+
let colors;
|
|
632
|
+
if (customTheme && Array.isArray(customTheme.colors) && customTheme.colors.length) colors = customTheme.colors;
|
|
633
|
+
else if (Array.isArray(paletteColors) && paletteColors.length) colors = paletteColors;
|
|
634
|
+
else colors = getSeriesColors(isDark ? CHART_COLORS.dark : CHART_COLORS.primary);
|
|
635
|
+
const bgRaw = readToken("--ev-bg-color", structure.backgroundColor);
|
|
636
|
+
const bgParsed = parseColor(bgRaw);
|
|
637
|
+
const backdropColor = bgParsed && bgParsed.a >= .9 ? bgRaw : readToken("--ev-bg-color-overlay", structure.backdropColor);
|
|
638
|
+
const theme = {
|
|
639
|
+
isDark,
|
|
640
|
+
...structure,
|
|
641
|
+
backgroundColor: bgRaw,
|
|
642
|
+
backdropColor,
|
|
643
|
+
textColor: readToken("--ev-text-color-primary", structure.textColor),
|
|
644
|
+
textColorSecondary: readToken("--ev-text-color-secondary", structure.textColorSecondary),
|
|
645
|
+
gridColor: readToken("--ev-border-color-lighter", structure.gridColor),
|
|
646
|
+
borderColor: readToken("--ev-border-color", structure.borderColor),
|
|
647
|
+
highlightColor: structure.highlightColor,
|
|
648
|
+
fontFamily: readToken("--ev-font-family", "'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', sans-serif"),
|
|
649
|
+
colors
|
|
650
|
+
};
|
|
651
|
+
if (customTheme) {
|
|
652
|
+
for (const [k, v] of Object.entries(customTheme)) if (v !== void 0) theme[k] = v;
|
|
653
|
+
}
|
|
654
|
+
return theme;
|
|
655
|
+
}
|
|
656
|
+
//#endregion
|
|
657
|
+
//#region src/3d/palette.js
|
|
658
|
+
/**
|
|
659
|
+
* 三维色带注册表 — 曲面 / 色标散点的高度映射色带
|
|
660
|
+
*
|
|
661
|
+
* 系列色系(7 套)直接复用 evoke-charts 主体系的注册表(../palettes.js):
|
|
662
|
+
* 同 id 同色值、同一处维护,二维与三维并排必然一致。本文件只保留三维独有的
|
|
663
|
+
* 连续色带(低→高)与解析函数。
|
|
664
|
+
*/
|
|
665
|
+
/** 系列色系 = 主体系注册表(别名导出,三维内部引用不动) */
|
|
666
|
+
var CHART3D_PALETTES = CHART_PALETTES;
|
|
667
|
+
var CHART3D_PALETTE_IDS = CHART_PALETTES.map((p) => p.id);
|
|
668
|
+
/** 曲面高度映射色带 — 每套色系配一条低→高的连续色带,供 surface3d 取样 */
|
|
669
|
+
var CHART3D_RAMPS = {
|
|
670
|
+
classic: {
|
|
671
|
+
light: [
|
|
672
|
+
"#e8f0ff",
|
|
673
|
+
"#8ab0ff",
|
|
674
|
+
"#4d86ff",
|
|
675
|
+
"#175DFF",
|
|
676
|
+
"#00329b"
|
|
677
|
+
],
|
|
678
|
+
dark: [
|
|
679
|
+
"#12244d",
|
|
680
|
+
"#26417f",
|
|
681
|
+
"#4d8bff",
|
|
682
|
+
"#9ec2ff",
|
|
683
|
+
"#e5eeff"
|
|
684
|
+
]
|
|
685
|
+
},
|
|
686
|
+
aurora: {
|
|
687
|
+
light: [
|
|
688
|
+
"#dbeafe",
|
|
689
|
+
"#93c5fd",
|
|
690
|
+
"#38bdf8",
|
|
691
|
+
"#10b981",
|
|
692
|
+
"#047857"
|
|
693
|
+
],
|
|
694
|
+
dark: [
|
|
695
|
+
"#0b2545",
|
|
696
|
+
"#155e75",
|
|
697
|
+
"#0ea5e9",
|
|
698
|
+
"#34d399",
|
|
699
|
+
"#d1fae5"
|
|
700
|
+
]
|
|
701
|
+
},
|
|
702
|
+
sunset: {
|
|
703
|
+
light: [
|
|
704
|
+
"#fff7ed",
|
|
705
|
+
"#fdba74",
|
|
706
|
+
"#f97316",
|
|
707
|
+
"#e11d48",
|
|
708
|
+
"#881337"
|
|
709
|
+
],
|
|
710
|
+
dark: [
|
|
711
|
+
"#3b1109",
|
|
712
|
+
"#9a3412",
|
|
713
|
+
"#f97316",
|
|
714
|
+
"#fb7185",
|
|
715
|
+
"#ffe4e6"
|
|
716
|
+
]
|
|
717
|
+
},
|
|
718
|
+
viridis: {
|
|
719
|
+
light: [
|
|
720
|
+
"#440154",
|
|
721
|
+
"#31688e",
|
|
722
|
+
"#35b779",
|
|
723
|
+
"#fde725"
|
|
724
|
+
],
|
|
725
|
+
dark: [
|
|
726
|
+
"#440154",
|
|
727
|
+
"#31688e",
|
|
728
|
+
"#35b779",
|
|
729
|
+
"#fde725"
|
|
730
|
+
]
|
|
731
|
+
},
|
|
732
|
+
heat: {
|
|
733
|
+
light: [
|
|
734
|
+
"#fff5eb",
|
|
735
|
+
"#fdd0a2",
|
|
736
|
+
"#fd8d3c",
|
|
737
|
+
"#d94801",
|
|
738
|
+
"#7f2704"
|
|
739
|
+
],
|
|
740
|
+
dark: [
|
|
741
|
+
"#2b0f00",
|
|
742
|
+
"#8c2d04",
|
|
743
|
+
"#d94801",
|
|
744
|
+
"#fdd0a2",
|
|
745
|
+
"#fff5eb"
|
|
746
|
+
]
|
|
747
|
+
},
|
|
748
|
+
mono: {
|
|
749
|
+
light: [
|
|
750
|
+
"#f3f4f6",
|
|
751
|
+
"#9ca3af",
|
|
752
|
+
"#4b5563",
|
|
753
|
+
"#111827"
|
|
754
|
+
],
|
|
755
|
+
dark: [
|
|
756
|
+
"#111827",
|
|
757
|
+
"#4b5563",
|
|
758
|
+
"#9ca3af",
|
|
759
|
+
"#f3f4f6"
|
|
760
|
+
]
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
/** 按 id 取曲面色带;未指定时随色系联动,未知 id 返回 null */
|
|
764
|
+
function resolveChartRamp(id, isDark) {
|
|
765
|
+
const ramp = CHART3D_RAMPS[id];
|
|
766
|
+
if (!ramp) return null;
|
|
767
|
+
return (isDark ? ramp.dark : ramp.light).slice();
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region src/3d/schema.js
|
|
771
|
+
/**
|
|
772
|
+
* Options 校验 — 手写 JSON-Schema 子集(与 evoke-charts 同一套实现思路)
|
|
773
|
+
*
|
|
774
|
+
* 校验只告警不阻断:dev 模式下组件把警告打印到控制台,生产渲染永不受影响。
|
|
775
|
+
* 好处是图型能力可以先行,契约滞后一步不会炸掉用户的页面。
|
|
776
|
+
*/
|
|
777
|
+
var seriesSchema = {
|
|
778
|
+
type: "object",
|
|
779
|
+
properties: {
|
|
780
|
+
name: { type: "string" },
|
|
781
|
+
data: { type: "array" },
|
|
782
|
+
color: { type: "string" }
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
var pieItemSchema = {
|
|
786
|
+
type: "object",
|
|
787
|
+
properties: {
|
|
788
|
+
name: { type: "string" },
|
|
789
|
+
value: { type: "number" },
|
|
790
|
+
color: { type: "string" }
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
var axisSchema = {
|
|
794
|
+
type: "object",
|
|
795
|
+
properties: {
|
|
796
|
+
show: { type: "boolean" },
|
|
797
|
+
labels: { type: "boolean" },
|
|
798
|
+
grid: { type: "boolean" },
|
|
799
|
+
line: { type: "boolean" },
|
|
800
|
+
title: { type: "boolean" },
|
|
801
|
+
name: { type: "string" },
|
|
802
|
+
type: {
|
|
803
|
+
type: "string",
|
|
804
|
+
enum: ["category", "value"]
|
|
805
|
+
},
|
|
806
|
+
min: { type: "number" },
|
|
807
|
+
max: { type: "number" },
|
|
808
|
+
ticks: { type: "number" },
|
|
809
|
+
maxTicks: { type: "number" },
|
|
810
|
+
padding: { type: "number" },
|
|
811
|
+
headroom: { type: "number" },
|
|
812
|
+
formatter: { type: "function" }
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
var chart3dOptionsSchema = {
|
|
816
|
+
type: "object",
|
|
817
|
+
required: ["type"],
|
|
818
|
+
properties: {
|
|
819
|
+
type: {
|
|
820
|
+
type: "string",
|
|
821
|
+
enum: CHART3D_TYPES
|
|
822
|
+
},
|
|
823
|
+
labels: { type: "array" },
|
|
824
|
+
series: {
|
|
825
|
+
type: "array",
|
|
826
|
+
items: seriesSchema
|
|
827
|
+
},
|
|
828
|
+
pieData: {
|
|
829
|
+
type: "array",
|
|
830
|
+
items: pieItemSchema
|
|
831
|
+
},
|
|
832
|
+
scatterData: { type: "array" },
|
|
833
|
+
surfaceData: {
|
|
834
|
+
type: "object",
|
|
835
|
+
properties: {
|
|
836
|
+
x: { type: "array" },
|
|
837
|
+
y: { type: "array" },
|
|
838
|
+
z: {
|
|
839
|
+
type: "array",
|
|
840
|
+
items: { type: "array" }
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
},
|
|
844
|
+
matrix: {
|
|
845
|
+
type: "array",
|
|
846
|
+
items: { type: "array" }
|
|
847
|
+
},
|
|
848
|
+
palette: {
|
|
849
|
+
type: "string",
|
|
850
|
+
enum: CHART3D_PALETTE_IDS
|
|
851
|
+
},
|
|
852
|
+
theme: { type: "object" },
|
|
853
|
+
colors: { type: "array" },
|
|
854
|
+
camera: {
|
|
855
|
+
type: "object",
|
|
856
|
+
properties: {
|
|
857
|
+
yaw: { type: "number" },
|
|
858
|
+
pitch: { type: "number" },
|
|
859
|
+
distance: { type: "number" },
|
|
860
|
+
fov: { type: "number" },
|
|
861
|
+
target: { type: "array" },
|
|
862
|
+
minPitch: { type: "number" },
|
|
863
|
+
maxPitch: { type: "number" },
|
|
864
|
+
minDistance: { type: "number" },
|
|
865
|
+
maxDistance: { type: "number" },
|
|
866
|
+
autoRotate: { type: "boolean" },
|
|
867
|
+
autoRotateSpeed: { type: "number" },
|
|
868
|
+
damping: { type: "number" }
|
|
869
|
+
}
|
|
870
|
+
},
|
|
871
|
+
xAxis: axisSchema,
|
|
872
|
+
yAxis: axisSchema,
|
|
873
|
+
zAxis: axisSchema,
|
|
874
|
+
grid: {
|
|
875
|
+
type: "object",
|
|
876
|
+
properties: { show: { type: "boolean" } }
|
|
877
|
+
},
|
|
878
|
+
box: {
|
|
879
|
+
type: "object",
|
|
880
|
+
properties: {
|
|
881
|
+
show: { type: "boolean" },
|
|
882
|
+
walls: {
|
|
883
|
+
type: ["string", "boolean"],
|
|
884
|
+
enum: [
|
|
885
|
+
"all",
|
|
886
|
+
"back",
|
|
887
|
+
"side",
|
|
888
|
+
"none",
|
|
889
|
+
true,
|
|
890
|
+
false
|
|
891
|
+
]
|
|
892
|
+
},
|
|
893
|
+
frame: { type: "boolean" }
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
lighting: {
|
|
897
|
+
type: "object",
|
|
898
|
+
properties: {
|
|
899
|
+
ambient: { type: "number" },
|
|
900
|
+
intensity: { type: "number" },
|
|
901
|
+
follow: { type: "boolean" },
|
|
902
|
+
direction: { type: "array" },
|
|
903
|
+
flat: { type: "boolean" }
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
depth: {
|
|
907
|
+
type: "object",
|
|
908
|
+
properties: {
|
|
909
|
+
haze: { type: "number" },
|
|
910
|
+
edge: { type: "number" },
|
|
911
|
+
gradient: { type: "number" }
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
bar: {
|
|
915
|
+
type: "object",
|
|
916
|
+
properties: {
|
|
917
|
+
width: { type: "number" },
|
|
918
|
+
depth: { type: "number" },
|
|
919
|
+
shadow: { type: "boolean" },
|
|
920
|
+
shadowStrength: { type: "number" },
|
|
921
|
+
shadowOffset: { type: "array" }
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
line: {
|
|
925
|
+
type: "object",
|
|
926
|
+
properties: {
|
|
927
|
+
area: { type: "boolean" },
|
|
928
|
+
dropLines: { type: "boolean" },
|
|
929
|
+
width: { type: "number" },
|
|
930
|
+
points: { type: "boolean" },
|
|
931
|
+
pointRadius: { type: "number" }
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
scatter: {
|
|
935
|
+
type: "object",
|
|
936
|
+
properties: {
|
|
937
|
+
size: { type: "number" },
|
|
938
|
+
dropLines: { type: "boolean" },
|
|
939
|
+
colorScale: { type: ["boolean", "string"] },
|
|
940
|
+
depthScale: { type: "boolean" }
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
surface: {
|
|
944
|
+
type: "object",
|
|
945
|
+
properties: {
|
|
946
|
+
wireframe: { type: "boolean" },
|
|
947
|
+
opacity: { type: "number" },
|
|
948
|
+
ramp: { type: "string" }
|
|
949
|
+
}
|
|
950
|
+
},
|
|
951
|
+
pie: {
|
|
952
|
+
type: "object",
|
|
953
|
+
properties: {
|
|
954
|
+
radius: { type: "number" },
|
|
955
|
+
innerRadius: { type: "number" },
|
|
956
|
+
thickness: { type: "number" },
|
|
957
|
+
startAngle: { type: "number" },
|
|
958
|
+
padAngle: { type: "number" },
|
|
959
|
+
shadow: { type: "boolean" },
|
|
960
|
+
shadowStrength: { type: "number" }
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
legend: {
|
|
964
|
+
type: "object",
|
|
965
|
+
properties: {
|
|
966
|
+
show: { type: "boolean" },
|
|
967
|
+
position: {
|
|
968
|
+
type: "string",
|
|
969
|
+
enum: ["top", "bottom"]
|
|
970
|
+
},
|
|
971
|
+
align: {
|
|
972
|
+
type: "string",
|
|
973
|
+
enum: [
|
|
974
|
+
"start",
|
|
975
|
+
"center",
|
|
976
|
+
"end"
|
|
977
|
+
]
|
|
978
|
+
},
|
|
979
|
+
formatter: { type: "function" },
|
|
980
|
+
itemGap: { type: "number" }
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
tooltip: {
|
|
984
|
+
type: "object",
|
|
985
|
+
properties: {
|
|
986
|
+
show: { type: "boolean" },
|
|
987
|
+
trigger: {
|
|
988
|
+
type: "string",
|
|
989
|
+
enum: ["hover", "click"]
|
|
990
|
+
},
|
|
991
|
+
formatter: { type: "function" },
|
|
992
|
+
valueFormatter: { type: "function" }
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
label: {
|
|
996
|
+
type: "object",
|
|
997
|
+
properties: { show: { type: "boolean" } }
|
|
998
|
+
},
|
|
999
|
+
title: {
|
|
1000
|
+
type: ["string", "object"],
|
|
1001
|
+
properties: {
|
|
1002
|
+
text: { type: "string" },
|
|
1003
|
+
subtitle: { type: "string" },
|
|
1004
|
+
show: { type: "boolean" },
|
|
1005
|
+
left: { type: "number" }
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
animation: {
|
|
1009
|
+
type: "object",
|
|
1010
|
+
properties: {
|
|
1011
|
+
enabled: { type: "boolean" },
|
|
1012
|
+
duration: { type: "number" },
|
|
1013
|
+
easing: {
|
|
1014
|
+
type: "string",
|
|
1015
|
+
enum: [
|
|
1016
|
+
"linear",
|
|
1017
|
+
"easeIn",
|
|
1018
|
+
"easeOut",
|
|
1019
|
+
"easeInOut",
|
|
1020
|
+
"easeOutBack",
|
|
1021
|
+
"easeOutExpo"
|
|
1022
|
+
]
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
},
|
|
1026
|
+
ariaLabel: { type: "string" },
|
|
1027
|
+
lang: {
|
|
1028
|
+
type: "string",
|
|
1029
|
+
enum: ["zh", "en"]
|
|
1030
|
+
},
|
|
1031
|
+
i18n: { type: "object" }
|
|
1032
|
+
}
|
|
1033
|
+
};
|
|
1034
|
+
var TYPE_CHECKS = {
|
|
1035
|
+
object: (v) => v !== null && typeof v === "object" && !Array.isArray(v),
|
|
1036
|
+
array: (v) => Array.isArray(v),
|
|
1037
|
+
string: (v) => typeof v === "string",
|
|
1038
|
+
number: (v) => typeof v === "number" && Number.isFinite(v),
|
|
1039
|
+
boolean: (v) => typeof v === "boolean",
|
|
1040
|
+
function: (v) => typeof v === "function"
|
|
1041
|
+
};
|
|
1042
|
+
function typeMatches(node, value) {
|
|
1043
|
+
return (Array.isArray(node.type) ? node.type : [node.type]).some((t) => {
|
|
1044
|
+
const check = TYPE_CHECKS[t];
|
|
1045
|
+
return check ? check(value) : true;
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
function typeName(value) {
|
|
1049
|
+
if (value === null) return "null";
|
|
1050
|
+
if (Array.isArray(value)) return "array";
|
|
1051
|
+
return typeof value;
|
|
1052
|
+
}
|
|
1053
|
+
function validateNode(node, value, path, warnings) {
|
|
1054
|
+
if (!node || typeof node !== "object") return;
|
|
1055
|
+
if (node.type && !typeMatches(node, value)) {
|
|
1056
|
+
warnings.push({
|
|
1057
|
+
path,
|
|
1058
|
+
message: Array.isArray(node.type) ? `类型不匹配(应为 ${node.type.join(" / ")} 之一)` : `应为 ${node.type},实际为 ${typeName(value)}`
|
|
1059
|
+
});
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
if (node.enum && !node.enum.includes(value)) {
|
|
1063
|
+
warnings.push({
|
|
1064
|
+
path,
|
|
1065
|
+
message: `应为 ${node.enum.map((e) => String(e)).join(" / ")} 之一,实际为 ${String(value)}`
|
|
1066
|
+
});
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
if (node.type === "object" && TYPE_CHECKS.object(value)) {
|
|
1070
|
+
for (const key of node.required || []) if (!(key in value)) warnings.push({
|
|
1071
|
+
path: `${path}.${key}`,
|
|
1072
|
+
message: "缺少必填字段"
|
|
1073
|
+
});
|
|
1074
|
+
if (node.properties) {
|
|
1075
|
+
for (const [key, sub] of Object.entries(node.properties)) if (key in value && value[key] !== void 0 && value[key] !== null) validateNode(sub, value[key], `${path}.${key}`, warnings);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (node.type === "array" && Array.isArray(value)) {
|
|
1079
|
+
if (node.items) value.forEach((item, i) => validateNode(node.items, item, `${path}[${i}]`, warnings));
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* 校验 options
|
|
1084
|
+
* @returns {{ok: boolean, warnings: Array<{path:string, message:string}>}}
|
|
1085
|
+
*/
|
|
1086
|
+
function validateOptions3d(options) {
|
|
1087
|
+
const warnings = [];
|
|
1088
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) return {
|
|
1089
|
+
ok: false,
|
|
1090
|
+
warnings: [{
|
|
1091
|
+
path: "options",
|
|
1092
|
+
message: "应为对象"
|
|
1093
|
+
}]
|
|
1094
|
+
};
|
|
1095
|
+
validateNode(chart3dOptionsSchema, options, "options", warnings);
|
|
1096
|
+
return {
|
|
1097
|
+
ok: warnings.length === 0,
|
|
1098
|
+
warnings
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
//#endregion
|
|
1102
|
+
export { DOWN_COLOR as C, getSeriesColors$1 as D, formatValue as E, readChartToken as O, DEFAULT_I18N_ZH$1 as S, VOLUME_SERIES_NAME as T, shadeColor as _, CHART3D_RAMPS as a, resolveChartPalette as b, CHART_COLORS as c, resolveEasing as d, resolveI18n as f, mixHue as g, lighten as h, CHART3D_PALETTE_IDS as i, easings as l, gradientAt as m, validateOptions3d as n, resolveChartRamp as o, darken as p, CHART3D_PALETTES as r, CHART3D_TYPES as s, chart3dOptionsSchema as t, getTheme as u, toRgba as v, UP_COLOR as w, CHART_COLORS$1 as x, CHART_PALETTES as y };
|