agentcss 0.0.0-stage → 0.11.3
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/01-tokens/README.md +236 -0
- package/01-tokens/behavior/dismissable.js +132 -0
- package/01-tokens/behavior/emit.js +76 -0
- package/01-tokens/behavior/focus-return.js +81 -0
- package/01-tokens/behavior/roving.js +152 -0
- package/01-tokens/behavior/typeahead.js +124 -0
- package/01-tokens/focus-ring-demo.html +133 -0
- package/01-tokens/focus-ring.css +102 -0
- package/01-tokens/page.css +244 -0
- package/01-tokens/polyfill.js +116 -0
- package/01-tokens/responsive-demo.html +171 -0
- package/01-tokens/scrollbar-demo.html +194 -0
- package/01-tokens/scrollbar.css +179 -0
- package/01-tokens/theme-toggle.js +103 -0
- package/01-tokens/tokens.css +972 -0
- package/01-tokens/typography.css +156 -0
- package/02-primitives/badge/README.md +71 -0
- package/02-primitives/badge/badge.css +141 -0
- package/02-primitives/badge/demo.html +101 -0
- package/02-primitives/button/README.md +99 -0
- package/02-primitives/button/button.css +353 -0
- package/02-primitives/button/demo.html +200 -0
- package/02-primitives/card/README.md +145 -0
- package/02-primitives/card/card.css +246 -0
- package/02-primitives/card/demo.html +170 -0
- package/02-primitives/choice/README.md +161 -0
- package/02-primitives/choice/choice.css +395 -0
- package/02-primitives/choice/demo.html +277 -0
- package/02-primitives/combobox/README.md +79 -0
- package/02-primitives/combobox/combobox.css +223 -0
- package/02-primitives/combobox/combobox.js +485 -0
- package/02-primitives/combobox/demo.html +109 -0
- package/02-primitives/date-range/README.md +100 -0
- package/02-primitives/date-range/date-range.css +206 -0
- package/02-primitives/date-range/date-range.js +301 -0
- package/02-primitives/date-range/demo.html +104 -0
- package/02-primitives/input/README.md +170 -0
- package/02-primitives/input/demo.html +246 -0
- package/02-primitives/input/input.css +239 -0
- package/02-primitives/popover/README.md +80 -0
- package/02-primitives/popover/demo.html +150 -0
- package/02-primitives/popover/popover.css +187 -0
- package/02-primitives/popover/popover.js +331 -0
- package/02-primitives/progress/README.md +82 -0
- package/02-primitives/progress/demo.html +154 -0
- package/02-primitives/progress/progress.css +172 -0
- package/02-primitives/select/README.md +95 -0
- package/02-primitives/select/demo.html +121 -0
- package/02-primitives/select/select.css +151 -0
- package/02-primitives/select/select.js +549 -0
- package/02-primitives/separator/README.md +63 -0
- package/02-primitives/separator/demo.html +84 -0
- package/02-primitives/separator/separator.css +73 -0
- package/02-primitives/skeleton/README.md +69 -0
- package/02-primitives/skeleton/demo.html +110 -0
- package/02-primitives/skeleton/skeleton.css +96 -0
- package/02-primitives/switch/README.md +65 -0
- package/02-primitives/switch/demo.html +115 -0
- package/02-primitives/switch/switch.css +370 -0
- package/03-patterns/accordion/README.md +121 -0
- package/03-patterns/accordion/accordion.css +453 -0
- package/03-patterns/accordion/accordion.js +369 -0
- package/03-patterns/accordion/demo.html +223 -0
- package/03-patterns/content/README.md +103 -0
- package/03-patterns/content/content.css +286 -0
- package/03-patterns/content/demo.html +88 -0
- package/03-patterns/drawer/README.md +79 -0
- package/03-patterns/drawer/demo.html +101 -0
- package/03-patterns/drawer/drawer.css +129 -0
- package/03-patterns/drawer/drawer.js +258 -0
- package/03-patterns/dropdown/README.md +78 -0
- package/03-patterns/dropdown/demo.html +101 -0
- package/03-patterns/dropdown/dropdown.css +135 -0
- package/03-patterns/dropdown/dropdown.js +451 -0
- package/03-patterns/form-validation/README.md +147 -0
- package/03-patterns/form-validation/demo.html +280 -0
- package/03-patterns/form-validation/form-validation.css +263 -0
- package/03-patterns/list/README.md +150 -0
- package/03-patterns/list/demo.html +225 -0
- package/03-patterns/list/flip.js +201 -0
- package/03-patterns/list/list.css +162 -0
- package/03-patterns/nav/README.md +225 -0
- package/03-patterns/nav/demo.html +131 -0
- package/03-patterns/nav/nav.css +314 -0
- package/03-patterns/nav/nav.js +430 -0
- package/03-patterns/nav/toc.js +111 -0
- package/03-patterns/overlay/README.md +196 -0
- package/03-patterns/overlay/demo.html +241 -0
- package/03-patterns/overlay/overlay.css +319 -0
- package/03-patterns/overlay/overlay.js +563 -0
- package/03-patterns/pagination/README.md +114 -0
- package/03-patterns/pagination/demo.html +165 -0
- package/03-patterns/pagination/pagination.css +195 -0
- package/03-patterns/pagination/pagination.js +395 -0
- package/03-patterns/reveal/README.md +91 -0
- package/03-patterns/reveal/demo.html +106 -0
- package/03-patterns/reveal/reveal.css +93 -0
- package/03-patterns/reveal/reveal.js +172 -0
- package/03-patterns/states/README.md +141 -0
- package/03-patterns/states/demo.html +240 -0
- package/03-patterns/states/states.css +252 -0
- package/03-patterns/tabs/README.md +84 -0
- package/03-patterns/tabs/demo.html +139 -0
- package/03-patterns/tabs/tabs.css +172 -0
- package/03-patterns/tabs/tabs.js +307 -0
- package/03-patterns/tooltip/README.md +96 -0
- package/03-patterns/tooltip/demo.html +78 -0
- package/03-patterns/tooltip/tooltip.css +89 -0
- package/03-patterns/tooltip/tooltip.js +288 -0
- package/03-patterns/tree/README.md +94 -0
- package/03-patterns/tree/demo.html +138 -0
- package/03-patterns/tree/tree.css +113 -0
- package/03-patterns/tree/tree.js +456 -0
- package/04-recipes/analysis-report/README.md +80 -0
- package/04-recipes/analysis-report/analysis-report.css +187 -0
- package/04-recipes/analysis-report/demo.html +274 -0
- package/04-recipes/data-showcase/README.md +45 -0
- package/04-recipes/data-showcase/demo.html +247 -0
- package/04-recipes/detail-page/README.md +93 -0
- package/04-recipes/detail-page/demo.html +55 -0
- package/04-recipes/detail-page/detail-page.css +190 -0
- package/04-recipes/detail-page/full.html +60 -0
- package/04-recipes/detail-page/null.html +60 -0
- package/04-recipes/detail-page/partial.html +60 -0
- package/04-recipes/detail-page/verify-skeleton.py +19 -0
- package/04-recipes/longform/README.md +93 -0
- package/04-recipes/longform/agc-memory.svg +4 -0
- package/04-recipes/longform/content.html +610 -0
- package/04-recipes/longform/content.md +51 -0
- package/04-recipes/longform/longform.html +627 -0
- package/04-recipes/report/README.md +176 -0
- package/04-recipes/report/demo.html +355 -0
- package/04-recipes/report/report.css +364 -0
- package/04-recipes/table/README.md +103 -0
- package/04-recipes/table/demo.html +455 -0
- package/04-recipes/table/table.css +372 -0
- package/04-recipes/tierA-tokens.css +189 -0
- package/09-assets/README.md +74 -0
- package/09-assets/bar/README.md +94 -0
- package/09-assets/bar/bar.css +166 -0
- package/09-assets/bar/bar.js +320 -0
- package/09-assets/bar/demo.html +176 -0
- package/09-assets/echarts-adapter/demo.html +192 -0
- package/09-assets/echarts-adapter/echarts-adapter.js +277 -0
- package/09-assets/model-viewer/README.md +155 -0
- package/09-assets/model-viewer/demo.html +105 -0
- package/09-assets/model-viewer/model-viewer.css +143 -0
- package/09-assets/model-viewer/model-viewer.js +423 -0
- package/09-assets/scientific-plot/README.md +94 -0
- package/09-assets/scientific-plot/demo.html +252 -0
- package/09-assets/scientific-plot/scientific-plot.css +141 -0
- package/09-assets/scientific-plot/scientific-plot.js +610 -0
- package/09-assets/sparkline/README.md +153 -0
- package/09-assets/sparkline/demo.html +163 -0
- package/09-assets/sparkline/sparkline.css +158 -0
- package/09-assets/sparkline/sparkline.js +508 -0
- package/API.md +332 -0
- package/CHANGELOG.md +3138 -0
- package/CODE_OF_CONDUCT.md +64 -0
- package/CONTRIBUTING.md +89 -0
- package/ERRORS.md +1658 -0
- package/LICENSE +21 -0
- package/README.md +497 -2
- package/SECURITY.md +72 -0
- package/START-HERE.md +164 -0
- package/SUPPORT.md +109 -0
- package/adapters/presentation/README.md +113 -0
- package/adapters/presentation/check.js +101 -0
- package/adapters/presentation/deck.schema.json +74 -0
- package/adapters/presentation/to-doc.js +138 -0
- package/ai/START-HERE.md +104 -0
- package/ai/behaviors.json +380 -0
- package/ai/behaviors.schema.json +61 -0
- package/ai/capabilities.json +56 -0
- package/ai/cli.js +387 -0
- package/ai/components.json +1944 -0
- package/ai/components.meta.json +168 -0
- package/ai/components.schema.json +220 -0
- package/ai/contract.schema.json +147 -0
- package/ai/deprecations.json +55 -0
- package/ai/deprecations.schema.json +85 -0
- package/ai/motion-policy.json +22 -0
- package/ai/patch.js +323 -0
- package/ai/patch.schema.json +63 -0
- package/ai/profiles.json +92 -0
- package/ai/tokens.audience.json +55 -0
- package/ai/tokens.audience.schema.json +73 -0
- package/ai/tokens.json +762 -0
- package/ai/tokens.schema.json +58 -0
- package/ai/tokens.tree.json +752 -0
- package/ai/tokens.tree.schema.json +56 -0
- package/ai/validate.js +383 -0
- package/dist/01-tokens/behavior/dismissable.js +60 -0
- package/dist/01-tokens/behavior/emit.js +21 -0
- package/dist/01-tokens/behavior/focus-return.js +31 -0
- package/dist/01-tokens/behavior/roving.js +77 -0
- package/dist/01-tokens/behavior/typeahead.js +61 -0
- package/dist/01-tokens/focus-ring.css +1 -0
- package/dist/01-tokens/page.css +1 -0
- package/dist/01-tokens/polyfill.js +78 -0
- package/dist/01-tokens/scrollbar.css +1 -0
- package/dist/01-tokens/theme-toggle.js +61 -0
- package/dist/01-tokens/tokens.css +1 -0
- package/dist/01-tokens/typography.css +1 -0
- package/dist/02-primitives/badge/badge.css +1 -0
- package/dist/02-primitives/button/button.css +1 -0
- package/dist/02-primitives/card/card.css +1 -0
- package/dist/02-primitives/choice/choice.css +1 -0
- package/dist/02-primitives/combobox/combobox.css +1 -0
- package/dist/02-primitives/combobox/combobox.js +439 -0
- package/dist/02-primitives/date-range/date-range.css +1 -0
- package/dist/02-primitives/date-range/date-range.js +267 -0
- package/dist/02-primitives/input/input.css +1 -0
- package/dist/02-primitives/popover/popover.css +1 -0
- package/dist/02-primitives/popover/popover.js +257 -0
- package/dist/02-primitives/progress/progress.css +1 -0
- package/dist/02-primitives/select/select.css +1 -0
- package/dist/02-primitives/select/select.js +449 -0
- package/dist/02-primitives/separator/separator.css +1 -0
- package/dist/02-primitives/skeleton/skeleton.css +1 -0
- package/dist/02-primitives/switch/switch.css +1 -0
- package/dist/03-patterns/accordion/accordion.css +1 -0
- package/dist/03-patterns/accordion/accordion.js +219 -0
- package/dist/03-patterns/content/content.css +1 -0
- package/dist/03-patterns/drawer/drawer.css +1 -0
- package/dist/03-patterns/drawer/drawer.js +229 -0
- package/dist/03-patterns/dropdown/dropdown.css +1 -0
- package/dist/03-patterns/dropdown/dropdown.js +365 -0
- package/dist/03-patterns/form-validation/form-validation.css +1 -0
- package/dist/03-patterns/list/flip.js +117 -0
- package/dist/03-patterns/list/list.css +1 -0
- package/dist/03-patterns/nav/nav.css +1 -0
- package/dist/03-patterns/nav/nav.js +322 -0
- package/dist/03-patterns/nav/toc.js +76 -0
- package/dist/03-patterns/overlay/overlay.css +1 -0
- package/dist/03-patterns/overlay/overlay.js +398 -0
- package/dist/03-patterns/pagination/pagination.css +1 -0
- package/dist/03-patterns/pagination/pagination.js +273 -0
- package/dist/03-patterns/reveal/reveal.css +1 -0
- package/dist/03-patterns/reveal/reveal.js +138 -0
- package/dist/03-patterns/states/states.css +1 -0
- package/dist/03-patterns/tabs/tabs.css +1 -0
- package/dist/03-patterns/tabs/tabs.js +242 -0
- package/dist/03-patterns/tooltip/tooltip.css +1 -0
- package/dist/03-patterns/tooltip/tooltip.js +213 -0
- package/dist/03-patterns/tree/tree.css +1 -0
- package/dist/03-patterns/tree/tree.js +383 -0
- package/dist/04-recipes/analysis-report/analysis-report.css +1 -0
- package/dist/04-recipes/detail-page/detail-page.css +1 -0
- package/dist/04-recipes/report/report.css +1 -0
- package/dist/04-recipes/table/table.css +1 -0
- package/dist/04-recipes/tierA-tokens.css +1 -0
- package/dist/09-assets/bar/bar.css +1 -0
- package/dist/09-assets/bar/bar.js +222 -0
- package/dist/09-assets/echarts-adapter/echarts-adapter.js +185 -0
- package/dist/09-assets/model-viewer/model-viewer.css +1 -0
- package/dist/09-assets/model-viewer/model-viewer.js +320 -0
- package/dist/09-assets/scientific-plot/scientific-plot.css +1 -0
- package/dist/09-assets/scientific-plot/scientific-plot.js +568 -0
- package/dist/09-assets/sparkline/sparkline.css +1 -0
- package/dist/09-assets/sparkline/sparkline.js +265 -0
- package/dist/COSTS.md +56 -0
- package/docs/AI-CONTRACT.md +197 -0
- package/docs/BACKLOG.md +127 -0
- package/docs/BENCHMARK.md +252 -0
- package/docs/GUIDANCE.md +154 -0
- package/docs/INVARIANT.md +372 -0
- package/docs/LAYER-BASELINE.md +158 -0
- package/docs/RELEASING.md +311 -0
- package/docs/RFC-THEME-SCOPE.md +141 -0
- package/docs/ROADMAP.md +182 -0
- package/index.html +82 -0
- package/package.json +107 -4
- package/types/index.d.ts +310 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* sparkline.js — 迷你趋势图(零依赖 · ES5 · 令牌驱动)
|
|
3
|
+
*
|
|
4
|
+
* ============================================================================
|
|
5
|
+
* 为什么自己写而不引库
|
|
6
|
+
* ============================================================================
|
|
7
|
+
* 调研了 GitHub 上几个高质量的零依赖方案,最小的
|
|
8
|
+
* `mitjafelicijan/sparklines` 是 **6390 字节 / 195 行 / gzip 1675 字节**,
|
|
9
|
+
* BSD-2 许可(可商用,只需保留声明)—— 结论是**它做得很好,值得参考**。
|
|
10
|
+
*
|
|
11
|
+
* 但**没有直接引**,两个原因(都不是"嫌它差"):
|
|
12
|
+
*
|
|
13
|
+
* ① **它是 ES6**:`const initSparkline = (sparkline) => {...}`
|
|
14
|
+
* 本库的硬约束是 **ES5**(精简档 跑在 某项目 的 WebView 上,
|
|
15
|
+
* 版本不确定)。`const` / 箭头函数在老 WebView 上直接语法错误。
|
|
16
|
+
*
|
|
17
|
+
* ② **它是 data 属性驱动 + 颜色写死**:示例里 `data-colors="gray"`
|
|
18
|
+
* 或直接给 HEX。本库要求**令牌驱动**(换主题要跟着变)。
|
|
19
|
+
*
|
|
20
|
+
* ⇒ 所以:**借鉴它的接口设计(data 属性驱动、零依赖、BSD 思路)**,
|
|
21
|
+
* 实现按本库约束重写。这不是"标准不够就自创",
|
|
22
|
+
* 是**在同一目标下按本库既定约束选实现方式**。
|
|
23
|
+
*
|
|
24
|
+
* ============================================================================
|
|
25
|
+
* 用法
|
|
26
|
+
* ============================================================================
|
|
27
|
+
* <span class="sparkline" data-spark="12,18,15,24,22,31,28"></span>
|
|
28
|
+
*
|
|
29
|
+
* 可选属性:
|
|
30
|
+
* data-spark-type line(默认)| bar | area
|
|
31
|
+
* data-spark-height 像素,默认 32
|
|
32
|
+
* data-spark-color CSS 颜色或 var(--x),默认 var(--accent)
|
|
33
|
+
* data-spark-fill yes(默认,area 类型填充)| no
|
|
34
|
+
* data-spark-last yes(标出最后一个点)| no
|
|
35
|
+
* data-spark-animate yes(默认)| no
|
|
36
|
+
*
|
|
37
|
+
* 更新数据:
|
|
38
|
+
* el.setAttribute('data-spark', '1,2,3');
|
|
39
|
+
* Chart.emit(el); // 重画
|
|
40
|
+
*
|
|
41
|
+
* ============================================================================
|
|
42
|
+
* 🔴 无障碍:这不是"图表",是**趋势的辅助提示**
|
|
43
|
+
* ============================================================================
|
|
44
|
+
* 纯 SVG 火花线对读屏**没有意义**(一堆路径,读屏只会念"图像")。
|
|
45
|
+
* ⇒ 本组件**不做 role="img"**(那会让人以为它有可读的文本等价物)。
|
|
46
|
+
* ⇒ **要给出可读的值**,请在旁边放一个数字或用 states 组件里的
|
|
47
|
+
* "空/错/加载"那套 —— 数字本身才是可访问的信息。
|
|
48
|
+
* ⇒ 什么时候**必须**配文字:数值重要时(例如"营收 128 万,同比 +3.2%")。
|
|
49
|
+
* 纯装饰性的趋势提示(列表右侧那种小图)可以不配。
|
|
50
|
+
*/
|
|
51
|
+
(function () {
|
|
52
|
+
'use strict';
|
|
53
|
+
|
|
54
|
+
var NS = 'http://www.w3.org/2000/svg';
|
|
55
|
+
var instances = [];
|
|
56
|
+
|
|
57
|
+
/* ---------- 小工具(ES5,不用 Array.from 等) */
|
|
58
|
+
function toArray(x) { return Array.prototype.slice.call(x); }
|
|
59
|
+
function el(name, attrs) {
|
|
60
|
+
var n = document.createElementNS(NS, name);
|
|
61
|
+
if (attrs) for (var k in attrs) if (attrs.hasOwnProperty(k)) n.setAttribute(k, attrs[k]);
|
|
62
|
+
return n;
|
|
63
|
+
}
|
|
64
|
+
function num(v, d) { var n = parseFloat(v); return isNaN(n) ? d : n; }
|
|
65
|
+
|
|
66
|
+
/* ---------- 解析:MISSING != ZERO ----------
|
|
67
|
+
*
|
|
68
|
+
* 🔴 修一个**真实的语义错误**(借鉴 演示项目名 的图表契约:
|
|
69
|
+
* `MISSING != ZERO / INVALID != MISSING / 过滤必须可见`)。
|
|
70
|
+
*
|
|
71
|
+
* 原来这里写的是:
|
|
72
|
+
* if (!isNaN(v)) out.push(v); // 跳过空项
|
|
73
|
+
* 后果有两处,都很坏:
|
|
74
|
+
* ① `"1,,3"` 变成 `1,3` ⇒ **画成一条直线**,
|
|
75
|
+
* 看起来像"从 1 平滑降到 3",实际是"1,然后**没数据**,然后 3"
|
|
76
|
+
* ② `"1,0,3"`(0 是真值)与 `"1,,3"`(缺失)**在图上完全一样**
|
|
77
|
+
* ⇒ 缺失被静默当成"没这个点",而不是"这里没数据"
|
|
78
|
+
*
|
|
79
|
+
* ⇒ 改为:**空值保留为 null**,画的时候**断开**(不跨空值连线)。
|
|
80
|
+
*
|
|
81
|
+
* 为什么这么重要:一条**假装连续**的线会让人读出不存在的趋势
|
|
82
|
+
* —— 数据缺失被当成了"平稳过渡"。这是图表最容易被忽略、
|
|
83
|
+
* 也最容易误导人的一种错。
|
|
84
|
+
*/
|
|
85
|
+
function parsePoints(spark) {
|
|
86
|
+
var raw = spark.getAttribute('data-spark') || '';
|
|
87
|
+
if (!raw.trim()) return [];
|
|
88
|
+
var parts = raw.split(',');
|
|
89
|
+
var out = [];
|
|
90
|
+
for (var i = 0; i < parts.length; i++) {
|
|
91
|
+
var t = parts[i].trim();
|
|
92
|
+
// 空串 / 各种"无数据"写法 ⇒ null(**不是 0**)
|
|
93
|
+
if (t === '' || t === '-' || t === 'null' || t === 'NaN') {
|
|
94
|
+
out.push(null);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
var v = parseFloat(t);
|
|
98
|
+
out.push(isNaN(v) ? null : v); // 非法值也当 null,但**在下方标出来**
|
|
99
|
+
}
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function readOpts(spark) {
|
|
104
|
+
return {
|
|
105
|
+
type: spark.getAttribute('data-spark-type') || 'line',
|
|
106
|
+
height: num(spark.getAttribute('data-spark-height'), 32),
|
|
107
|
+
color: spark.getAttribute('data-spark-color') || 'var(--accent)',
|
|
108
|
+
fill: (spark.getAttribute('data-spark-fill') || 'yes') !== 'no',
|
|
109
|
+
last: (spark.getAttribute('data-spark-last') || 'no') === 'yes',
|
|
110
|
+
animate: (spark.getAttribute('data-spark-animate') || 'yes') !== 'no',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ---------- 几何:算出所有 x/y ---------- */
|
|
115
|
+
function scale(points, w, h, pad) {
|
|
116
|
+
// 🔴 MISSING != ZERO:**求最值时必须排除 null**。
|
|
117
|
+
// Math.min(1, null, 3) === 1(null 会被当 0)——
|
|
118
|
+
// 若数据全是正数,null 参与计算会把 min 拉低,图被压扁。
|
|
119
|
+
var vals = [];
|
|
120
|
+
for (var k = 0; k < points.length; k++) {
|
|
121
|
+
if (points[k] !== null) vals.push(points[k]);
|
|
122
|
+
}
|
|
123
|
+
if (!vals.length) return null; // 全是缺失 ⇒ 画不了
|
|
124
|
+
var min = Math.min.apply(null, vals);
|
|
125
|
+
var max = Math.max.apply(null, vals);
|
|
126
|
+
// 全平的一条线:给一个人造的上下界,否则除以 0
|
|
127
|
+
if (max === min) { max = min + 1; min = min - 1; }
|
|
128
|
+
var span = max - min;
|
|
129
|
+
var n = points.length;
|
|
130
|
+
var xs = [], ys = [];
|
|
131
|
+
for (var i = 0; i < n; i++) {
|
|
132
|
+
xs.push(n === 1 ? w / 2 : pad + (w - pad * 2) * (i / (n - 1)));
|
|
133
|
+
// 🔴 缺失点的 y 保持 null,draw() 会据此断开
|
|
134
|
+
ys.push(points[i] === null ? null
|
|
135
|
+
: h - pad - (h - pad * 2) * ((points[i] - min) / span));
|
|
136
|
+
}
|
|
137
|
+
return { xs: xs, ys: ys, min: min, max: max };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* ---------- 动画:优先 CSS(可被 reduced-motion 关掉) ---------- */
|
|
141
|
+
function supportsReducedMotion() {
|
|
142
|
+
return typeof window.matchMedia === 'function' &&
|
|
143
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ---------- 画 ---------- */
|
|
147
|
+
/* 🔴 修 iOS 实机报的「滑动时一闪一闪」
|
|
148
|
+
|
|
149
|
+
真因(实测定位,两条叠加):
|
|
150
|
+
① **svg 节点每次都被重建** —— draw() 先清空全部子节点再重建。
|
|
151
|
+
而 iOS **地址栏随滑动收缩/展开 ⇒ 触发 resize ⇒ ResizeObserver 重绘**
|
|
152
|
+
⇒ 每一次滑动都把整张图换一遍节点 ⇒ **闪一下**。
|
|
153
|
+
② **`animation: spark-draw` 被反复重播** ——
|
|
154
|
+
dasharray=1000px 从 1000 画到 0,每次重绘都从头画一遍。
|
|
155
|
+
|
|
156
|
+
⇒ 修法:
|
|
157
|
+
① **尺寸没变就不重绘**(宽度不变直接 return)—— 这一条就消掉了绝大部分闪烁
|
|
158
|
+
② 重绘时**不加动画类**(动画只该在「首次出现」时播一次)
|
|
159
|
+
*/
|
|
160
|
+
function draw(spark) {
|
|
161
|
+
var pts = parsePoints(spark);
|
|
162
|
+
|
|
163
|
+
/* 🔴 修「滑动时一闪一闪」—— **顺序错了,闪得离谱**
|
|
164
|
+
|
|
165
|
+
原来这里是「**先清空** → 再量 w → 再比签名 → 才 return」:
|
|
166
|
+
|
|
167
|
+
var old = spark.firstChild;
|
|
168
|
+
while (old) { …removeChild… } ← 🔴 清空发生在 return 之前
|
|
169
|
+
…量 w、算 sig、if (没变) return… ← 我加的短路,**根本没机会生效**
|
|
170
|
+
|
|
171
|
+
⇒ 节点已经先被清掉了,每次都重建一遍新的。
|
|
172
|
+
iOS 滑动 ⇒ 地址栏收缩 ⇒ resize ⇒ ResizeObserver 回调
|
|
173
|
+
⇒ 每一次滑动都清空重建 ⇒ **肉眼一闪一闪**(真机实测反馈)。
|
|
174
|
+
|
|
175
|
+
⇒ 修:**先量尺寸、先比签名,命中就 return,一个子节点都不碰。**
|
|
176
|
+
清空动作挪到确认"确实要重画"之后。 */
|
|
177
|
+
var wQuick = Math.round(spark.getBoundingClientRect().width);
|
|
178
|
+
var sigQuick = wQuick + '|' + (spark.getAttribute('data-spark') || '');
|
|
179
|
+
var isFirstQuick = spark.__sparkSig === undefined;
|
|
180
|
+
if (!isFirstQuick && spark.__sparkSig === sigQuick && spark.firstChild) {
|
|
181
|
+
return; // 🔴 没变 ⇒ 什么都不做(更不清空)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 确认要重画了,这一步才清空
|
|
185
|
+
var old = spark.firstChild;
|
|
186
|
+
while (old) { var n = old.nextSibling; spark.removeChild(old); old = n; }
|
|
187
|
+
if (pts.length < 2) return; // 少于 2 个点画不出趋势
|
|
188
|
+
|
|
189
|
+
var o = readOpts(spark);
|
|
190
|
+
/* 🔴 修「末点圆点被吃掉一部分」(真机实测反馈,**全局都有**)的
|
|
191
|
+
**真正根因** —— 不是 pad 太小,是 **量到的宽度不对**。
|
|
192
|
+
|
|
193
|
+
实测证据:viewBox 宽度 **699**,而容器实际只有 **300px**。
|
|
194
|
+
⇒ SVG 被横向压缩到 43% ⇒ **末点圆点被压扁、被裁**。
|
|
195
|
+
|
|
196
|
+
为什么会量到 699:
|
|
197
|
+
`clientWidth` 在 **flex 布局尚未稳定**时读到的是**收缩前**的宽度
|
|
198
|
+
(那一列此刻还占着整行)。我原来的 pad=2 之所以"看起来对",
|
|
199
|
+
是因为它和这个错误的宽度是**配套错的**。
|
|
200
|
+
|
|
201
|
+
⇒ 修法两条,缺一不可:
|
|
202
|
+
① 用 `getBoundingClientRect().width` —— 它给的是**当前实际渲染宽度**;
|
|
203
|
+
② 布局变化后**重绘**(ResizeObserver 或至少 window.resize),
|
|
204
|
+
因为 flex 收缩是**稍后才发生的**。
|
|
205
|
+
|
|
206
|
+
⚠️ 只做 ① 不够:初次绘制时 rect 仍可能是旧值。
|
|
207
|
+
*/
|
|
208
|
+
var w = Math.round(spark.getBoundingClientRect().width)
|
|
209
|
+
|| spark.clientWidth
|
|
210
|
+
|| num(spark.getAttribute('data-spark-width'), 120);
|
|
211
|
+
var h = o.height;
|
|
212
|
+
|
|
213
|
+
/* 🔴 修「滑动时一闪一闪」的核心:**尺寸没变就别重绘**
|
|
214
|
+
|
|
215
|
+
iOS 的**地址栏会随滑动收缩/展开** ⇒ 触发 resize ⇒ ResizeObserver 回调
|
|
216
|
+
⇒ 而 sparkline 的**宽度根本没变**(只高度变)⇒
|
|
217
|
+
每一次滑动都把整张图的 DOM 节点换一遍 ⇒ **肉眼看到"闪"**。
|
|
218
|
+
|
|
219
|
+
⇒ 缓存上次画过的 (w, h, 数据),三者都没变就**直接返回**。
|
|
220
|
+
这一条就能消掉绝大部分闪烁,而且零成本。
|
|
221
|
+
|
|
222
|
+
⚠️ 数据也必须参与比较 —— 「更新数据」按钮会改 data-spark,
|
|
223
|
+
那时**必须**重绘,不能被这个优化挡住。 */
|
|
224
|
+
/* 签名已在函数开头比对过(那里才能真正拦住清空),
|
|
225
|
+
这里只负责**记录**。签名不含 h:高度变了也要重画,
|
|
226
|
+
但那属于"用户主动改 data-spark-height",不是滑动。 */
|
|
227
|
+
spark.__sparkSig = sigQuick;
|
|
228
|
+
|
|
229
|
+
/* 🔴 加(真机实测反馈「趋势图和文字重叠」):
|
|
230
|
+
**把容器高度同步成图的高度**。
|
|
231
|
+
|
|
232
|
+
根因:CSS 里 `.sparkline { height: 32px }` 是写死的,
|
|
233
|
+
而 `data-spark-height` 可以是 48/56 ⇒ **容器装不下图**,
|
|
234
|
+
加上 `overflow: visible` ⇒ 溢出的线画到容器外、压在下边的文字上。
|
|
235
|
+
|
|
236
|
+
⇒ 在这里写 `--spark-h`,CSS 用 `height: var(--spark-h, 32px)` 读它。
|
|
237
|
+
**一处设置,CSS 与 JS 不可能再不一致。**
|
|
238
|
+
|
|
239
|
+
⚠️ 用 setProperty 而不是 style.height:
|
|
240
|
+
那样会与 CSS 的 `height` 打架(行内样式优先级更高,改主题时更难看)。 */
|
|
241
|
+
spark.style.setProperty('--spark-h', h + 'px');
|
|
242
|
+
|
|
243
|
+
/* 🔴 修「末点圆点被裁掉一半」(真机实测反馈,且**全局都有**)
|
|
244
|
+
|
|
245
|
+
实测:8/8 张图的折线末点都只有 **2px** 余量,
|
|
246
|
+
而末点圆点半径是 **2.5px** ⇒ `overflow:hidden` **必裁掉一半**。
|
|
247
|
+
|
|
248
|
+
⭐ 真因:`pad` 是**写死的 2**,没有考虑"末端要画东西"。
|
|
249
|
+
|
|
250
|
+
⇒ 改成**按内容算余量**:
|
|
251
|
+
· 有末点圆点(r=2.5)⇒ 至少留 `r + 1`
|
|
252
|
+
· 有柱状图 ⇒ 至少留半个柱宽(否则最后一根也贴边)
|
|
253
|
+
· 折线默认 ⇒ 1.5px(线宽的一半,视觉上刚好)
|
|
254
|
+
|
|
255
|
+
⚠️ 之前我为了修「溢出压到文字」给 `.sparkline` 加了 `overflow:hidden`
|
|
256
|
+
—— 那是**必要的**(防溢出压到下边的字),
|
|
257
|
+
但**它必须与 pad 配合**:pad 不足 ⇒ 末端元素被裁。
|
|
258
|
+
两个 bug 是同一处代码的两面。
|
|
259
|
+
*/
|
|
260
|
+
/* pad 必须 ≥「末端绘制物的半径」,否则 overflow:hidden 会裁掉它。
|
|
261
|
+
下面 LAST_R / LAST_BAR 是各形态的末端绘制半径,统一在这里定,
|
|
262
|
+
**不要在别处各写一个数字** —— 那是这次 bug 的成因。 */
|
|
263
|
+
var LAST_R = 2.5; // 末点圆点半径(与下方 circle r 一致)
|
|
264
|
+
var LAST_BAR = 3; // 柱宽的一半(与下方 bw 一致)
|
|
265
|
+
/* 🔴 修「末点圆点被吃掉一半」—— 实测 cx=297.5, r=2.5,
|
|
266
|
+
viewBox 宽 300 ⇒ **圆心距边缘 2.5px = 半径** ⇒ 切线刚好在边缘,
|
|
267
|
+
`overflow:hidden` 就会切掉半个圆点。
|
|
268
|
+
|
|
269
|
+
⚠️ 之前的 pad 算成「r + 1.5」是**不够的** ——
|
|
270
|
+
scale() 的 x 公式是 `pad + (w - pad*2) * (i/(n-1))`,
|
|
271
|
+
最后一点 = **w - pad**,
|
|
272
|
+
所以 pad 必须 **≥ 半径**,而且要留一点余量才好看。
|
|
273
|
+
|
|
274
|
+
⇒ 这里给 LAST_R(不是 LAST_R + 1.5),让「pad = 圆点半径」成为
|
|
275
|
+
**可验证的等式**:圆心距边缘 = pad = r ⇒ 圆点完整可见。 */
|
|
276
|
+
// 🔴 pad=4 ⇒ 圆心x=w-4、圆点右缘=w-1.5 ⇒ **余 1.5px 呼吸**(算过)
|
|
277
|
+
// ⚠️⚠️ 这里**不能**写 `o.last === 'yes'` ——
|
|
278
|
+
// readOpts 里 `last:` 已经是**布尔值**(`=== 'yes'` 的结果)。
|
|
279
|
+
// 我曾把它当字符串比 ⇒ 永远 false ⇒ pad 恒为 1
|
|
280
|
+
// ⇒ 末点圆点**必然被裁一半**,而门禁一路报"余 -1.5px"我才发现。
|
|
281
|
+
var pad = o.last ? (LAST_R + 1.5) : 1;
|
|
282
|
+
var s = scale(pts, w, h, pad);
|
|
283
|
+
// 🔴 修崩溃:scale() 在"全是缺失"时返回 null,
|
|
284
|
+
// 而这里原来直接用了 s.xs ⇒ 抛 TypeError,**整个库挂掉**。
|
|
285
|
+
// (是我引入 scale 提前返回时漏了这条。)
|
|
286
|
+
if (!s) return; // 一个有效点都没有 ⇒ 画不了,也不该画
|
|
287
|
+
if (s.xs.length < 2) return; // 只有一个有效点,画不出趋势
|
|
288
|
+
|
|
289
|
+
var svg = el('svg', {
|
|
290
|
+
width: '100%', height: h,
|
|
291
|
+
viewBox: '0 0 ' + w + ' ' + h,
|
|
292
|
+
/* 🔴 **删掉 `preserveAspectRatio="none"`** ——
|
|
293
|
+
真机实测指出的问题(截图为准):趋势图变成一条「扁平的锯齿带」。
|
|
294
|
+
|
|
295
|
+
真因:`none` 允许 **x 与 y 方向独立缩放**。
|
|
296
|
+
本组件的 viewBox 宽度 = 容器**实际像素宽**(w 来自 clientWidth),
|
|
297
|
+
而 SVG 用 `width:100%` 渲染到同一个宽度 ⇒
|
|
298
|
+
**横向本来就不该缩放**,但 `none` 仍会让浏览器在
|
|
299
|
+
容器宽度 ≠ viewBox 宽度时(非整数舍入、滚动条出现、
|
|
300
|
+
flex 布局未稳定)把图形**横向拉伸** ⇒
|
|
301
|
+
1.5px 的线被拉成几像素宽的「斜面」,看起来像扁平的色带。
|
|
302
|
+
|
|
303
|
+
为什么它当初在:多半是想"让图一定填满容器"。
|
|
304
|
+
但**填满容器应该是布局的事**(width:100% 已经做了),
|
|
305
|
+
不该由 SVG 的缩放规则来兜 —— 那是**用视觉换正确性**。
|
|
306
|
+
|
|
307
|
+
⇒ 删掉后走默认的 `xMidYMid meet`:
|
|
308
|
+
- viewBox 宽高比 = 容器宽高比 ⇒ **1:1 渲染,1px 就是 1px**
|
|
309
|
+
- 即使比例略有出入,也只会**等比留边**,不会把线拉变形
|
|
310
|
+
*/
|
|
311
|
+
// 🔴 不用 role="img":见文件头的无障碍说明
|
|
312
|
+
'aria-hidden': 'true',
|
|
313
|
+
focusable: 'false',
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
/* 🔴 MISSING != ZERO 的落点:把序列切成**连续段**,**不跨空值连线**。
|
|
317
|
+
*
|
|
318
|
+
* 为什么必须这样:一条横跨缺失点的线,等于在说
|
|
319
|
+
* "从 1 平滑地涨到 3,中间一切正常" —— 而真相是"中间没数据"。
|
|
320
|
+
* **数据缺失被画成了平稳过渡**,这是图表最容易忽略也最会误导人的错。
|
|
321
|
+
*
|
|
322
|
+
* 契约来源:演示项目名 的 ChartsPage 写着
|
|
323
|
+
* 「MISSING != ZERO / INVALID != MISSING / 过滤必须可见」
|
|
324
|
+
* 它自己做到了 connectNulls={false} 且 0 处遗漏 —— 本库照它做。
|
|
325
|
+
*/
|
|
326
|
+
var segs = [], cur = [];
|
|
327
|
+
for (var i = 0; i < s.xs.length; i++) {
|
|
328
|
+
if (s.ys[i] === null) {
|
|
329
|
+
if (cur.length) segs.push(cur);
|
|
330
|
+
cur = [];
|
|
331
|
+
} else {
|
|
332
|
+
cur.push(i);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (cur.length) segs.push(cur);
|
|
336
|
+
|
|
337
|
+
// 有效点少于 2 个 ⇒ 画不出趋势
|
|
338
|
+
var total = 0;
|
|
339
|
+
for (var q = 0; q < segs.length; q++) total += segs[q].length;
|
|
340
|
+
if (total < 2) { spark.appendChild(svg); return; }
|
|
341
|
+
|
|
342
|
+
function pathOf(seg) {
|
|
343
|
+
var d = 'M' + s.xs[seg[0]].toFixed(1) + ' ' + s.ys[seg[0]].toFixed(1);
|
|
344
|
+
for (var t = 1; t < seg.length; t++) {
|
|
345
|
+
d += 'L' + s.xs[seg[t]].toFixed(1) + ' ' + s.ys[seg[t]].toFixed(1);
|
|
346
|
+
}
|
|
347
|
+
return d;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// 面积填充:每段一个 path,**段间不填充**
|
|
351
|
+
if (o.type === 'area' && o.fill) {
|
|
352
|
+
var base = h - pad;
|
|
353
|
+
for (var g = 0; g < segs.length; g++) {
|
|
354
|
+
var sg = segs[g];
|
|
355
|
+
if (sg.length < 2) continue; // 单点不成"面积"
|
|
356
|
+
var ad = pathOf(sg) +
|
|
357
|
+
'L' + s.xs[sg[sg.length - 1]].toFixed(1) + ' ' + base +
|
|
358
|
+
'L' + s.xs[sg[0]].toFixed(1) + ' ' + base + 'Z';
|
|
359
|
+
var ap = el('path', { d: ad, 'class': 'spark-area', 'fill-opacity': '0.14' });
|
|
360
|
+
ap.style.setProperty('--spark-c', o.color);
|
|
361
|
+
svg.appendChild(ap);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (o.type === 'bar') {
|
|
366
|
+
var bw = Math.max(1, (w - pad * 2) / pts.length - 1);
|
|
367
|
+
for (var j = 0; j < s.xs.length; j++) {
|
|
368
|
+
// 🔴 缺失点**不画柱** —— 画成 0 高度等于假装"这里是 0"
|
|
369
|
+
if (s.ys[j] === null) continue;
|
|
370
|
+
var bh = h - pad - s.ys[j];
|
|
371
|
+
var rc = el('rect', {
|
|
372
|
+
x: (s.xs[j] - bw / 2).toFixed(1), y: s.ys[j].toFixed(1),
|
|
373
|
+
width: bw.toFixed(1), height: Math.max(0, bh).toFixed(1),
|
|
374
|
+
'class': 'spark-bar',
|
|
375
|
+
'fill-opacity': j === s.xs.length - 1 ? '1' : '0.72',
|
|
376
|
+
});
|
|
377
|
+
rc.style.setProperty('--spark-c', o.color);
|
|
378
|
+
svg.appendChild(rc);
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
for (var m = 0; m < segs.length; m++) {
|
|
382
|
+
var seg = segs[m];
|
|
383
|
+
if (seg.length === 1) {
|
|
384
|
+
// 单点成段 ⇒ 画成点,否则这段"什么都不画"(用户以为图坏了)
|
|
385
|
+
var dc = el('circle', {
|
|
386
|
+
cx: s.xs[seg[0]].toFixed(1), cy: s.ys[seg[0]].toFixed(1), r: '1.5',
|
|
387
|
+
'class': 'spark-dot',
|
|
388
|
+
});
|
|
389
|
+
dc.style.setProperty('--spark-c', o.color);
|
|
390
|
+
svg.appendChild(dc);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
/* 🔴 改:用 **CSS 类**上色,不用 stroke="var(--x)" 属性。
|
|
394
|
+
Chromium 里 attribute 的 var() 能解析(实测 rgb(122,169,222) 正确),
|
|
395
|
+
但 **presentation attribute 上的 var() 在各浏览器支持不一致**
|
|
396
|
+
—— 规范没保证,Safari 尤其不可靠。
|
|
397
|
+
⇒ 颜色一律走 CSS(`.spark-line{stroke:var(--spark-c)}`),
|
|
398
|
+
这里只放一个类名 + 把颜色写进自定义属性。
|
|
399
|
+
**所有浏览器都确定支持。 */
|
|
400
|
+
var path = el('path', {
|
|
401
|
+
d: pathOf(seg),
|
|
402
|
+
'class': 'spark-line', // 🔴 CSS 决定描边/填充(属性打不过 CSS)
|
|
403
|
+
'stroke-width': '1.5',
|
|
404
|
+
'stroke-linecap': 'round', 'stroke-linejoin': 'round',
|
|
405
|
+
'vector-effect': 'non-scaling-stroke', // 缩放时线宽不变
|
|
406
|
+
});
|
|
407
|
+
path.style.setProperty('--spark-c', o.color);
|
|
408
|
+
// 🔴 动画只该在**首次绘制**时播一次;重绘(尺寸/数据变化)时
|
|
409
|
+
// 再播一遍就会看到「闪」—— 所以只有首绘才加动画类。
|
|
410
|
+
if (o.animate && !supportsReducedMotion() && isFirstQuick) {
|
|
411
|
+
// 🔴 **拆成独立的动画类**。
|
|
412
|
+
// 旧逻辑是「没动画就不加 spark-line 类」——
|
|
413
|
+
// 但重写外观后 `spark-line` 必须**常驻**(它负责 fill:none),
|
|
414
|
+
// 于是这个判断形同虚设,**动画会无视设置照播**。
|
|
415
|
+
// ⇒ 外观与行为分成两个类,各管各的。
|
|
416
|
+
path.classList.add('spark-anim');
|
|
417
|
+
}
|
|
418
|
+
svg.appendChild(path);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 标出最后一个点("现在在哪")
|
|
423
|
+
// 🔴 末点缺失 ⇒ **不标**(标了等于假装知道末值)
|
|
424
|
+
var li = s.xs.length - 1;
|
|
425
|
+
if (o.last && s.ys[li] !== null) {
|
|
426
|
+
var lc = el('circle', {
|
|
427
|
+
cx: s.xs[li].toFixed(1), cy: s.ys[li].toFixed(1), r: String(LAST_R),
|
|
428
|
+
'class': 'spark-dot',
|
|
429
|
+
});
|
|
430
|
+
lc.style.setProperty('--spark-c', o.color);
|
|
431
|
+
svg.appendChild(lc);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
spark.appendChild(svg);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/* ---------- 对外 ---------- */
|
|
438
|
+
var Chart = {
|
|
439
|
+
draw: draw,
|
|
440
|
+
emit: function (spark) { draw(spark); },
|
|
441
|
+
/** 画一个区域里的所有 sparkline */
|
|
442
|
+
update: function (root) {
|
|
443
|
+
var list = (root || document).querySelectorAll('.sparkline[data-spark]');
|
|
444
|
+
for (var i = 0; i < list.length; i++) draw(list[i]);
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
window.Chart = Chart;
|
|
448
|
+
|
|
449
|
+
function init() { Chart.update(); }
|
|
450
|
+
|
|
451
|
+
/* 🔴 **ResizeObserver:布局变化后重绘**(修「末点圆点被吃掉」的关键之二)
|
|
452
|
+
|
|
453
|
+
为什么必须有:
|
|
454
|
+
sparkline 常放在 **flex 行**里(如「代码 + 数字 + 涨跌 + 走势」)。
|
|
455
|
+
flex 的收缩是**分阶段**发生的:初次绘制时那一列可能还占着整行,
|
|
456
|
+
之后才被压到最终宽度。
|
|
457
|
+
⇒ **初次绘制拿到的宽度是过时的**,SVG 会被拉伸/压缩,
|
|
458
|
+
末点圆点被压扁、被 `overflow:hidden` 裁掉。
|
|
459
|
+
|
|
460
|
+
⚠️ ResizeObserver 在老 WebView(精简档)**不存在** ——
|
|
461
|
+
所以必须做特性检测,**不能直接用**。
|
|
462
|
+
*/
|
|
463
|
+
if (typeof window.ResizeObserver === 'function') {
|
|
464
|
+
var ro = new window.ResizeObserver(function (entries) {
|
|
465
|
+
for (var i = 0; i < entries.length; i++) {
|
|
466
|
+
var el = entries[i].target;
|
|
467
|
+
if (el && el.className && el.className.indexOf('sparkline') > -1) {
|
|
468
|
+
draw(el);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
// 观察所有 sparkline(DOMContentLoaded 后才有)
|
|
473
|
+
function observeAll() {
|
|
474
|
+
var list = document.querySelectorAll('.sparkline[data-spark]');
|
|
475
|
+
for (var i = 0; i < list.length; i++) {
|
|
476
|
+
try { ro.observe(list[i]); } catch (e) { /* 忽略 */ }
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (document.readyState === 'loading') {
|
|
480
|
+
document.addEventListener('DOMContentLoaded', observeAll);
|
|
481
|
+
} else {
|
|
482
|
+
observeAll();
|
|
483
|
+
}
|
|
484
|
+
// 动态插入的(demo 的"更新数据"按钮)也要观察
|
|
485
|
+
Chart.observe = function (el) {
|
|
486
|
+
if (el) { try { ro.observe(el); } catch (e) { /* 忽略 */ } }
|
|
487
|
+
else observeAll();
|
|
488
|
+
};
|
|
489
|
+
} else {
|
|
490
|
+
/* 🔴 兜底:老 WebView 没有 ResizeObserver ⇒ 退到 window.resize
|
|
491
|
+
(窄屏旋转 / 窗口缩放时至少能重绘)。 */
|
|
492
|
+
window.addEventListener('resize', function () { Chart.update(); });
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// @autoinit: .sparkline[data-spark] —— 扫类名+属性自动观察并绘制(详见 API.md「自动初始化」)
|
|
496
|
+
if (document.readyState === 'loading') {
|
|
497
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
498
|
+
} else {
|
|
499
|
+
init();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// 窗口尺寸变化时重画(SVG 用 viewBox,但要重算像素宽度)
|
|
503
|
+
var t = null;
|
|
504
|
+
window.addEventListener('resize', function () {
|
|
505
|
+
if (t) clearTimeout(t);
|
|
506
|
+
t = setTimeout(function () { Chart.update(); }, 150);
|
|
507
|
+
});
|
|
508
|
+
})();
|