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.
Files changed (274) hide show
  1. package/01-tokens/README.md +236 -0
  2. package/01-tokens/behavior/dismissable.js +132 -0
  3. package/01-tokens/behavior/emit.js +76 -0
  4. package/01-tokens/behavior/focus-return.js +81 -0
  5. package/01-tokens/behavior/roving.js +152 -0
  6. package/01-tokens/behavior/typeahead.js +124 -0
  7. package/01-tokens/focus-ring-demo.html +133 -0
  8. package/01-tokens/focus-ring.css +102 -0
  9. package/01-tokens/page.css +244 -0
  10. package/01-tokens/polyfill.js +116 -0
  11. package/01-tokens/responsive-demo.html +171 -0
  12. package/01-tokens/scrollbar-demo.html +194 -0
  13. package/01-tokens/scrollbar.css +179 -0
  14. package/01-tokens/theme-toggle.js +103 -0
  15. package/01-tokens/tokens.css +972 -0
  16. package/01-tokens/typography.css +156 -0
  17. package/02-primitives/badge/README.md +71 -0
  18. package/02-primitives/badge/badge.css +141 -0
  19. package/02-primitives/badge/demo.html +101 -0
  20. package/02-primitives/button/README.md +99 -0
  21. package/02-primitives/button/button.css +353 -0
  22. package/02-primitives/button/demo.html +200 -0
  23. package/02-primitives/card/README.md +145 -0
  24. package/02-primitives/card/card.css +246 -0
  25. package/02-primitives/card/demo.html +170 -0
  26. package/02-primitives/choice/README.md +161 -0
  27. package/02-primitives/choice/choice.css +395 -0
  28. package/02-primitives/choice/demo.html +277 -0
  29. package/02-primitives/combobox/README.md +79 -0
  30. package/02-primitives/combobox/combobox.css +223 -0
  31. package/02-primitives/combobox/combobox.js +485 -0
  32. package/02-primitives/combobox/demo.html +109 -0
  33. package/02-primitives/date-range/README.md +100 -0
  34. package/02-primitives/date-range/date-range.css +206 -0
  35. package/02-primitives/date-range/date-range.js +301 -0
  36. package/02-primitives/date-range/demo.html +104 -0
  37. package/02-primitives/input/README.md +170 -0
  38. package/02-primitives/input/demo.html +246 -0
  39. package/02-primitives/input/input.css +239 -0
  40. package/02-primitives/popover/README.md +80 -0
  41. package/02-primitives/popover/demo.html +150 -0
  42. package/02-primitives/popover/popover.css +187 -0
  43. package/02-primitives/popover/popover.js +331 -0
  44. package/02-primitives/progress/README.md +82 -0
  45. package/02-primitives/progress/demo.html +154 -0
  46. package/02-primitives/progress/progress.css +172 -0
  47. package/02-primitives/select/README.md +95 -0
  48. package/02-primitives/select/demo.html +121 -0
  49. package/02-primitives/select/select.css +151 -0
  50. package/02-primitives/select/select.js +549 -0
  51. package/02-primitives/separator/README.md +63 -0
  52. package/02-primitives/separator/demo.html +84 -0
  53. package/02-primitives/separator/separator.css +73 -0
  54. package/02-primitives/skeleton/README.md +69 -0
  55. package/02-primitives/skeleton/demo.html +110 -0
  56. package/02-primitives/skeleton/skeleton.css +96 -0
  57. package/02-primitives/switch/README.md +65 -0
  58. package/02-primitives/switch/demo.html +115 -0
  59. package/02-primitives/switch/switch.css +370 -0
  60. package/03-patterns/accordion/README.md +121 -0
  61. package/03-patterns/accordion/accordion.css +453 -0
  62. package/03-patterns/accordion/accordion.js +369 -0
  63. package/03-patterns/accordion/demo.html +223 -0
  64. package/03-patterns/content/README.md +103 -0
  65. package/03-patterns/content/content.css +286 -0
  66. package/03-patterns/content/demo.html +88 -0
  67. package/03-patterns/drawer/README.md +79 -0
  68. package/03-patterns/drawer/demo.html +101 -0
  69. package/03-patterns/drawer/drawer.css +129 -0
  70. package/03-patterns/drawer/drawer.js +258 -0
  71. package/03-patterns/dropdown/README.md +78 -0
  72. package/03-patterns/dropdown/demo.html +101 -0
  73. package/03-patterns/dropdown/dropdown.css +135 -0
  74. package/03-patterns/dropdown/dropdown.js +451 -0
  75. package/03-patterns/form-validation/README.md +147 -0
  76. package/03-patterns/form-validation/demo.html +280 -0
  77. package/03-patterns/form-validation/form-validation.css +263 -0
  78. package/03-patterns/list/README.md +150 -0
  79. package/03-patterns/list/demo.html +225 -0
  80. package/03-patterns/list/flip.js +201 -0
  81. package/03-patterns/list/list.css +162 -0
  82. package/03-patterns/nav/README.md +225 -0
  83. package/03-patterns/nav/demo.html +131 -0
  84. package/03-patterns/nav/nav.css +314 -0
  85. package/03-patterns/nav/nav.js +430 -0
  86. package/03-patterns/nav/toc.js +111 -0
  87. package/03-patterns/overlay/README.md +196 -0
  88. package/03-patterns/overlay/demo.html +241 -0
  89. package/03-patterns/overlay/overlay.css +319 -0
  90. package/03-patterns/overlay/overlay.js +563 -0
  91. package/03-patterns/pagination/README.md +114 -0
  92. package/03-patterns/pagination/demo.html +165 -0
  93. package/03-patterns/pagination/pagination.css +195 -0
  94. package/03-patterns/pagination/pagination.js +395 -0
  95. package/03-patterns/reveal/README.md +91 -0
  96. package/03-patterns/reveal/demo.html +106 -0
  97. package/03-patterns/reveal/reveal.css +93 -0
  98. package/03-patterns/reveal/reveal.js +172 -0
  99. package/03-patterns/states/README.md +141 -0
  100. package/03-patterns/states/demo.html +240 -0
  101. package/03-patterns/states/states.css +252 -0
  102. package/03-patterns/tabs/README.md +84 -0
  103. package/03-patterns/tabs/demo.html +139 -0
  104. package/03-patterns/tabs/tabs.css +172 -0
  105. package/03-patterns/tabs/tabs.js +307 -0
  106. package/03-patterns/tooltip/README.md +96 -0
  107. package/03-patterns/tooltip/demo.html +78 -0
  108. package/03-patterns/tooltip/tooltip.css +89 -0
  109. package/03-patterns/tooltip/tooltip.js +288 -0
  110. package/03-patterns/tree/README.md +94 -0
  111. package/03-patterns/tree/demo.html +138 -0
  112. package/03-patterns/tree/tree.css +113 -0
  113. package/03-patterns/tree/tree.js +456 -0
  114. package/04-recipes/analysis-report/README.md +80 -0
  115. package/04-recipes/analysis-report/analysis-report.css +187 -0
  116. package/04-recipes/analysis-report/demo.html +274 -0
  117. package/04-recipes/data-showcase/README.md +45 -0
  118. package/04-recipes/data-showcase/demo.html +247 -0
  119. package/04-recipes/detail-page/README.md +93 -0
  120. package/04-recipes/detail-page/demo.html +55 -0
  121. package/04-recipes/detail-page/detail-page.css +190 -0
  122. package/04-recipes/detail-page/full.html +60 -0
  123. package/04-recipes/detail-page/null.html +60 -0
  124. package/04-recipes/detail-page/partial.html +60 -0
  125. package/04-recipes/detail-page/verify-skeleton.py +19 -0
  126. package/04-recipes/longform/README.md +93 -0
  127. package/04-recipes/longform/agc-memory.svg +4 -0
  128. package/04-recipes/longform/content.html +610 -0
  129. package/04-recipes/longform/content.md +51 -0
  130. package/04-recipes/longform/longform.html +627 -0
  131. package/04-recipes/report/README.md +176 -0
  132. package/04-recipes/report/demo.html +355 -0
  133. package/04-recipes/report/report.css +364 -0
  134. package/04-recipes/table/README.md +103 -0
  135. package/04-recipes/table/demo.html +455 -0
  136. package/04-recipes/table/table.css +372 -0
  137. package/04-recipes/tierA-tokens.css +189 -0
  138. package/09-assets/README.md +74 -0
  139. package/09-assets/bar/README.md +94 -0
  140. package/09-assets/bar/bar.css +166 -0
  141. package/09-assets/bar/bar.js +320 -0
  142. package/09-assets/bar/demo.html +176 -0
  143. package/09-assets/echarts-adapter/demo.html +192 -0
  144. package/09-assets/echarts-adapter/echarts-adapter.js +277 -0
  145. package/09-assets/model-viewer/README.md +155 -0
  146. package/09-assets/model-viewer/demo.html +105 -0
  147. package/09-assets/model-viewer/model-viewer.css +143 -0
  148. package/09-assets/model-viewer/model-viewer.js +423 -0
  149. package/09-assets/scientific-plot/README.md +94 -0
  150. package/09-assets/scientific-plot/demo.html +252 -0
  151. package/09-assets/scientific-plot/scientific-plot.css +141 -0
  152. package/09-assets/scientific-plot/scientific-plot.js +610 -0
  153. package/09-assets/sparkline/README.md +153 -0
  154. package/09-assets/sparkline/demo.html +163 -0
  155. package/09-assets/sparkline/sparkline.css +158 -0
  156. package/09-assets/sparkline/sparkline.js +508 -0
  157. package/API.md +332 -0
  158. package/CHANGELOG.md +3138 -0
  159. package/CODE_OF_CONDUCT.md +64 -0
  160. package/CONTRIBUTING.md +89 -0
  161. package/ERRORS.md +1658 -0
  162. package/LICENSE +21 -0
  163. package/README.md +497 -2
  164. package/SECURITY.md +72 -0
  165. package/START-HERE.md +164 -0
  166. package/SUPPORT.md +109 -0
  167. package/adapters/presentation/README.md +113 -0
  168. package/adapters/presentation/check.js +101 -0
  169. package/adapters/presentation/deck.schema.json +74 -0
  170. package/adapters/presentation/to-doc.js +138 -0
  171. package/ai/START-HERE.md +104 -0
  172. package/ai/behaviors.json +380 -0
  173. package/ai/behaviors.schema.json +61 -0
  174. package/ai/capabilities.json +56 -0
  175. package/ai/cli.js +387 -0
  176. package/ai/components.json +1944 -0
  177. package/ai/components.meta.json +168 -0
  178. package/ai/components.schema.json +220 -0
  179. package/ai/contract.schema.json +147 -0
  180. package/ai/deprecations.json +55 -0
  181. package/ai/deprecations.schema.json +85 -0
  182. package/ai/motion-policy.json +22 -0
  183. package/ai/patch.js +323 -0
  184. package/ai/patch.schema.json +63 -0
  185. package/ai/profiles.json +92 -0
  186. package/ai/tokens.audience.json +55 -0
  187. package/ai/tokens.audience.schema.json +73 -0
  188. package/ai/tokens.json +762 -0
  189. package/ai/tokens.schema.json +58 -0
  190. package/ai/tokens.tree.json +752 -0
  191. package/ai/tokens.tree.schema.json +56 -0
  192. package/ai/validate.js +383 -0
  193. package/dist/01-tokens/behavior/dismissable.js +60 -0
  194. package/dist/01-tokens/behavior/emit.js +21 -0
  195. package/dist/01-tokens/behavior/focus-return.js +31 -0
  196. package/dist/01-tokens/behavior/roving.js +77 -0
  197. package/dist/01-tokens/behavior/typeahead.js +61 -0
  198. package/dist/01-tokens/focus-ring.css +1 -0
  199. package/dist/01-tokens/page.css +1 -0
  200. package/dist/01-tokens/polyfill.js +78 -0
  201. package/dist/01-tokens/scrollbar.css +1 -0
  202. package/dist/01-tokens/theme-toggle.js +61 -0
  203. package/dist/01-tokens/tokens.css +1 -0
  204. package/dist/01-tokens/typography.css +1 -0
  205. package/dist/02-primitives/badge/badge.css +1 -0
  206. package/dist/02-primitives/button/button.css +1 -0
  207. package/dist/02-primitives/card/card.css +1 -0
  208. package/dist/02-primitives/choice/choice.css +1 -0
  209. package/dist/02-primitives/combobox/combobox.css +1 -0
  210. package/dist/02-primitives/combobox/combobox.js +439 -0
  211. package/dist/02-primitives/date-range/date-range.css +1 -0
  212. package/dist/02-primitives/date-range/date-range.js +267 -0
  213. package/dist/02-primitives/input/input.css +1 -0
  214. package/dist/02-primitives/popover/popover.css +1 -0
  215. package/dist/02-primitives/popover/popover.js +257 -0
  216. package/dist/02-primitives/progress/progress.css +1 -0
  217. package/dist/02-primitives/select/select.css +1 -0
  218. package/dist/02-primitives/select/select.js +449 -0
  219. package/dist/02-primitives/separator/separator.css +1 -0
  220. package/dist/02-primitives/skeleton/skeleton.css +1 -0
  221. package/dist/02-primitives/switch/switch.css +1 -0
  222. package/dist/03-patterns/accordion/accordion.css +1 -0
  223. package/dist/03-patterns/accordion/accordion.js +219 -0
  224. package/dist/03-patterns/content/content.css +1 -0
  225. package/dist/03-patterns/drawer/drawer.css +1 -0
  226. package/dist/03-patterns/drawer/drawer.js +229 -0
  227. package/dist/03-patterns/dropdown/dropdown.css +1 -0
  228. package/dist/03-patterns/dropdown/dropdown.js +365 -0
  229. package/dist/03-patterns/form-validation/form-validation.css +1 -0
  230. package/dist/03-patterns/list/flip.js +117 -0
  231. package/dist/03-patterns/list/list.css +1 -0
  232. package/dist/03-patterns/nav/nav.css +1 -0
  233. package/dist/03-patterns/nav/nav.js +322 -0
  234. package/dist/03-patterns/nav/toc.js +76 -0
  235. package/dist/03-patterns/overlay/overlay.css +1 -0
  236. package/dist/03-patterns/overlay/overlay.js +398 -0
  237. package/dist/03-patterns/pagination/pagination.css +1 -0
  238. package/dist/03-patterns/pagination/pagination.js +273 -0
  239. package/dist/03-patterns/reveal/reveal.css +1 -0
  240. package/dist/03-patterns/reveal/reveal.js +138 -0
  241. package/dist/03-patterns/states/states.css +1 -0
  242. package/dist/03-patterns/tabs/tabs.css +1 -0
  243. package/dist/03-patterns/tabs/tabs.js +242 -0
  244. package/dist/03-patterns/tooltip/tooltip.css +1 -0
  245. package/dist/03-patterns/tooltip/tooltip.js +213 -0
  246. package/dist/03-patterns/tree/tree.css +1 -0
  247. package/dist/03-patterns/tree/tree.js +383 -0
  248. package/dist/04-recipes/analysis-report/analysis-report.css +1 -0
  249. package/dist/04-recipes/detail-page/detail-page.css +1 -0
  250. package/dist/04-recipes/report/report.css +1 -0
  251. package/dist/04-recipes/table/table.css +1 -0
  252. package/dist/04-recipes/tierA-tokens.css +1 -0
  253. package/dist/09-assets/bar/bar.css +1 -0
  254. package/dist/09-assets/bar/bar.js +222 -0
  255. package/dist/09-assets/echarts-adapter/echarts-adapter.js +185 -0
  256. package/dist/09-assets/model-viewer/model-viewer.css +1 -0
  257. package/dist/09-assets/model-viewer/model-viewer.js +320 -0
  258. package/dist/09-assets/scientific-plot/scientific-plot.css +1 -0
  259. package/dist/09-assets/scientific-plot/scientific-plot.js +568 -0
  260. package/dist/09-assets/sparkline/sparkline.css +1 -0
  261. package/dist/09-assets/sparkline/sparkline.js +265 -0
  262. package/dist/COSTS.md +56 -0
  263. package/docs/AI-CONTRACT.md +197 -0
  264. package/docs/BACKLOG.md +127 -0
  265. package/docs/BENCHMARK.md +252 -0
  266. package/docs/GUIDANCE.md +154 -0
  267. package/docs/INVARIANT.md +372 -0
  268. package/docs/LAYER-BASELINE.md +158 -0
  269. package/docs/RELEASING.md +311 -0
  270. package/docs/RFC-THEME-SCOPE.md +141 -0
  271. package/docs/ROADMAP.md +182 -0
  272. package/index.html +82 -0
  273. package/package.json +107 -4
  274. package/types/index.d.ts +310 -0
@@ -0,0 +1,155 @@
1
+ # model-viewer — 惰性加载的 3D 模型查看器
2
+
3
+ > 建立于 2026-10-04。定位:
4
+ > **「这个模块我们可能以后很久都不会去动它,必须一次性就做对。
5
+ > 我们的精力要放在其他的主要内容上面,这个只是我们给未来做的一个储备。但是不能错。」**
6
+
7
+ ---
8
+
9
+ ## 定位与边界
10
+
11
+ **它做什么**:点一下才加载 3D 引擎,拖动可从任意角度查看模型。
12
+
13
+ **它不做什么**(以及为什么):
14
+
15
+ | 不做 | 原因 |
16
+ |---|---|
17
+ | **不把 three.js 打包成本库依赖** | 592KB,违背本库「零依赖、可拷贝即用」;且 `vendor/` 里那两个 loader 是 **ES6 class**(实测),与「**JS 一律 ES5**」(精简档 的 WebView 会直接语法错误)冲突 |
18
+ | **精简档(某项目)不做 3D** | WebGL 在老 WebView 上大概率不可用。本组件**会检测并给中文提示**,绝不白屏 |
19
+ | **不自己手搓 glTF 解析器** | 用成熟的 `GLTFLoader`(three 官方) |
20
+ | **不内置 Draco / KTX2 解码器** | 各自还几百 KB,等真需要时再加(见 TODO) |
21
+
22
+ **分层**:本库负责「**怎么正确地接**」(接入规范 + 降级 UI + 令牌驱动);
23
+ **three.js 本身由主项目负责**(可以 CDN、可以 npm、可以本地文件)。
24
+
25
+ ---
26
+
27
+ ## 用法
28
+
29
+ ```html
30
+ <link rel="stylesheet" href="09-assets/model-viewer/model-viewer.css">
31
+
32
+ <div class="mv"
33
+ data-mv="models/fox.glb" <!-- 模型路径(必填)-->
34
+ data-mv-height="320" <!-- 画布高 px,默认 320 -->
35
+ data-mv-three="vendor/three.min.js" <!-- 引擎(必填)-->
36
+ data-mv-gltf="vendor/GLTFLoader.js" <!-- 模型加载器(必填)-->
37
+ data-mv-orbit="vendor/OrbitControls.js"></div> <!-- 拖动控制(可省)-->
38
+
39
+ <script src="09-assets/model-viewer/model-viewer.js"></script>
40
+ ```
41
+
42
+ **API**:
43
+
44
+ | 方法 | 用途 |
45
+ |---|---|
46
+ | `ModelViewer.update(root?)` | 扫描并初始化 `[data-mv]` |
47
+ | `ModelViewer.refresh(root?)` | **换主题后调用**(three 内的材质要重建) |
48
+
49
+ ⚠️ **HTML 里绝不要写 `<script src="three.min.js">`** —— 那等于 592KB 在首屏白给,
50
+ 正好违背 要的「一定要快」。
51
+
52
+ ---
53
+
54
+ ## 🔴🔴 部署前提:服务器 CSP 必须允许 `blob:`
55
+
56
+ **这是 实测踩到的最重要一条**(本地能跑、线上挂):
57
+
58
+ three 的 `GLTFLoader` 对 **glb 内嵌的贴图会创建 Blob URL**
59
+ (PNG 太大,不能用 data URI)。
60
+ 如果服务器的 CSP 不允许 `blob:`,浏览器会直接拦下 ⇒ 报 `Failed to fetch`。
61
+
62
+ 实测本服务器当前的头:
63
+
64
+ ```
65
+ Content-Security-Policy:
66
+ img-src 'self' data: https: ← ❌ 缺 blob:
67
+ connect-src 'self' https: ← ❌ 缺 blob:
68
+ ```
69
+
70
+ ⇒ **需要改成**(只加 `blob:`,不放宽其他):
71
+
72
+ ```
73
+ img-src 'self' data: blob: https:
74
+ connect-src 'self' blob: https:
75
+ ```
76
+
77
+ ⚠️ **为什么本地测不出来**:`http://127.0.0.1` 没有 CSP,
78
+ 所以本地永远通过 —— **只有部署到 https 服务器才暴露**。
79
+ ⇒ 改 CSP 之后,**必须去服务器上再验一次**(本地通过不算数)。
80
+
81
+ ⚠️ 组件已经把这条写进错误提示了(不再抛 `Failed to fetch` 给用户):
82
+ > 「模型加载失败:服务器的 Content-Security-Policy 缺少 blob:…」
83
+
84
+ ---
85
+
86
+ ## 三条硬指标(最终定的,逐条对应)
87
+
88
+ | 要求 | 做法 | 实测 |
89
+ |---|---|---|
90
+ | **首屏要快** | 引擎**点击后**才 `<script>` 注入 | 首屏 **0 字节**引擎 ✅ |
91
+ | | 模型走 `XHR`(`arraybuffer`)以便显示**真实进度** | ✅ |
92
+ | **绝不能白屏** | WebGL 不支持 / 引擎 404 / loader 404 / 模型 404 / 解析失败 —— **五种**全部中文提示 + 重试 | ✅ 5/5 |
93
+ | **视觉统一** | 颜色、圆角、间距全部 `var(--*)` | ✅ |
94
+ | **iOS 手感** | `touch-action: none`(拖动不触发滚动)· 关 tap highlight · `devicePixelRatio` 上限 2 · **页面隐藏时停渲染** | ✅ |
95
+
96
+ ---
97
+
98
+ ## 已验证的行为
99
+
100
+ ```
101
+ 正常 → 状态 ok,模型 159 KB,引擎就绪 72ms,拖动可旋转
102
+ 模型 404 → 中文提示「模型下载失败(HTTP 404)」+ 重试按钮
103
+ 引擎 404 → 中文提示「3D 引擎加载失败:脚本加载失败…」+ 重试按钮
104
+ loader 404→ 同上
105
+ 控制器 404→ 同上(且仍能出图,只是不能拖动)
106
+ WebGL 不可用 → 按钮禁用 + 明确说明
107
+ ```
108
+
109
+ **交互实测**:真实触摸拖动后相机 x `0 → -1.97`(视角确实变了)· 缩放 z `2.4 → 3.97`。
110
+
111
+ ### 调试用的 URL 参数
112
+
113
+ `demo.html?mv=models/NOPE.glb` / `?three=vendor/NOPE.js` / `?gltf=…` / `?orbit=…`
114
+ ⇒ 可直接复现任意降级路径,**不必改代码**。
115
+
116
+ ---
117
+
118
+ ## 🔴 TODO —— 需要持续优化(明确要求标注)
119
+
120
+ 按收益排序。每次改动都要跑 `05-audit/model-viewer-contract.js`。
121
+
122
+ | 优先级 | 项目 | 预期收益 | 代价 |
123
+ |---|---|---|---|
124
+ | **P0** | **Draco 几何压缩** | 模型可再降 **约 10 倍**(159KB → ~16KB) | 需 decoder wasm(~200KB,**只加载一次,可缓存**) |
125
+ | **P0** | **KTX2 / Basis 纹理压缩** | 贴图可降 **约 8 倍**,且**显存省一半** | 需 decoder(~200KB) |
126
+ | **P1** | **模型分级**(缩略图 → 低模 → 完整) | 首屏几乎为 0;适合素材库几十个模型的场景 | 需要离线生成流程 |
127
+ | **P1** | **`gltf-transform` 网格简化** | 高模降到适合网页的三角数 | 需 CLI 工具链 |
128
+ | **P2** | **首帧渲染耗时优化** | 当前引擎就绪 72ms(本地)→ 真实网络需测 | 取决于网络 |
129
+ | **P2** | **模型缓存**(`Cache Storage`) | 第二次访问 0 字节 | 需处理版本失效 |
130
+ | **P2** | **WebGL 上下文丢失恢复**(`webglcontextlost`) | 切后台回来可能黑屏 | 需重建场景 |
131
+ | **P3** | AR/VR(`<model-viewer>` 的 `ar` 属性)| 手机上看实景 | 需换库 |
132
+ | **P3** | 多个模型 + 材质切换 | 展陈类需求 | 视需求 |
133
+
134
+ ### 素材现状(`内置素材目录`)
135
+
136
+ **20 个 glb,共 1.3GB**。体积差 **1700 倍**:
137
+
138
+ | 文件 | 体积 | 备注 |
139
+ |---|---|---|
140
+ | `Chick.glb` | 12.7 KB | 最小 |
141
+ | `Duck.glb` | 117.7 KB | |
142
+ | **`Fox.glb`** | **159 KB** | **本库采用**(指定的"小狐狸")|
143
+ | `Lamp.glb` | 598 KB | |
144
+ | `DamagedHelmet.glb` | 3.7 MB | |
145
+ | `演示项目名.glb` | **21 MB** | ⚠️ **绝不能直接上网页** |
146
+
147
+ ⚠️ **规矩**:新增素材**必须 ≤ 200KB**,超过先走 Draco 压缩。
148
+
149
+ ---
150
+
151
+ ## 相关
152
+
153
+ - 规范边界:同一条原则——**分析类可视化交给成熟库**
154
+ - 契约测试:`node 05-audit/model-viewer-contract.js`
155
+ - 素材备库:`内置素材目录`(20 个 glb,**本库只拷最小的**)
@@ -0,0 +1,105 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
6
+ <meta name="color-scheme" content="light dark">
7
+ <link rel="icon" href="data:,">
8
+ <title>3D 模型查看器 · frontend-lib</title>
9
+
10
+ <!-- 🔴 注意:这里**没有** <script src="three.min.js">
11
+ 3D 引擎是**点击后才加载**的(见 model-viewer.js 的 loadScript)。
12
+ 写在 HTML 里 = 592KB 在首屏白给,正好违背 要的「一定要快」。 -->
13
+ <link rel="stylesheet" href="../../01-tokens/tokens.css">
14
+ <link rel="stylesheet" href="../../01-tokens/typography.css">
15
+ <link rel="stylesheet" href="../../01-tokens/page.css">
16
+ <link rel="stylesheet" href="model-viewer.css">
17
+
18
+ <style>
19
+ .note code { font-family: var(--mono); font-size: .92em;
20
+ background: var(--surface-sunken); padding: 1px 4px; border-radius: 3px; }
21
+ ul { margin: 0 0 var(--sp-3); padding-left: 20px; color: var(--text-secondary);
22
+ font-size: var(--fs-sm); line-height: 1.8; }
23
+ table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); margin: 0 0 var(--sp-4); }
24
+ th, td { text-align: left; padding: var(--sp-2) var(--sp-3);
25
+ border-bottom: 1px solid var(--border-decor); }
26
+ th { font-size: var(--fs-xs); color: var(--text-secondary); font-weight: 500; }
27
+ td.num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
28
+ .ok { color: var(--success); }
29
+ </style>
30
+ </head>
31
+ <body class="page page">
32
+ <main class="wrap">
33
+
34
+ <h1>3D 模型查看器</h1>
35
+ <p class="lede">
36
+ 惰性加载:<strong>首屏 0 字节 3D 引擎</strong>,点了才下载。
37
+ 这是 确定的三条硬要求之一(「体积和加载一定要快」)。
38
+ </p>
39
+
40
+ <h2 class="rule-top">试试看:小狐狸</h2>
41
+ <p class="note">
42
+ <strong>先别急着点按钮</strong> —— 打开开发者工具的 Network 面板,
43
+ 首屏<strong>不会有 three.js</strong>。点下去才出现
44
+ <code>three.min.js</code> <code>GLTFLoader.js</code> <code>OrbitControls.js</code>。
45
+ </p>
46
+ <p class="note">
47
+ <strong>发布包里没有下面这些资源</strong> —— <code>vendor/</code>(引擎与两个 loader)
48
+ 和 <code>models/</code>(.glb 模型)都在 <code>package.json</code> 的
49
+ <code>files</code> 排除名单里(592KB 体积,且那两个 loader 是 ES6 class,
50
+ 与「JS 一律 ES5」冲突)。
51
+ ⇒ 直接打开本页看到的是<strong>降级 UI,这是预期行为,不是坏了</strong>;
52
+ 想看真效果,请自行放好这三个文件,或用下方 URL 参数逐条复现降级路径。
53
+ </p>
54
+
55
+ <div class="mv" data-mv="models/fox.glb"
56
+ data-mv-height="320"
57
+ data-mv-three="vendor/three.min.js"
58
+ data-mv-gltf="vendor/GLTFLoader.js"
59
+ data-mv-orbit="vendor/OrbitControls.js"></div>
60
+
61
+ <h2 class="rule-top">为什么是这样设计的</h2>
62
+ <table>
63
+ <tr><th>要求</th><th>做法</th></tr>
64
+ <tr><td>首屏要快</td>
65
+ <td>3D 引擎<strong>点击后</strong>才 <code>&lt;script&gt;</code> 注入,
66
+ 首屏 <strong>0 KB</strong>;模型走 <code>XHR</code> 以便显示<strong>真实进度</strong></td></tr>
67
+ <tr><td>绝不能白屏</td>
68
+ <td>WebGL 不支持 / 库没来 / 模型 404 / 解析失败
69
+ —— 四种失败<strong>全部</strong>给中文提示 + <strong>重试按钮</strong></td></tr>
70
+ <tr><td>视觉统一</td>
71
+ <td>颜色、圆角、间距<strong>全部</strong>走 <code>var(--*)</code>,换主题跟着变</td></tr>
72
+ <tr><td>iOS 手感</td>
73
+ <td><code>touch-action: none</code> 让拖动不触发页面滚动;
74
+ 关掉 tap highlight;页面切后台<strong>停止渲染</strong>省电</td></tr>
75
+ </table>
76
+
77
+ <h2 class="rule-top">边界(本库不做什么)</h2>
78
+ <ul>
79
+ <li><strong>不把 three.js 打包成本库的依赖</strong> ——
80
+ 592KB 违背「零依赖」;而且素材里那两个 loader 是 <strong>ES6</strong>,
81
+ 与「JS 一律 ES5」冲突(精简档 的 WebView 会直接语法错误)。</li>
82
+ <li><strong>精简档(某项目)不做 3D</strong> —— WebGL 在老 WebView 上大概率不可用。
83
+ 本组件会检测并给出明确提示,<strong>不会白屏</strong>。</li>
84
+ <li><strong>不自己手搓 glTF 解析器</strong> —— 用成熟的 GLTFLoader。</li>
85
+ </ul>
86
+
87
+ <h2 class="rule-top">体积现状</h2>
88
+ <table>
89
+ <tr><th>文件</th><th class="num">体积</th><th>何时加载</th></tr>
90
+ <tr><td><code>model-viewer.js</code>(本库)</td><td class="num">~9 KB</td><td><span class="ok">首屏</span></td></tr>
91
+ <tr><td><code>models/fox.glb</code></td><td class="num">159 KB</td><td>点击后(可 XHR 显示进度)</td></tr>
92
+ <tr><td><code>vendor/three.min.js</code></td><td class="num">589 KB</td><td>点击后</td></tr>
93
+ <tr><td><code>vendor/GLTFLoader.js</code></td><td class="num">94 KB</td><td>点击后</td></tr>
94
+ <tr><td><code>vendor/OrbitControls.js</code></td><td class="num">26 KB</td><td>点击后</td></tr>
95
+ </table>
96
+ <p class="note">
97
+ ⭐ <strong>首屏只付出约 9 KB</strong>;点一次才付 868 KB。
98
+ 素材库里还有 20 个模型(最大 21MB),<strong>本库只放最小的 1–2 个</strong>。
99
+ </p>
100
+
101
+ </main>
102
+
103
+ <script src="model-viewer.js"></script>
104
+ </body>
105
+ </html>
@@ -0,0 +1,143 @@
1
+ /* ============================================================================
2
+ model-viewer.css — 3D 查看器样式(令牌驱动 · 零依赖)
3
+ ============================================================================
4
+ 🔴 依据 三条硬要求:
5
+ ① 首屏 0 字节 three(样式不含任何库)
6
+ ② 失败必须降级(占位 + 中文提示,**绝不白屏**)
7
+ ③ 令牌驱动(颜色全走 var(--*),换主题跟着变)
8
+ ========================================================================= */
9
+
10
+ .mv {
11
+ position: relative;
12
+ display: block;
13
+ width: 100%;
14
+ box-sizing: border-box;
15
+ border: 1px solid var(--border-decor);
16
+ border-radius: var(--r-md);
17
+ background: var(--surface);
18
+ overflow: hidden;
19
+ }
20
+
21
+ /* --- 占位 / 错误态:两者都必须看得见,不许白屏 --- */
22
+ .mv__stage {
23
+ display: flex;
24
+ flex-direction: column;
25
+ align-items: center;
26
+ justify-content: center;
27
+ gap: var(--sp-2);
28
+ min-height: 180px;
29
+ padding: var(--sp-5) var(--sp-4);
30
+ background: var(--surface-sunken);
31
+ text-align: center;
32
+ }
33
+
34
+ .mv__stage--err { border-inline-start: 3px solid var(--danger); }
35
+
36
+ .mv__ph {
37
+ font-size: var(--fs-sm);
38
+ color: var(--text-tertiary);
39
+ letter-spacing: 0.08em;
40
+ text-transform: uppercase;
41
+ }
42
+
43
+ .mv__status,
44
+ .mv__err {
45
+ margin: 0;
46
+ font-size: var(--fs-sm);
47
+ color: var(--text-secondary);
48
+ line-height: 1.6;
49
+ }
50
+
51
+ .mv__err { color: var(--danger); }
52
+
53
+ /* --- 按钮 --- */
54
+ .mv__btn {
55
+ display: block;
56
+ width: calc(100% - var(--sp-6));
57
+ margin: var(--sp-3) var(--sp-3) 0;
58
+ min-height: 44px; /* WCAG 2.5.5 命中区 */
59
+ padding: var(--sp-2) var(--sp-4);
60
+ font: inherit;
61
+ font-size: var(--fs-sm);
62
+ font-weight: 500;
63
+ color: var(--text-on-accent);
64
+ background: var(--accent);
65
+ border: 0;
66
+ border-radius: var(--r-sm);
67
+ cursor: pointer;
68
+ -webkit-tap-highlight-color: transparent; /* iOS 点击高亮(见 tokens.css)*/
69
+ transition: background var(--dur-fast) var(--ease-out);
70
+ }
71
+
72
+ .mv__btn:hover:not(:disabled) { background: var(--accent-hover); }
73
+ .mv__btn:disabled { opacity: 0.5; cursor: not-allowed; }
74
+
75
+ .mv__btn:focus-visible {
76
+ outline: 2px solid var(--accent);
77
+ outline-offset: 2px;
78
+ }
79
+
80
+ .mv__hint {
81
+ margin: 0;
82
+ padding: 0 var(--sp-3) var(--sp-3);
83
+ font-size: var(--fs-xs);
84
+ color: var(--text-tertiary);
85
+ line-height: 1.6;
86
+ }
87
+
88
+ .mv__hint code {
89
+ font-family: var(--mono);
90
+ font-size: 1em;
91
+ color: var(--text-secondary);
92
+ }
93
+
94
+ /* --- 3D 画布 --- */
95
+ .mv canvas {
96
+ display: block;
97
+ width: 100% !important;
98
+ height: auto !important;
99
+ touch-action: none; /* 🔴 关键:让拖动不触发页面滚动 */
100
+ background: transparent;
101
+ }
102
+
103
+ /* --- 加载完成后的说明条 --- */
104
+ .mv__cap {
105
+ margin: 0;
106
+ padding: var(--sp-2) var(--sp-3);
107
+ font-size: var(--fs-xs);
108
+ color: var(--text-secondary);
109
+ background: var(--surface);
110
+ border-top: 1px solid var(--border-decor);
111
+ line-height: 1.6;
112
+ }
113
+
114
+ .mv__cap b {
115
+ font-family: var(--mono);
116
+ font-weight: 600;
117
+ color: var(--text-primary);
118
+ font-variant-numeric: tabular-nums;
119
+ }
120
+
121
+ /* 🔴 尊重 reduced-motion:不用动画掩盖(见 charter 07) */
122
+ @media (prefers-reduced-motion: reduce) {
123
+ .mv__btn { transition: none; }
124
+ }
125
+
126
+ /* 🔴 打印时不该出现 3D(见 charter 09) */
127
+ @media print {
128
+ .mv__btn, .mv__hint { display: none; }
129
+ }
130
+
131
+ /* ============================================================================
132
+ 🔴 触屏 hover 门控(hover-gate 门禁要求)
133
+ --------------------------------------------------------------------------
134
+ 为什么必须做:`:hover` 在**触屏设备上会"粘住"** ——
135
+ 点一下按钮,hover 状态一直保持,要点别处才消失
136
+ (移动端根本没有"移开鼠标"这个动作)。
137
+
138
+ 正确做法:只对**真能悬停的设备**(鼠标 / 触控板)生效。
139
+ 依据:Emil Kowalski 的设计规则(ui-benchmark-redesign 技能收录)。
140
+ ========================================================================== */
141
+ @media (hover: hover) and (pointer: fine) {
142
+ .mv__btn:hover:not(:disabled) { background: var(--accent-hover); }
143
+ }