@melt-js/demo-vue3 2026.11.6 → 2026.11.8

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 (147) hide show
  1. package/README.md +129 -89
  2. package/dist/assets/BilibiliBannerView-CDLH7Krl.js +2 -0
  3. package/dist/assets/BilibiliBannerView-CDLH7Krl.js.map +1 -0
  4. package/dist/assets/BilibiliBannerView-odVKtreG.css +1 -0
  5. package/dist/assets/BookFlipView-CkqJEuv9.css +1 -0
  6. package/dist/assets/BookFlipView-Dej1lwhJ.js +15 -0
  7. package/dist/assets/BookFlipView-Dej1lwhJ.js.map +1 -0
  8. package/dist/assets/ChinaRouteMapView-BLjvnvTt.css +1 -0
  9. package/dist/assets/ChinaRouteMapView-CgjpTWF3.js +8 -0
  10. package/dist/assets/ChinaRouteMapView-CgjpTWF3.js.map +1 -0
  11. package/dist/assets/CircularMenuView-CHXEBHII.js +2 -0
  12. package/dist/assets/CircularMenuView-CHXEBHII.js.map +1 -0
  13. package/dist/assets/CircularMenuView-CeaWrpMP.css +1 -0
  14. package/dist/assets/CompareSliderView-BBoom9R3.css +1 -0
  15. package/dist/assets/CompareSliderView-CLZsMU7c.js +21 -0
  16. package/dist/assets/CompareSliderView-CLZsMU7c.js.map +1 -0
  17. package/dist/assets/CorgiInputView-LPvLui2T.css +1 -0
  18. package/dist/assets/CorgiInputView-pwOLZYR6.js +2 -0
  19. package/dist/assets/CorgiInputView-pwOLZYR6.js.map +1 -0
  20. package/dist/assets/CubeWaveLoadingView-BO46TGRX.css +1 -0
  21. package/dist/assets/CubeWaveLoadingView-D9NwMwpM.js +2 -0
  22. package/dist/assets/CubeWaveLoadingView-D9NwMwpM.js.map +1 -0
  23. package/dist/assets/CyberInputView-BHar20pn.css +1 -0
  24. package/dist/assets/CyberInputView-Du5heU8z.js +2 -0
  25. package/dist/assets/CyberInputView-Du5heU8z.js.map +1 -0
  26. package/dist/assets/DnaLoadingView-3vi09F4K.js +2 -0
  27. package/dist/assets/DnaLoadingView-3vi09F4K.js.map +1 -0
  28. package/dist/assets/DnaLoadingView-dehw8f0X.css +1 -0
  29. package/dist/assets/DominoLoadingView-BTB9LFXn.css +1 -0
  30. package/dist/assets/DominoLoadingView-DzJl3a--.js +2 -0
  31. package/dist/assets/DominoLoadingView-DzJl3a--.js.map +1 -0
  32. package/dist/assets/ExpandSearchView-B-wpzp0y.css +1 -0
  33. package/dist/assets/ExpandSearchView-C4O5PPjU.js +2 -0
  34. package/dist/assets/ExpandSearchView-C4O5PPjU.js.map +1 -0
  35. package/dist/assets/FilmRollView-tFY-ep_S.js +19 -0
  36. package/dist/assets/FilmRollView-tFY-ep_S.js.map +1 -0
  37. package/dist/assets/FilmRollView-v7inzOYE.css +1 -0
  38. package/dist/assets/FloatingLabelInputView-CItHui8c.css +1 -0
  39. package/dist/assets/FloatingLabelInputView-JPg7bVuK.js +2 -0
  40. package/dist/assets/FloatingLabelInputView-JPg7bVuK.js.map +1 -0
  41. package/dist/assets/GearLoadingView-DfLdPPk4.css +1 -0
  42. package/dist/assets/GearLoadingView-YiW1SMCn.js +2 -0
  43. package/dist/assets/GearLoadingView-YiW1SMCn.js.map +1 -0
  44. package/dist/assets/GlowBorderView-BmTBEju1.js +2 -0
  45. package/dist/assets/GlowBorderView-BmTBEju1.js.map +1 -0
  46. package/dist/assets/GlowBorderView-D7Wt2YEa.css +1 -0
  47. package/dist/assets/GooeyBallsLoadingView-CiwF9hPT.js +2 -0
  48. package/dist/assets/GooeyBallsLoadingView-CiwF9hPT.js.map +1 -0
  49. package/dist/assets/GooeyBallsLoadingView-DKT7NsAO.css +1 -0
  50. package/dist/assets/GridLoadingView-CyzTuI12.css +1 -0
  51. package/dist/assets/GridLoadingView-n9LlBTgT.js +2 -0
  52. package/dist/assets/GridLoadingView-n9LlBTgT.js.map +1 -0
  53. package/dist/assets/HostView-CP_6ZTK_.css +1 -0
  54. package/dist/assets/{HostView-DMHAyQge.js → HostView-vKQfSkJY.js} +2 -2
  55. package/dist/assets/HostView-vKQfSkJY.js.map +1 -0
  56. package/dist/assets/HourglassLoadingView-D9SGJzLq.js +2 -0
  57. package/dist/assets/HourglassLoadingView-D9SGJzLq.js.map +1 -0
  58. package/dist/assets/HourglassLoadingView-dsB6ng0T.css +1 -0
  59. package/dist/assets/JellyLoadingView-CtCUp0jT.css +1 -0
  60. package/dist/assets/JellyLoadingView-LJ8BPYka.js +2 -0
  61. package/dist/assets/JellyLoadingView-LJ8BPYka.js.map +1 -0
  62. package/dist/assets/LevaPlayground-ChwSFC5s.js +2 -0
  63. package/dist/assets/LevaPlayground-ChwSFC5s.js.map +1 -0
  64. package/dist/assets/LevaPlayground-DYAgbNnJ.css +1 -0
  65. package/dist/assets/LiftCardView-Bv6YvmTE.css +1 -0
  66. package/dist/assets/LiftCardView-C0VfbszA.js +28 -0
  67. package/dist/assets/LiftCardView-C0VfbszA.js.map +1 -0
  68. package/dist/assets/MagneticButtonView-BoZbe0sR.js +2 -0
  69. package/dist/assets/MagneticButtonView-BoZbe0sR.js.map +1 -0
  70. package/dist/assets/MagneticButtonView-Csu-n0s8.css +1 -0
  71. package/dist/assets/MatrixRain-DWRa8Kxg.css +1 -0
  72. package/dist/assets/MatrixRain-U_uRV11-.js +2 -0
  73. package/dist/assets/MatrixRain-U_uRV11-.js.map +1 -0
  74. package/dist/assets/MinecraftChestView-BSnz6YZY.js +2 -0
  75. package/dist/assets/MinecraftChestView-BSnz6YZY.js.map +1 -0
  76. package/dist/assets/MinecraftChestView-BZ5ZtKLg.css +1 -0
  77. package/dist/assets/NeonRingLoadingView-D5em-Iib.js +2 -0
  78. package/dist/assets/NeonRingLoadingView-D5em-Iib.js.map +1 -0
  79. package/dist/assets/NeonRingLoadingView-DtKIq8k2.css +1 -0
  80. package/dist/assets/{OverviewView-BRUCgEGh.js → OverviewView-mLhs_vHL.js} +2 -2
  81. package/dist/assets/OverviewView-mLhs_vHL.js.map +1 -0
  82. package/dist/assets/PasswordInputView-BM7RjPtl.css +1 -0
  83. package/dist/assets/PasswordInputView-DPusH-BN.js +2 -0
  84. package/dist/assets/PasswordInputView-DPusH-BN.js.map +1 -0
  85. package/dist/assets/PhotoSphereView-D5g7d_V3.css +1 -0
  86. package/dist/assets/PhotoSphereView-D8PDD5QY.js +9 -0
  87. package/dist/assets/PhotoSphereView-D8PDD5QY.js.map +1 -0
  88. package/dist/assets/PosterCollageView-BUOlOdaN.css +1 -0
  89. package/dist/assets/PosterCollageView-C2j-UMQC.js +37 -0
  90. package/dist/assets/PosterCollageView-C2j-UMQC.js.map +1 -0
  91. package/dist/assets/RemoteView-DKphv0mb.css +1 -0
  92. package/dist/assets/RemoteView-DRUaw9Ch.js +2 -0
  93. package/dist/assets/RemoteView-DRUaw9Ch.js.map +1 -0
  94. package/dist/assets/RopeGalleryView-CBivZ0jc.css +1 -0
  95. package/dist/assets/RopeGalleryView-D37UWJSV.js +16 -0
  96. package/dist/assets/RopeGalleryView-D37UWJSV.js.map +1 -0
  97. package/dist/assets/SlideAuthPanelView-DiOHJdSv.js +2 -0
  98. package/dist/assets/SlideAuthPanelView-DiOHJdSv.js.map +1 -0
  99. package/dist/assets/SlideAuthPanelView-_QaZcEYH.css +1 -0
  100. package/dist/assets/SplitCodePreviewView-BZcPXChF.css +1 -0
  101. package/dist/assets/SplitCodePreviewView-CkkPwITx.js +19 -0
  102. package/dist/assets/SplitCodePreviewView-CkkPwITx.js.map +1 -0
  103. package/dist/assets/SpotlightButtonView-9RwX8Qjg.js +2 -0
  104. package/dist/assets/SpotlightButtonView-9RwX8Qjg.js.map +1 -0
  105. package/dist/assets/SpotlightButtonView-DvdEWD_f.css +1 -0
  106. package/dist/assets/TerminalAuthView-CPIWKFqb.css +1 -0
  107. package/dist/assets/TerminalAuthView-D8ApeVsK.js +2 -0
  108. package/dist/assets/TerminalAuthView-D8ApeVsK.js.map +1 -0
  109. package/dist/assets/TiltCardView-AGwamyro.css +1 -0
  110. package/dist/assets/TiltCardView-BxrG2N_S.js +2 -0
  111. package/dist/assets/TiltCardView-BxrG2N_S.js.map +1 -0
  112. package/dist/assets/TypewriterView-Ca7m1d4Y.js +3 -0
  113. package/dist/assets/TypewriterView-Ca7m1d4Y.js.map +1 -0
  114. package/dist/assets/TypewriterView-xTdYfGZQ.css +1 -0
  115. package/dist/assets/VortexLoadingView-CPkxwNCv.js +2 -0
  116. package/dist/assets/VortexLoadingView-CPkxwNCv.js.map +1 -0
  117. package/dist/assets/VortexLoadingView-jAvZzFVT.css +1 -0
  118. package/dist/assets/WarpStarfieldView-CnGXASRV.js +2 -0
  119. package/dist/assets/WarpStarfieldView-CnGXASRV.js.map +1 -0
  120. package/dist/assets/WarpStarfieldView-DZOdyf9G.css +1 -0
  121. package/dist/assets/WaterBallLoadingView-Ciivxe7-.css +1 -0
  122. package/dist/assets/WaterBallLoadingView-DrmqqA8f.js +2 -0
  123. package/dist/assets/WaterBallLoadingView-DrmqqA8f.js.map +1 -0
  124. package/dist/assets/WheelMenuButtonView-D02f0oKw.js +30 -0
  125. package/dist/assets/WheelMenuButtonView-D02f0oKw.js.map +1 -0
  126. package/dist/assets/WheelMenuButtonView-DuO5cS3h.css +1 -0
  127. package/dist/assets/{_plugin-vue_export-helper-CzJpAC-G.js → _plugin-vue_export-helper-BbhRsZUO.js} +2 -2
  128. package/dist/assets/_plugin-vue_export-helper-BbhRsZUO.js.map +1 -0
  129. package/dist/assets/{index-tixem2vV.js → index-CthyPCf3.js} +20 -20
  130. package/dist/assets/index-CthyPCf3.js.map +1 -0
  131. package/dist/assets/loadApp-BA8EV_l7.js +11 -0
  132. package/dist/assets/loadApp-BA8EV_l7.js.map +1 -0
  133. package/dist/index.html +13 -13
  134. package/package.json +3 -1
  135. package/dist/assets/CircularMenuView-Bm2jCflo.css +0 -1
  136. package/dist/assets/CircularMenuView-DVx3F33D.js +0 -2
  137. package/dist/assets/CircularMenuView-DVx3F33D.js.map +0 -1
  138. package/dist/assets/HostView-DMHAyQge.js.map +0 -1
  139. package/dist/assets/HostView-I-DgsKrJ.css +0 -1
  140. package/dist/assets/OverviewView-BRUCgEGh.js.map +0 -1
  141. package/dist/assets/RemoteView-CD5hkZNz.js +0 -2
  142. package/dist/assets/RemoteView-CD5hkZNz.js.map +0 -1
  143. package/dist/assets/RemoteView-CurHWajw.css +0 -1
  144. package/dist/assets/_plugin-vue_export-helper-CzJpAC-G.js.map +0 -1
  145. package/dist/assets/index-tixem2vV.js.map +0 -1
  146. package/dist/assets/loadApp-DK5s6_Re.js +0 -11
  147. package/dist/assets/loadApp-DK5s6_Re.js.map +0 -1
package/README.md CHANGED
@@ -1,89 +1,129 @@
1
- # @melt-js/demo-vue3
2
-
3
- Vue 3 + Vite 5 的 hel 宿主(消费方)示例,消费 [`comps-vue3`](../comps-vue3)。
4
-
5
- 页面使用 **vue-router(hash 模式)** 把示例拆成三个可切换的页面,顶部导航栏同时显示当前示例的简介,示例区右上角有**全屏按钮**(只放大当前示例的组件预览区,基于 `vue-fullscreen` 的 `pageOnly + teleport` 模式)。
6
-
7
- | 项 | 值 |
8
- | --- | --- |
9
- | npm 包名 | `@melt-js/demo-vue3` |
10
- | unpkg 入口 | `https://unpkg.com/@melt-js/demo-vue3/dist/index.html` |
11
- | 端口 | `30020` |
12
- | 消费的远程组件 | `comps-vue3`(开发 `http://localhost:20020` / 生产 unpkg) |
13
- | 路由 | `vue-router@4.6.4`,hash 模式 |
14
- | 全屏 | `vue-fullscreen@5.1.2`,`pageOnly` + `teleport`(不调用浏览器原生全屏) |
15
-
16
- ## 示例路由
17
-
18
- | 路由 | 导航文案 | 内容 |
19
- | --- | --- | --- |
20
- | `#/` | 简介 | demo 概览:依赖、端口、远端地址与示例入口 |
21
- | `#/host` | 主应用 | 宿主自己的计数按钮,用来对照远程组件的行为 |
22
- | `#/remote` | 远程组件 | `preFetchLib` 加载的 `comps-vue3` `HelloWorld` |
23
-
24
- 约定:
25
-
26
- - **hash 模式**:demo 的构建产物会通过 unpkg 上的 `dist/index.html` 打开,history 模式在那种静态文件路径下会 404。
27
- - 路由清单在 `src/router/index.ts`,每条路由的 `meta.title` / `meta.description` 同时驱动导航栏 tab 与下方那行简介;新增示例只要加一条路由。
28
- - 全屏按钮只把当前示例标记为 `data-demo-preview` 的组件预览区送进全屏,页面标题、简介、说明与页脚都留在屏幕外;全屏借用 `vue-fullscreen` 5.x 的 `pageOnly + teleport` 模式:预览区临时挂到 body、用 fixed 铺满视口,退出时还原到页面原位置,不调用浏览器原生 Fullscreen API(iframe 内也可用)。预览区右下角提供「退出全屏」浮层按钮,Esc 也可退出;切换路由时会自动退出并还原。
29
- - 需要全屏的示例在组件预览区外包一层 `<DemoPreview>`;简介页没有预览区,在路由 `meta` 里标记 `fullscreen: false`,工具栏不显示全屏按钮。
30
-
31
- ## 文件结构
32
-
33
- ```
34
- demo-vue3/src
35
- ├── App.vue # demo 外壳:导航栏 + 示例舞台(含全屏按钮)+ router-view
36
- ├── components/
37
- │ ├── DemoNavbar.vue # 顶部导航栏:示例 tab + 当前示例简介
38
- │ ├── DemoPreview.vue # 组件预览区:全屏目标 + 「退出全屏」浮层按钮
39
- │ └── ScreenfullButton.vue # 示例区全屏按钮
40
- ├── composables/
41
- │ └── useDemoFullscreen.ts # vue-fullscreen 的 pageOnly + teleport 封装(provide/inject)
42
- ├── configs/
43
- │ ├── app.ts # demo 展示信息(名称 / 端口 / unpkg 入口)
44
- │ └── remote.ts # 远程组件地址与 preFetchLib(Promise 复用)
45
- ├── router/index.ts # 示例路由(hash 模式)
46
- ├── styles/demo-shell.css # 外壳样式(导航栏 / 舞台 / 全屏 / 通用排版)
47
- ├── style.css # 变量与全局重置
48
- └── views/ # OverviewView / HostView / RemoteView
49
- ```
50
-
51
- ## 关键流程
52
-
53
- 1. `src/main.ts`:`bindVueRuntime({ Vue })` 把宿主 vue 挂到 `window.Vue`,远程组件 external 的 `vue` 会复用这一份(少了这一份会出现双 vue,表现为远程组件点击后视图不更新);随后在入口处调用 `loadRemoteLib()` 预加载远程组件,就绪后再挂载宿主。
54
- 2. `src/configs/remote.ts`:按 `import.meta.env.PROD` 选择远端地址——生产构建指向 unpkg 上的 `hel-meta.json`,本地开发指向 `http://localhost:20020`,可用 `VITE_REMOTE_HOST` 覆盖。`loadRemoteLib()` 内部调用 `preFetchLib('comps-vue3', { custom: { host } })`:host 为 `hel-meta.json` 时直接取元数据,本地 dev server 没有该文件时解析 `index.html`。
55
- 3. `src/router/index.ts` + `src/loadApp.ts`:创建 hash 路由并在 `createApp(App).use(router)` 时挂载;`#/host`、`#/remote` 等示例按路由懒加载。
56
- 4. `src/views/RemoteView.vue`:`defineAsyncComponent` 复用入口处已预加载的同一个 Promise,渲染远程组件的 `HelloWorld`。
57
- 5. `src/composables/useDemoFullscreen.ts` + `src/components/DemoPreview.vue`:`App.vue` 用 `provideDemoFullscreen()` 创建唯一一份 `useFullscreen` 控制器(`pageOnly`、`teleport`、`fullscreenClass: 'is-demo-fullscreen'`),`ScreenfullButton` 与 `DemoPreview` 通过 `useDemoFullscreen()` 共用它,全屏状态与「退出全屏」按钮始终同步;全屏目标由 App 解析为当前页面的 `[data-demo-preview]`。
58
-
59
- > `vite build` 的产物会自动从 unpkg 加载已发布的 `comps-vue3`;`vite dev` 仍走本地 20020,无需手动改 `host`。
60
-
61
- ## 本地开发
62
-
63
- ```bash
64
- # 终端 1:起远程组件(20020)
65
- pnpm comps-vue3:dev
66
-
67
- # 终端 2:起宿主(30020)
68
- pnpm demo-vue3:dev
69
- ```
70
-
71
- 打开 <http://localhost:30020> ,`远程组件` 示例会渲染 `comps-vue3` 的 `HelloWorld` 组件。
72
-
73
- 也可以让宿主指向已构建的远程产物:
74
-
75
- ```bash
76
- pnpm comps-vue3:build:local && pnpm comps-vue3:preview # 20020 上跑 hel_dist
77
- pnpm demo-vue3:dev
78
- ```
79
-
80
- ## 构建 / 发布
81
-
82
- ```bash
83
- pnpm demo-vue3:build # 产出 dist/
84
- pnpm --filter @melt-js/demo-vue3 run publish:stage # build → npm stage 暂存,等待 2FA 审批
85
- ```
86
-
87
- - `vite.config.ts` 的 `base` 为 `./`,发布后 unpkg 上的 `dist/index.html` 可直接加载同目录资源;hash 路由同样可用。
88
- - `files` 白名单只发布 `dist/` 与 README。
89
- - 生产构建自动使用 `https://unpkg.com/@melt-js/comps-vue3/hel_dist/hel-meta.json`,本地开发自动使用 `http://localhost:20020`;也可用 `VITE_REMOTE_HOST` 覆盖。
1
+ # @melt-js/demo-vue3
2
+
3
+ Vue 3 + Vite 5 的 hel 宿主(消费方)示例,消费 [`comps-vue3`](../comps-vue3)。
4
+
5
+ 页面使用 **vue-router(hash 模式)** 把示例拆成多个可切换的页面,顶部导航栏同时显示当前示例的简介,示例区右上角有**全屏按钮**(只放大当前示例的组件预览区,基于 `vue-fullscreen` 的 `pageOnly + teleport` 模式)。
6
+
7
+ | 项 | 值 |
8
+ | --- | --- |
9
+ | npm 包名 | `@melt-js/demo-vue3` |
10
+ | unpkg 入口 | `https://unpkg.com/@melt-js/demo-vue3/dist/index.html` |
11
+ | 端口 | `30020` |
12
+ | 消费的远程组件 | `comps-vue3`(开发 `http://localhost:20020` / 生产 unpkg) |
13
+ | 路由 | `vue-router@4.6.4`,hash 模式 |
14
+ | 全屏 | `vue-fullscreen@5.1.2`,`pageOnly` + `teleport`(不调用浏览器原生全屏) |
15
+
16
+ ## 示例路由
17
+
18
+ | 路由 | 导航文案 | 内容 |
19
+ | --- | --- | --- |
20
+ | `#/` | 简介 | demo 概览:依赖、端口、远端地址与示例入口 |
21
+ | `#/host` | 主应用 | 宿主自己的计数按钮,用来对照远程组件的行为 |
22
+ | `#/remote` | 远程组件 | `preFetchLib` 加载的 `comps-vue3` `HelloWorld` |
23
+ | `#/circular-menu` | 圆形菜单 | `CircularMenu`:随页面滚动 / 容器内吸顶两个示例 + 属性面板(wheel 半环、fan 整环,定位可切 relative / sticky / fixed / absolute) |
24
+ | `#/magnetic-button` | 磁吸按钮 | `MagneticButton`:默认参数与高强度参数并排对比 + 属性面板(文案 / 强度 / 主色 / 光点) |
25
+ | `#/spotlight-button` | 聚光灯按钮 | `SpotlightButton`:紧光斑与宽光斑对比 + 属性面板(文案 / 配色 / 光斑半径) |
26
+ | `#/glow-border` | 流光边框 | `GlowBorder`:160 / 240 / 320 三种尺寸对比 + 属性面板(尺寸 / 圆角 / 厚度 / 配色 / 时长 / 光晕) |
27
+ | `#/typewriter` | 逐字打印 | `Typewriter`:两行文案 + `restart()` / `finish()` 手动控制 + 属性面板(文案 / 字速 / 延迟 / 循环 / 光标) |
28
+ | `#/tilt-card` | 倾斜卡片 | `TiltCard`:6° / 14°(+反光) / 20° 三张卡片对比 + 属性面板(角度 / 放大 / 圆角 / 反光 / 阴影) |
29
+ | `#/floating-label-input` | 浮动标签 | `FloatingLabelInput`:默认 / 换主色 / 带初始值三态对比 + 属性面板(文案 / 类型 / 主色 / 高度 / 禁用) |
30
+ | `#/password-input` | 密码框 | `PasswordInput`:带标签 / 无标签 / 关掉切换按钮 + 属性面板(文案 / 占位 / 主色 / toggleable / autocomplete) |
31
+ | `#/expand-search` | 收缩搜索 | `ExpandSearch`:默认与紧凑两种尺寸,回车派发 `search` + 属性面板(占位 / 主色 / 收起宽度 / 圆角 / 高度 / 清除按钮) |
32
+ | `#/cyber-input` | 故障输入框 | `CyberInput`:完整故障效果(页面另配矩阵雨背景)与关掉故障的安静版 + 属性面板(文案 / 霓虹色 / 三个效果开关) |
33
+ | `#/corgi-input` | 柯基输入框 | `CorgiInput`:两只不同尺寸的小狗对比 + 属性面板(气泡文案 / 跟随开关 / 眼睛幅度 / 尺寸) |
34
+ | `#/grid-loading` | 九宫格 loading | `GridLoading`:默认 / 换色放大 / 带文案三种 + 属性面板(尺寸 / 颜色 / 周期 / 错开 / 间距 / 圆角 / 暂停) |
35
+ | `#/gooey-balls-loading` | 元素交融 | `GooeyBallsLoading`:多彩 / 单色 / 关掉 gooey 三态对比 + 属性面板(轨道 / 球径 / 球数 / 周期 / 融合开关) |
36
+ | `#/dna-loading` | DNA loading | `DnaLoading`:12 对与 20 对节点、两套配色对比 + 属性面板(行数 / 摆幅 / 周期 / 双色 / 文案) |
37
+ | `#/neon-ring-loading` | 霓虹环 | `NeonRingLoading`:自动假进度与受控进度(滑杆驱动)对比 + 属性面板(尺寸 / 四色 / 倍速 / auto / progress) |
38
+ | `#/gear-loading` | 齿轮联动 | `GearLoading`:原尺寸 / 缩小加速 / 关掉蒸汽三种 + 属性面板(尺寸 / 倍速 / 进度条 / 蒸汽 / 文案) |
39
+ | `#/vortex-loading` | 粒子漩涡 | `VortexLoading`:默认 / 换色相加粒子 / 关拖尾连线三种 + 属性面板(边长 / 数量 / 色相 / 倍速 / 连线 / 拖尾) |
40
+ | `#/water-ball-loading` | 水球进度 | `WaterBallLoading`:自动水位与滑杆驱动的受控水位对比 + 属性面板(球径 / 双色 / auto / progress / 时长 / 波浪倍速 / 水滴) |
41
+ | `#/hourglass-loading` | 沙漏 | `HourglassLoading`:10s / 90px 快速 / 160px 冷色三档 + 属性面板(宽度 / 周期 / 沙色 / 木框色) |
42
+ | `#/cube-wave-loading` | 盒子波场 | `CubeWaveLoading`:5×5 / 换色 / 7×7 大矩阵三种 + 属性面板(边长 / 行列数 / 色相 / 节奏) |
43
+ | `#/jelly-loading` | 果冻弹跳 | `JellyLoading`:5 只 / 8 只更快 / 关掉表情三种 + 属性面板(画布宽 / 数量 / 速度 / 错峰 / 表情) |
44
+ | `#/domino-loading` | 多米诺 | `DominoLoading`:14 / 20 张更慢 / 关掉进度条三种 + 属性面板(画布宽 / 张数 / 速度 / 进度条 / 文案) |
45
+ | `#/slide-auth-panel` | 拟态登录 | `SlideAuthPanel`:登录态与注册态左右互换滑动 + 属性面板(模式 / 主色 / 高度 / 滑动时长 / 两套文案) |
46
+ | `#/terminal-auth` | 终端登录 | `TerminalAuth`:荧光绿与青色两套、可切登录/注册 + 属性面板(模式 / 荧光色 / 标题 / 按钮文案 / 强度条 / 日志 / 扫描线) |
47
+ | `#/wheel-menu-button` | 轮盘按钮 | `WheelMenuButton`:可拖拽的悬浮轮盘,拖动不误触、轻点展开环形菜单 + 属性面板(尺寸 / 半径 / 配色 / 拖拽阈值 / 贴边留白 / 初始位置) |
48
+ | `#/photo-sphere` | 3D 照片球 | `PhotoSphere`:12 张渐变图组成的球体,拖动旋转 / 点击放大 / 本地上传替换 + 属性面板(尺寸 / 缩略图 / 半径 / 自转 / 灵敏度 / 摩擦 / 压缩上限) |
49
+ | `#/warp-starfield` | 星际穿越 | `WarpStarfield`:可交互的曲速星流(指针控方向 / 按住加速 / 滚轮调速)+ 低高速两个静态对比 + 属性面板(星数 / 巡航 / 倍率 / 调速区间 / 交互 / HUD / 星云) |
50
+ | `#/china-route-map` | 地图轨迹 | `ChinaRouteMap`:中国地图上按经纬度画路线,点地图加点、缩放平移、播放镜头跟随 + 属性面板(时长 / 跟随倍数 / 开关项 / 主色) |
51
+ | `#/split-code-preview` | 界面切代码 | `SplitCodePreview`:拖动分割线在渲染结果与源码间切换,右侧代码可编辑并实时重渲染 + 属性面板(split / glitch / 圆角 / 主色) |
52
+ | `#/book-flip` | 书本翻页 | `BookFlip`:4 张内页 + 首尾封面,点击 / 按钮 / 键盘翻转,另有小尺寸与自动播放两个对比 + 属性面板(尺寸 / 时长 / 纸色 / 页码 / 自动播放) |
53
+ | `#/minecraft-chest` | Minecraft 箱子 | `MinecraftChest`:拖动环视、点击开盖的方块箱,另有去场景只读与小一号预开两个对比 + 属性面板(六种尺寸 / 缩放 / 视角 / 交互 / 音效 / 场景) |
54
+ | `#/bilibili-banner` | B站横幅 | `BilibiliBanner`:从 bilibili-banner-master 的 Vue 版本迁移的 23 套节日 banner,每个主题一个按钮切换,多层图片 / 视频跟随指针横向视差 + 属性面板(主题下拉 / 高度 / logo / 底色) |
55
+ | `#/film-roll` | 旅行胶卷 | `FilmRoll`:12 格横向胶片,拖动带惯性拉扯与重影、松手吸附,时间轴跳转 + 全屏预览以及两个小尺寸变体 + 属性面板(单格尺寸 / 间距 / 当前格 / 开关项) |
56
+ | `#/rope-gallery` | 绳串图片 | `RopeGallery`:7 张照片挂在会晃的绳子上,风强度滑杆接 `v-model:wind`、可拖动拉绳,另有小尺寸 / 大风两个对比 + 属性面板(风强度 / 照片尺寸 / 两端留白 / 开关项) |
57
+ | `#/compare-slider` | 图片对比 | `CompareSlider`:原片 / 调色后对比卡,可上传两张图、交换、重置、自动巡航,另有 4:3 与 1:1 两个变体 + 属性面板(位置 / 宽高比 / 把手 / 主色 / 自动巡航) |
58
+ | `#/poster-collage` | 海报拼贴 | `PosterCollage`:1400×800 设计稿上 12 层人物拼贴,悬停放大并置顶,另有 6 层与「带底图」两个变体 + 属性面板(画布尺寸 / 底图 / 放大倍数 / 层级偏移) |
59
+ | `#/lift-card` | 立体卡片 | `LiftCard`:hover 时底板仰起、照片沿 Z 轴弹出、倒影浮现,三张不同参数 + 一张大卡 + 属性面板(尺寸 / 仰角 / 弹出距离 / 配色 / 倒影 / 强制展开) |
60
+
61
+ 约定:
62
+
63
+ - **hash 模式**:demo 的构建产物会通过 unpkg 上的 `dist/index.html` 打开,history 模式在那种静态文件路径下会 404。
64
+ - 路由清单在 `src/router/index.ts`,每条路由的 `meta.title` / `meta.description` 同时驱动导航栏 tab 与下方那行简介;新增示例只要加一条路由。
65
+ - 全屏按钮只把当前示例标记为 `data-demo-preview` 的组件预览区送进全屏,页面标题、简介、说明与页脚都留在屏幕外;全屏借用 `vue-fullscreen` 5.x 的 `pageOnly + teleport` 模式:预览区临时挂到 body、用 fixed 铺满视口,退出时还原到页面原位置,不调用浏览器原生 Fullscreen API(iframe 内也可用)。预览区右下角提供「退出全屏」浮层按钮,Esc 也可退出;切换路由时会自动退出并还原。
66
+ - 需要全屏的示例在组件预览区外包一层 `<DemoPreview>`;简介页没有预览区,在路由 `meta` 里标记 `fullscreen: false`,工具栏不显示全屏按钮。
67
+ - 每个组件示例都用 `<LevaPlayground>` 包一层 leva 实验台(左侧组件 + 右侧 `libs-vue3-leva` 的 `PropsPanel`),面板值通过 `v-bind` 实时写回组件;不再有单独的「属性面板」tab。`LevaPlayground` 支持 `presets` / `listeners` / `slotHtml` / `stage-style`。
68
+
69
+ ## 文件结构
70
+
71
+ ```
72
+ demo-vue3/src
73
+ ├── App.vue # demo 外壳:导航栏 + 示例舞台(含全屏按钮)+ router-view
74
+ ├── components/
75
+ │ ├── DemoNavbar.vue # 顶部导航栏:示例 tab + 当前示例简介
76
+ │ ├── DemoPreview.vue # 组件预览区:全屏目标 + 「退出全屏」浮层按钮
77
+ │ ├── LevaPlayground.vue # 单个示例的 leva 实验台:组件 + schema → PropsPlayground
78
+ │ ├── MatrixRain.vue # 赛博朋克页 / 终端登录页的矩阵雨背景(页面氛围,不属于组件)
79
+ │ └── ScreenfullButton.vue # 示例区全屏按钮
80
+ ├── composables/
81
+ │ └── useDemoFullscreen.ts # vue-fullscreen 的 pageOnly + teleport 封装(provide/inject)
82
+ ├── configs/
83
+ │ ├── app.ts # demo 展示信息(名称 / 端口 / unpkg 入口)
84
+ │ └── remote.ts # 远程组件地址与 preFetchLib(Promise 复用)
85
+ ├── router/index.ts # 示例路由(hash 模式)
86
+ ├── styles/demo-shell.css # 外壳样式(导航栏 / 舞台 / 全屏 / 通用排版)
87
+ ├── style.css # 变量与全局重置
88
+ └── views/ # OverviewView / HostView / RemoteView / CircularMenuView / MagneticButtonView / SpotlightButtonView / GlowBorderView / TypewriterView / TiltCardView / FloatingLabelInputView / PasswordInputView / ExpandSearchView / CyberInputView / CorgiInputView / GridLoadingView / GooeyBallsLoadingView / DnaLoadingView / NeonRingLoadingView / GearLoadingView / VortexLoadingView / WaterBallLoadingView / HourglassLoadingView / CubeWaveLoadingView / JellyLoadingView / DominoLoadingView / SlideAuthPanelView / TerminalAuthView / WheelMenuButtonView / PhotoSphereView / WarpStarfieldView / ChinaRouteMapView / SplitCodePreviewView / BookFlipView / MinecraftChestView / FilmRollView / RopeGalleryView / CompareSliderView / PosterCollageView / LiftCardView
89
+ ```
90
+
91
+ ## 关键流程
92
+
93
+ 1. `src/main.ts`:`bindVueRuntime({ Vue })` 把宿主 vue 挂到 `window.Vue`,远程组件 external 的 `vue` 会复用这一份(少了这一份会出现双 vue,表现为远程组件点击后视图不更新);随后在入口处调用 `loadRemoteLib()` 预加载远程组件,就绪后再挂载宿主。
94
+ 2. `src/configs/remote.ts`:按 `import.meta.env.PROD` 选择远端地址——生产构建指向 unpkg 上的 `hel-meta.json`,本地开发指向 `http://localhost:20020`,可用 `VITE_REMOTE_HOST` 覆盖。`loadRemoteLib()` 内部调用 `preFetchLib('comps-vue3', { custom: { host } })`:host 为 `hel-meta.json` 时直接取元数据,本地 dev server 没有该文件时解析 `index.html`。
95
+ 3. `src/router/index.ts` + `src/loadApp.ts`:创建 hash 路由并在 `createApp(App).use(router)` 时挂载;`#/host`、`#/remote` 等示例按路由懒加载。
96
+ 4. `src/views/RemoteView.vue`:`defineAsyncComponent` 复用入口处已预加载的同一个 Promise,渲染远程组件的 `HelloWorld`。
97
+ 5. `src/composables/useDemoFullscreen.ts` + `src/components/DemoPreview.vue`:`App.vue` 用 `provideDemoFullscreen()` 创建唯一一份 `useFullscreen` 控制器(`pageOnly`、`teleport`、`fullscreenClass: 'is-demo-fullscreen'`),`ScreenfullButton` 与 `DemoPreview` 通过 `useDemoFullscreen()` 共用它,全屏状态与「退出全屏」按钮始终同步;全屏目标由 App 解析为当前页面的 `[data-demo-preview]`。
98
+
99
+ > `vite build` 的产物会自动从 unpkg 加载已发布的 `comps-vue3`;`vite dev` 仍走本地 20020,无需手动改 `host`。
100
+
101
+ ## 本地开发
102
+
103
+ ```bash
104
+ # 终端 1:起远程组件(20020)
105
+ pnpm comps-vue3:dev
106
+
107
+ # 终端 2:起宿主(30020)
108
+ pnpm demo-vue3:dev
109
+ ```
110
+
111
+ 打开 <http://localhost:30020> ,`远程组件` 示例会渲染 `comps-vue3` 的 `HelloWorld` 组件。
112
+
113
+ 也可以让宿主指向已构建的远程产物:
114
+
115
+ ```bash
116
+ pnpm comps-vue3:build:local && pnpm comps-vue3:preview # 20020 上跑 hel_dist
117
+ pnpm demo-vue3:dev
118
+ ```
119
+
120
+ ## 构建 / 发布
121
+
122
+ ```bash
123
+ pnpm demo-vue3:build # 产出 dist/
124
+ pnpm --filter @melt-js/demo-vue3 run publish:stage # build → npm stage 暂存,等待 2FA 审批
125
+ ```
126
+
127
+ - `vite.config.ts` 的 `base` 为 `./`,发布后 unpkg 上的 `dist/index.html` 可直接加载同目录资源;hash 路由同样可用。
128
+ - `files` 白名单只发布 `dist/` 与 README。
129
+ - 生产构建自动使用 `https://unpkg.com/@melt-js/comps-vue3/hel_dist/hel-meta.json`,本地开发自动使用 `http://localhost:20020`;也可用 `VITE_REMOTE_HOST` 覆盖。
@@ -0,0 +1,2 @@
1
+ import{_ as g,a as v}from"./_plugin-vue_export-helper-BbhRsZUO.js";import{L as _}from"./LevaPlayground-ChwSFC5s.js";import{d as f,f as o,g as e,t as b,F as h,j as w,A as a,u as i,m as n,l as B,H as y,b as k,c as x,o as d,q as E}from"./index-CthyPCf3.js";import"./loadApp-BA8EV_l7.js";const C=[{name:"theme",type:"'autumn-1' | 'autumn-2' | 'autumn-3' | 'autumn-4' | 'autumn-5' | 'spring-1' | 'spring-2' | 'spring-3' | 'spring-4' | 'spring-5' | 'spring-6' | 'summer-1' | 'summer-2' | 'summer-3' | 'summer-4' | 'summer-5' | 'summer-6' | 'summer-7' | 'winter-1' | 'winter-2' | 'winter-3' | 'winter-4' | 'winter-5'",description:"banner 主题,对应原仓库的 23 套节日 banner",default:'"spring-1"',required:!1,version:"",isEvent:!1,bindable:!0,value:"spring-1",control:{options:{"autumn-1":"autumn-1","autumn-2":"autumn-2","autumn-3":"autumn-3","autumn-4":"autumn-4","autumn-5":"autumn-5","spring-1":"spring-1","spring-2":"spring-2","spring-3":"spring-3","spring-4":"spring-4","spring-5":"spring-5","spring-6":"spring-6","summer-1":"summer-1","summer-2":"summer-2","summer-3":"summer-3","summer-4":"summer-4","summer-5":"summer-5","summer-6":"summer-6","summer-7":"summer-7","winter-1":"winter-1","winter-2":"winter-2","winter-3":"winter-3","winter-4":"winter-4","winter-5":"winter-5"}}},{name:"height",type:"number",description:"banner 高度,单位 px(原版固定 150)",default:"150",required:!1,version:"",isEvent:!1,bindable:!0,value:150,control:{min:90,max:320,step:2}},{name:"logo",type:"boolean",description:"是否显示左上角的 bilibili logo",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"background",type:"string",description:"没有图层铺满时的底色",default:'"#f9f9f9"',required:!1,version:"",isEvent:!1,bindable:!0,value:"#f9f9f9"}],V={class:"demo-page"},q={class:"bb-demo"},L={class:"bb-demo__head"},S={class:"bb-demo__tag"},N={class:"bb-demo__themes"},F=["onClick"],H={class:"bb-demo__stage"},T={class:"bb-demo__row"},$={class:"bb-demo__cell"},j={class:"bb-demo__cell"},z=f({__name:"BilibiliBannerView",setup(A){const t=y("BilibiliBanner"),m=[{value:"spring-1",label:"春日 1"},{value:"spring-2",label:"春日 2"},{value:"spring-3",label:"春日 3"},{value:"spring-4",label:"春日 4"},{value:"spring-5",label:"春日 5"},{value:"spring-6",label:"春日 6"},{value:"summer-1",label:"夏日 1"},{value:"summer-2",label:"夏日 2"},{value:"summer-3",label:"夏日 3"},{value:"summer-4",label:"夏日 4"},{value:"summer-5",label:"夏日 5"},{value:"summer-6",label:"夏日 6"},{value:"summer-7",label:"夏日 7"},{value:"autumn-1",label:"秋日 1"},{value:"autumn-2",label:"秋日 2"},{value:"autumn-3",label:"秋日 3"},{value:"autumn-4",label:"秋日 4"},{value:"autumn-5",label:"秋日 5"},{value:"winter-1",label:"冬日 1"},{value:"winter-2",label:"冬日 2"},{value:"winter-3",label:"冬日 3"},{value:"winter-4",label:"冬日 4"},{value:"winter-5",label:"冬日 5"}],u=k("spring-1"),p=x(()=>{var r,l;return(l=(r=m.find(s=>s.value===u.value))==null?void 0:r.label)!=null?l:""}),c={display:"block",height:"210px",padding:"12px",background:"#eef1f5"};return(r,l)=>(d(),o("div",V,[l[4]||(l[4]=e("header",null,[e("p",{class:"demo-page__eyebrow"},"远程组件"),e("h1",{class:"demo-page__title"},"comps-vue3 · BilibiliBanner"),e("p",{class:"demo-page__lead"}," 从 bilibili-banner-master 的 Vue 版本迁移来的 B 站节日横幅:23 套主题,每套由多层图片 / 视频叠成, 每层按各自的 bench 系数跟手,指针在横幅上左右移动就会错位出视差;触摸端点一下左上角 logo 复位。 图层资源随 comps-vue3 的 public/bilibili 一起下发,远程加载时会自动指到远端包的地址。 ")],-1)),e("section",q,[e("div",L,[l[0]||(l[0]=e("h2",{class:"demo-page__section-title"},"主题",-1)),e("code",S,b(p.value),1)]),e("div",N,[(d(),o(h,null,w(m,s=>e("button",{key:s.value,type:"button",class:E(["bb-demo__theme",{"is-active":s.value===u.value}]),onClick:D=>u.value=s.value},b(s.label),11,F)),64))]),e("div",H,[a(i(t),{theme:u.value,height:180},null,8,["theme"])]),l[3]||(l[3]=e("p",{class:"bb-demo__hint"}," 把指针放到横幅上左右移动(或横向滑动)试试;换成主题时图层会重新铺开。 ",-1)),e("div",T,[e("div",$,[a(i(t),{theme:"winter-1",height:120,logo:!1}),l[1]||(l[1]=e("p",null,[e("code",null,':logo="false"'),n(" 去掉左上角 logo,单独看图层")],-1))]),e("div",j,[a(i(t),{theme:"summer-5",height:120,background:"#101820"}),l[2]||(l[2]=e("p",null,[e("code",null,"background"),n(" 是图层没铺满时的底色")],-1))])])]),a(g,{class:"bb-preview"},{default:B(()=>[a(_,{name:"BilibiliBanner",component:i(t),schema:i(C),"stage-style":c},null,8,["component","schema"])]),_:1}),l[5]||(l[5]=e("p",{class:"demo-note"},[e("span",null,[n(" 属性面板里 "),e("code",null,"theme"),n(" 是 23 套主题的下拉,"),e("code",null,"height"),n(" 是横幅高度, "),e("code",null,"logo"),n(" 控制左上角 logo,"),e("code",null,"background"),n(" 是兜底底色; 远程包会把 "),e("code",null,"public/bilibili/<theme>/"),n(" 里的图层按绝对地址加载。 ")])],-1))]))}}),J=v(z,[["__scopeId","data-v-37501e4b"]]);export{J as default};
2
+ //# sourceMappingURL=BilibiliBannerView-CDLH7Krl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BilibiliBannerView-CDLH7Krl.js","sources":["../../src/views/BilibiliBannerView.vue"],"sourcesContent":["<template>\n <div class=\"demo-page\">\n <header>\n <p class=\"demo-page__eyebrow\">远程组件</p>\n <h1 class=\"demo-page__title\">comps-vue3 · BilibiliBanner</h1>\n <p class=\"demo-page__lead\">\n 从 bilibili-banner-master 的 Vue 版本迁移来的 B 站节日横幅:23 套主题,每套由多层图片 / 视频叠成,\n 每层按各自的 bench 系数跟手,指针在横幅上左右移动就会错位出视差;触摸端点一下左上角 logo 复位。\n 图层资源随 comps-vue3 的 public/bilibili 一起下发,远程加载时会自动指到远端包的地址。\n </p>\n </header>\n\n <section class=\"bb-demo\">\n <div class=\"bb-demo__head\">\n <h2 class=\"demo-page__section-title\">主题</h2>\n <code class=\"bb-demo__tag\">{{ currentLabel }}</code>\n </div>\n\n <div class=\"bb-demo__themes\">\n <button\n v-for=\"item in THEMES\"\n :key=\"item.value\"\n type=\"button\"\n class=\"bb-demo__theme\"\n :class=\"{ 'is-active': item.value === theme }\"\n @click=\"theme = item.value\"\n >\n {{ item.label }}\n </button>\n </div>\n\n <div class=\"bb-demo__stage\">\n <BilibiliBanner :theme=\"theme\" :height=\"180\" />\n </div>\n <p class=\"bb-demo__hint\">\n 把指针放到横幅上左右移动(或横向滑动)试试;换成主题时图层会重新铺开。\n </p>\n\n <div class=\"bb-demo__row\">\n <div class=\"bb-demo__cell\">\n <BilibiliBanner theme=\"winter-1\" :height=\"120\" :logo=\"false\" />\n <p><code>:logo=\"false\"</code> 去掉左上角 logo,单独看图层</p>\n </div>\n <div class=\"bb-demo__cell\">\n <BilibiliBanner theme=\"summer-5\" :height=\"120\" background=\"#101820\" />\n <p><code>background</code> 是图层没铺满时的底色</p>\n </div>\n </div>\n </section>\n\n <DemoPreview class=\"bb-preview\">\n <LevaPlayground\n name=\"BilibiliBanner\"\n :component=\"BilibiliBanner\"\n :schema=\"BilibiliBannerSchema\"\n :stage-style=\"stageStyle\"\n />\n </DemoPreview>\n\n <p class=\"demo-note\">\n <span>\n 属性面板里 <code>theme</code> 是 23 套主题的下拉,<code>height</code> 是横幅高度,\n <code>logo</code> 控制左上角 logo,<code>background</code> 是兜底底色;\n 远程包会把 <code>public/bilibili/&lt;theme&gt;/</code> 里的图层按绝对地址加载。\n </span>\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport BilibiliBannerSchema from '@melt-js/comps-vue3/src/components/BilibiliBanner/propsSchema.json'\nimport DemoPreview from '../components/DemoPreview.vue'\nimport LevaPlayground from '../components/LevaPlayground.vue'\nimport { remoteComponent } from '../configs/remote'\n\nconst BilibiliBanner = remoteComponent('BilibiliBanner')\n\n/** 与原仓库 inst 组件一一对应的主题名,顺序按春 → 夏 → 秋 → 冬 */\nconst THEMES = [\n { value: 'spring-1', label: '春日 1' },\n { value: 'spring-2', label: '春日 2' },\n { value: 'spring-3', label: '春日 3' },\n { value: 'spring-4', label: '春日 4' },\n { value: 'spring-5', label: '春日 5' },\n { value: 'spring-6', label: '春日 6' },\n { value: 'summer-1', label: '夏日 1' },\n { value: 'summer-2', label: '夏日 2' },\n { value: 'summer-3', label: '夏日 3' },\n { value: 'summer-4', label: '夏日 4' },\n { value: 'summer-5', label: '夏日 5' },\n { value: 'summer-6', label: '夏日 6' },\n { value: 'summer-7', label: '夏日 7' },\n { value: 'autumn-1', label: '秋日 1' },\n { value: 'autumn-2', label: '秋日 2' },\n { value: 'autumn-3', label: '秋日 3' },\n { value: 'autumn-4', label: '秋日 4' },\n { value: 'autumn-5', label: '秋日 5' },\n { value: 'winter-1', label: '冬日 1' },\n { value: 'winter-2', label: '冬日 2' },\n { value: 'winter-3', label: '冬日 3' },\n { value: 'winter-4', label: '冬日 4' },\n { value: 'winter-5', label: '冬日 5' },\n] as const\n\ntype BannerTheme = (typeof THEMES)[number]['value']\n\nconst theme = ref<BannerTheme>('spring-1')\nconst currentLabel = computed(() => THEMES.find((item) => item.value === theme.value)?.label ?? '')\n\nconst stageStyle = {\n display: 'block',\n height: '210px',\n padding: '12px',\n background: '#eef1f5',\n}\n</script>\n\n<style scoped>\n.bb-demo {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.bb-demo__head {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: baseline;\n justify-content: space-between;\n}\n\n.bb-demo__head .demo-page__section-title {\n margin: 0;\n}\n\n.bb-demo__tag {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--demo-accent-soft);\n color: var(--demo-accent);\n font-size: 12px;\n font-weight: 600;\n}\n\n.bb-demo__themes {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.bb-demo__theme {\n padding: 5px 12px;\n border: 1px solid var(--demo-border);\n border-radius: 999px;\n background: transparent;\n color: var(--demo-fg);\n font-size: 12px;\n cursor: pointer;\n transition: color 0.2s, border-color 0.2s, background 0.2s;\n}\n\n.bb-demo__theme:hover {\n border-color: var(--demo-accent);\n color: var(--demo-accent);\n}\n\n.bb-demo__theme.is-active {\n border-color: var(--demo-accent);\n background: var(--demo-accent);\n color: #fff;\n}\n\n.bb-demo__stage {\n overflow: hidden;\n border-radius: 16px;\n background: #f9f9f9;\n box-shadow: 0 18px 40px -24px rgba(15, 23, 42, 0.55);\n}\n\n.bb-demo__hint {\n margin: 0;\n color: var(--demo-muted);\n font-size: 13px;\n}\n\n.bb-demo__row {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n margin-top: 6px;\n}\n\n.bb-demo__cell {\n flex: 1 1 320px;\n min-width: 260px;\n}\n\n.bb-demo__cell > :first-child {\n overflow: hidden;\n border-radius: 14px;\n}\n\n.bb-demo__cell p {\n margin: 8px 0 0;\n color: var(--demo-muted);\n font-size: 12px;\n}\n</style>\n"],"names":["BilibiliBanner","remoteComponent","THEMES","theme","ref","currentLabel","computed","_b","_a","item","stageStyle","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_cache","_hoisted_4","_toDisplayString","_hoisted_5","_Fragment","_renderList","$event","_hoisted_6","_hoisted_7","_createVNode","_unref","_hoisted_8","_hoisted_9","_hoisted_10","DemoPreview","LevaPlayground","BilibiliBannerSchema"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ .bb-demo[data-v-37501e4b]{display:flex;flex-direction:column;gap:12px}.bb-demo__head[data-v-37501e4b]{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:baseline;justify-content:space-between}.bb-demo__head .demo-page__section-title[data-v-37501e4b]{margin:0}.bb-demo__tag[data-v-37501e4b]{padding:2px 8px;border-radius:999px;background:var(--demo-accent-soft);color:var(--demo-accent);font-size:12px;font-weight:600}.bb-demo__themes[data-v-37501e4b]{display:flex;flex-wrap:wrap;gap:8px}.bb-demo__theme[data-v-37501e4b]{padding:5px 12px;border:1px solid var(--demo-border);border-radius:999px;background:transparent;color:var(--demo-fg);font-size:12px;cursor:pointer;transition:color .2s,border-color .2s,background .2s}.bb-demo__theme[data-v-37501e4b]:hover{border-color:var(--demo-accent);color:var(--demo-accent)}.bb-demo__theme.is-active[data-v-37501e4b]{border-color:var(--demo-accent);background:var(--demo-accent);color:#fff}.bb-demo__stage[data-v-37501e4b]{overflow:hidden;border-radius:16px;background:#f9f9f9;box-shadow:0 18px 40px -24px #0f172a8c}.bb-demo__hint[data-v-37501e4b]{margin:0;color:var(--demo-muted);font-size:13px}.bb-demo__row[data-v-37501e4b]{display:flex;flex-wrap:wrap;gap:20px;margin-top:6px}.bb-demo__cell[data-v-37501e4b]{flex:1 1 320px;min-width:260px}.bb-demo__cell[data-v-37501e4b]>:first-child{overflow:hidden;border-radius:14px}.bb-demo__cell p[data-v-37501e4b]{margin:8px 0 0;color:var(--demo-muted);font-size:12px}
@@ -0,0 +1 @@
1
+ .book-demo[data-v-c89c9df1]{display:flex;flex-direction:column;gap:10px}.book-demo__head[data-v-c89c9df1]{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:baseline;justify-content:space-between}.book-demo__head .demo-page__section-title[data-v-c89c9df1]{margin:0}.book-demo__tag[data-v-c89c9df1]{padding:2px 8px;border-radius:999px;background:var(--demo-accent-soft);color:var(--demo-accent);font-size:12px;font-weight:600}.book-demo__stage[data-v-c89c9df1]{display:flex;align-items:center;justify-content:center;padding:36px 24px;border-radius:16px;background:#4f4e68}.book-demo__stage--small[data-v-c89c9df1]{padding:20px}.book-demo__hint[data-v-c89c9df1]{margin:0;color:var(--demo-muted);font-size:13px}.book-demo__row[data-v-c89c9df1]{display:flex;flex-wrap:wrap;gap:20px;margin-top:6px}.book-demo__cell[data-v-c89c9df1]{flex:1 1 320px;min-width:280px}.book-demo__cell p[data-v-c89c9df1]{margin:8px 0 0;color:var(--demo-muted);font-size:12px}.book-preview.is-demo-fullscreen[data-v-c89c9df1]{padding:16px;background:#4f4e68}
@@ -0,0 +1,15 @@
1
+ import{_ as x,a as w}from"./_plugin-vue_export-helper-BbhRsZUO.js";import{L as E}from"./LevaPlayground-ChwSFC5s.js";import{d as V,f as q,g as e,m as o,t as d,A as n,u as a,l as F,D as B,H as C,b as _,c as S,o as $}from"./index-CthyPCf3.js";import"./loadApp-BA8EV_l7.js";const I=[{name:"pages",type:"() => { front: Face; back: Face }[]",description:"内页:每张叶子有 front / back 两面(title / text / image)",default:"[]",required:!1,version:"",isEvent:!1,bindable:!1},{name:"cover",type:"() => Face",description:"首封面(合上时看到的那一面)",default:`{
2
+ title: '封面',
3
+ text: ''
4
+ }`,required:!1,version:"",isEvent:!1,bindable:!1},{name:"endCover",type:"() => Face",description:"尾封面(翻到最后看到的那一面)",default:`{
5
+ title: '',
6
+ text: ''
7
+ }`,required:!1,version:"",isEvent:!1,bindable:!1},{name:"width",type:"number",description:"书页宽度,单位 px(单页宽 = 一半)",default:"560",required:!1,version:"",isEvent:!1,bindable:!0,value:560,control:{min:360,max:900,step:20}},{name:"height",type:"number",description:"书页高度,单位 px",default:"380",required:!1,version:"",isEvent:!1,bindable:!0,value:380,control:{min:240,max:560,step:20}},{name:"duration",type:"number",description:"一次翻页的时长,单位毫秒",default:"1200",required:!1,version:"",isEvent:!1,bindable:!0,value:1200,control:{min:300,max:3e3,step:100}},{name:"paper",type:"string",description:"纸色",default:'"#e9e6c4"',required:!1,version:"",isEvent:!1,bindable:!0,value:"#e9e6c4"},{name:"ink",type:"string",description:"正文颜色",default:'"#3f3b2f"',required:!1,version:"",isEvent:!1,bindable:!0,value:"#3f3b2f"},{name:"modelValue",type:"number",description:"当前翻到第几张(可用 v-model 双向绑定)",default:"0",required:!1,version:"",isEvent:!1,bindable:!0,value:0,control:{min:0,max:12,step:1}},{name:"controls",type:"boolean",description:"是否显示上一页 / 下一页按钮和页码",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"hint",type:"boolean",description:"是否显示底部操作提示",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"autoplay",type:"boolean",description:"是否自动往后翻(翻到最后一页停下)",default:"false",required:!1,version:"",isEvent:!1,bindable:!0,value:!1},{name:"autoplayInterval",type:"number",description:"自动翻页的间隔,单位毫秒",default:"2600",required:!1,version:"",isEvent:!1,bindable:!0,value:2600,control:{min:800,max:6e3,step:200}}],T={class:"demo-page"},N={class:"book-demo"},A={class:"book-demo__head"},D={class:"book-demo__tag"},G={class:"book-demo__stage"},L={class:"book-demo__hint"},P={class:"book-demo__row"},z={class:"book-demo__cell"},U={class:"book-demo__stage book-demo__stage--small"},j={class:"book-demo__cell"},H={class:"book-demo__stage book-demo__stage--small"},M=V({__name:"BookFlipView",setup(O){const i=C("BookFlip"),v=[["#f97316","#ec4899"],["#22d3ee","#3b82f6"],["#a855f7","#6366f1"],["#34d399","#14b8a6"],["#fbbf24","#f97316"],["#f472b6","#a855f7"],["#38bdf8","#818cf8"],["#4ade80","#22d3ee"]];function r(s){const[t,m]=v[s%v.length],k=`<svg xmlns="http://www.w3.org/2000/svg" width="300" height="400">
8
+ <defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
9
+ <stop offset="0" stop-color="${t}"/><stop offset="1" stop-color="${m}"/>
10
+ </linearGradient></defs>
11
+ <rect width="300" height="400" fill="url(#g)"/>
12
+ <text x="150" y="222" font-family="sans-serif" font-size="120" font-weight="700"
13
+ fill="rgba(255,255,255,0.82)" text-anchor="middle">${s}</text>
14
+ </svg>`;return`data:image/svg+xml;utf8,${encodeURIComponent(k)}`}const f=["第一页:组件从一整页 demo 里被拆出来,带上自己的 props、事件和样式,能独立站在任何一个 Vue 3 项目里。","第二页:所有视觉常量都变成了 props —— 尺寸、颜色、时长、开关,在属性面板里拖一拖就能看到差别。","第三页:交互逻辑重新组织过:拖动、点击、缩放这些手势用 Pointer Events 统一处理,鼠标和触摸是同一套。","第四页:动画尽量交给 CSS 和 rAF,Vue 只负责状态;每帧只写必要的 DOM,避免把响应式拖进动画循环里。"],l=f.map((s,t)=>({front:{title:`第 ${t*2+1} 面`,text:s,image:r(t*2+1)},back:{title:`第 ${t*2+2} 面`,text:f[(t+1)%f.length],image:r(t*2+2)}})),c={title:"『组件提取笔记』",text:"",image:r(0)},p={title:"",text:"",image:r(7)},u=_(0),b=S(()=>l.length+2),g=_("(还没翻过)"),h={display:"flex",alignItems:"center",justifyContent:"center",height:"560px",padding:"24px",background:"#4f4e68"};function y(s){g.value=`翻到第 ${s} 张`}return(s,t)=>($(),q("div",T,[t[8]||(t[8]=e("header",null,[e("p",{class:"demo-page__eyebrow"},"远程组件"),e("h1",{class:"demo-page__title"},"comps-vue3 · BookFlip"),e("p",{class:"demo-page__lead"},[o(" 来自 video-code 的「书本翻页」:一叠对开页贴在书脊右侧,绕左边缘 "),e("code",null,"rotateY(-180deg)"),o(" 翻过去,正面转走背面转来,每面都有标题、正文和底图。原 demo 用 "),e("code",null,"z-index"),o(" + "),e("code",null,"visibility"),o(" 关键帧处理正反面切换;组件里改用 "),e("code",null,"backface-visibility: hidden"),o(", 逻辑简单很多,另外补了上一页 / 下一页、页码、键盘左右键和自动播放。 ")])],-1)),e("section",N,[e("div",A,[t[1]||(t[1]=e("h2",{class:"demo-page__section-title"},"点右半页翻过去",-1)),e("code",D,d(u.value)+" / "+d(b.value),1)]),t[7]||(t[7]=e("p",{class:"demo-card__text"}," 下面这本是 4 张内页(8 个面)加首尾封面:点右半页向前翻,点左边已经翻过的页可以翻回来; 底部按钮和 ← → 键也能翻。底图是用内联 SVG 生成的渐变,不依赖外网。 ",-1)),e("div",G,[n(a(i),{modelValue:u.value,"onUpdate:modelValue":t[0]||(t[0]=m=>u.value=m),pages:a(l),cover:c,"end-cover":p,width:620,height:400,duration:1e3,onChange:y},null,8,["modelValue","pages"])]),e("p",L,[t[2]||(t[2]=o(" 当前第 ",-1)),e("code",null,d(u.value),1),t[3]||(t[3]=o(" 张(共 ",-1)),e("code",null,d(b.value),1),t[4]||(t[4]=o(" 张)· 最近一次翻页: ",-1)),e("code",null,d(g.value),1)]),e("div",P,[e("div",z,[e("div",U,[n(a(i),{pages:a(l).slice(0,2),cover:c,"end-cover":p,width:360,height:240,duration:700,hint:!1,paper:"#f3ecdc"},null,8,["pages"])]),t[5]||(t[5]=e("p",null,[o("小尺寸 + 米色纸("),e("code",null,"width"),o(" / "),e("code",null,"height"),o(" / "),e("code",null,"paper"),o(")")],-1))]),e("div",j,[e("div",H,[n(a(i),{pages:a(l).slice(0,3),cover:c,"end-cover":p,width:360,height:240,duration:800,autoplay:!0,"autoplay-interval":1800,controls:!1,hint:!1},null,8,["pages"])]),t[6]||(t[6]=e("p",null,[o("自动播放("),e("code",null,"autoplay"),o(",翻到最后一页停下,无按钮)")],-1))])])]),n(x,{class:"book-preview"},{default:F(()=>[n(E,{name:"BookFlip",component:a(i),schema:a(I),presets:{pages:a(l),cover:c,endCover:p,width:560,height:380},"stage-style":h},null,8,["component","schema","presets"])]),_:1}),t[9]||(t[9]=B('<p class="demo-note" data-v-c89c9df1><span data-v-c89c9df1> 面板里 <code data-v-c89c9df1>width</code> / <code data-v-c89c9df1>height</code> 是书页尺寸,<code data-v-c89c9df1>duration</code> 是一次翻页的毫秒数, <code data-v-c89c9df1>paper</code> / <code data-v-c89c9df1>ink</code> 是纸色和正文颜色,<code data-v-c89c9df1>modelValue</code> 是当前翻到第几张 (和 <code data-v-c89c9df1>v-model</code> 同步),<code data-v-c89c9df1>controls</code> / <code data-v-c89c9df1>hint</code> 控制按钮和提示, <code data-v-c89c9df1>autoplay</code> + <code data-v-c89c9df1>autoplayInterval</code> 是自动翻页。内页数组用 <code data-v-c89c9df1>pages</code> 传入(每张有 front / back 两面)。 </span></p>',1))]))}}),K=w(M,[["__scopeId","data-v-c89c9df1"]]);export{K as default};
15
+ //# sourceMappingURL=BookFlipView-Dej1lwhJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BookFlipView-Dej1lwhJ.js","sources":["../../src/views/BookFlipView.vue"],"sourcesContent":["<template>\n <div class=\"demo-page\">\n <header>\n <p class=\"demo-page__eyebrow\">远程组件</p>\n <h1 class=\"demo-page__title\">comps-vue3 · BookFlip</h1>\n <p class=\"demo-page__lead\">\n 来自 video-code 的「书本翻页」:一叠对开页贴在书脊右侧,绕左边缘 <code>rotateY(-180deg)</code>\n 翻过去,正面转走背面转来,每面都有标题、正文和底图。原 demo 用 <code>z-index</code> +\n <code>visibility</code> 关键帧处理正反面切换;组件里改用 <code>backface-visibility: hidden</code>,\n 逻辑简单很多,另外补了上一页 / 下一页、页码、键盘左右键和自动播放。\n </p>\n </header>\n\n <section class=\"book-demo\">\n <div class=\"book-demo__head\">\n <h2 class=\"demo-page__section-title\">点右半页翻过去</h2>\n <code class=\"book-demo__tag\">{{ page }} / {{ leaves }}</code>\n </div>\n <p class=\"demo-card__text\">\n 下面这本是 4 张内页(8 个面)加首尾封面:点右半页向前翻,点左边已经翻过的页可以翻回来;\n 底部按钮和 ← → 键也能翻。底图是用内联 SVG 生成的渐变,不依赖外网。\n </p>\n\n <div class=\"book-demo__stage\">\n <BookFlip\n v-model=\"page\"\n :pages=\"pages\"\n :cover=\"cover\"\n :end-cover=\"endCover\"\n :width=\"620\"\n :height=\"400\"\n :duration=\"1000\"\n @change=\"onChange\"\n />\n </div>\n\n <p class=\"book-demo__hint\">\n 当前第 <code>{{ page }}</code> 张(共 <code>{{ leaves }}</code> 张)· 最近一次翻页:\n <code>{{ lastChange }}</code>\n </p>\n\n <div class=\"book-demo__row\">\n <div class=\"book-demo__cell\">\n <div class=\"book-demo__stage book-demo__stage--small\">\n <BookFlip\n :pages=\"pages.slice(0, 2)\"\n :cover=\"cover\"\n :end-cover=\"endCover\"\n :width=\"360\"\n :height=\"240\"\n :duration=\"700\"\n :hint=\"false\"\n paper=\"#f3ecdc\"\n />\n </div>\n <p>小尺寸 + 米色纸(<code>width</code> / <code>height</code> / <code>paper</code>)</p>\n </div>\n <div class=\"book-demo__cell\">\n <div class=\"book-demo__stage book-demo__stage--small\">\n <BookFlip\n :pages=\"pages.slice(0, 3)\"\n :cover=\"cover\"\n :end-cover=\"endCover\"\n :width=\"360\"\n :height=\"240\"\n :duration=\"800\"\n :autoplay=\"true\"\n :autoplay-interval=\"1800\"\n :controls=\"false\"\n :hint=\"false\"\n />\n </div>\n <p>自动播放(<code>autoplay</code>,翻到最后一页停下,无按钮)</p>\n </div>\n </div>\n </section>\n\n <DemoPreview class=\"book-preview\">\n <LevaPlayground\n name=\"BookFlip\"\n :component=\"BookFlip\"\n :schema=\"BookFlipSchema\"\n :presets=\"{ pages, cover, endCover, width: 560, height: 380 }\"\n :stage-style=\"stageStyle\"\n />\n </DemoPreview>\n\n <p class=\"demo-note\">\n <span>\n 面板里 <code>width</code> / <code>height</code> 是书页尺寸,<code>duration</code> 是一次翻页的毫秒数,\n <code>paper</code> / <code>ink</code> 是纸色和正文颜色,<code>modelValue</code> 是当前翻到第几张\n (和 <code>v-model</code> 同步),<code>controls</code> / <code>hint</code> 控制按钮和提示,\n <code>autoplay</code> + <code>autoplayInterval</code> 是自动翻页。内页数组用\n <code>pages</code> 传入(每张有 front / back 两面)。\n </span>\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport BookFlipSchema from '@melt-js/comps-vue3/src/components/BookFlip.propsSchema.json'\nimport DemoPreview from '../components/DemoPreview.vue'\nimport LevaPlayground from '../components/LevaPlayground.vue'\nimport { remoteComponent } from '../configs/remote'\n\nconst BookFlip = remoteComponent('BookFlip')\n\ntype Face = { title?: string; text?: string; image?: string }\ntype Page = { front: Face; back: Face }\n\nconst PALETTE = [\n ['#f97316', '#ec4899'],\n ['#22d3ee', '#3b82f6'],\n ['#a855f7', '#6366f1'],\n ['#34d399', '#14b8a6'],\n ['#fbbf24', '#f97316'],\n ['#f472b6', '#a855f7'],\n ['#38bdf8', '#818cf8'],\n ['#4ade80', '#22d3ee'],\n]\n\n/** 生成一张带编号的渐变底图,免得 demo 依赖外网图片 */\nfunction poster(index: number): string {\n const [from, to] = PALETTE[index % PALETTE.length]\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"300\" height=\"400\">\n <defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">\n <stop offset=\"0\" stop-color=\"${from}\"/><stop offset=\"1\" stop-color=\"${to}\"/>\n </linearGradient></defs>\n <rect width=\"300\" height=\"400\" fill=\"url(#g)\"/>\n <text x=\"150\" y=\"222\" font-family=\"sans-serif\" font-size=\"120\" font-weight=\"700\"\n fill=\"rgba(255,255,255,0.82)\" text-anchor=\"middle\">${index}</text>\n </svg>`\n return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`\n}\n\nconst TEXTS = [\n '第一页:组件从一整页 demo 里被拆出来,带上自己的 props、事件和样式,能独立站在任何一个 Vue 3 项目里。',\n '第二页:所有视觉常量都变成了 props —— 尺寸、颜色、时长、开关,在属性面板里拖一拖就能看到差别。',\n '第三页:交互逻辑重新组织过:拖动、点击、缩放这些手势用 Pointer Events 统一处理,鼠标和触摸是同一套。',\n '第四页:动画尽量交给 CSS 和 rAF,Vue 只负责状态;每帧只写必要的 DOM,避免把响应式拖进动画循环里。',\n]\n\nconst pages: Page[] = TEXTS.map((text, index) => ({\n front: {\n title: `第 ${index * 2 + 1} 面`,\n text,\n image: poster(index * 2 + 1),\n },\n back: {\n title: `第 ${index * 2 + 2} 面`,\n text: TEXTS[(index + 1) % TEXTS.length],\n image: poster(index * 2 + 2),\n },\n}))\n\nconst cover: Face = { title: '『组件提取笔记』', text: '', image: poster(0) }\nconst endCover: Face = { title: '', text: '', image: poster(7) }\n\nconst page = ref(0)\nconst leaves = computed(() => pages.length + 2)\nconst lastChange = ref('(还没翻过)')\n\nconst stageStyle = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '560px',\n padding: '24px',\n background: '#4f4e68',\n}\n\nfunction onChange(value: number) {\n lastChange.value = `翻到第 ${value} 张`\n}\n</script>\n\n<style scoped>\n.book-demo {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.book-demo__head {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: baseline;\n justify-content: space-between;\n}\n\n.book-demo__head .demo-page__section-title {\n margin: 0;\n}\n\n.book-demo__tag {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--demo-accent-soft);\n color: var(--demo-accent);\n font-size: 12px;\n font-weight: 600;\n}\n\n.book-demo__stage {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 36px 24px;\n border-radius: 16px;\n background: #4f4e68;\n}\n\n.book-demo__stage--small {\n padding: 20px;\n}\n\n.book-demo__hint {\n margin: 0;\n color: var(--demo-muted);\n font-size: 13px;\n}\n\n.book-demo__row {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n margin-top: 6px;\n}\n\n.book-demo__cell {\n flex: 1 1 320px;\n min-width: 280px;\n}\n\n.book-demo__cell p {\n margin: 8px 0 0;\n color: var(--demo-muted);\n font-size: 12px;\n}\n\n.book-preview.is-demo-fullscreen {\n padding: 16px;\n background: #4f4e68;\n}\n</style>\n"],"names":["BookFlip","remoteComponent","PALETTE","poster","index","from","to","svg","TEXTS","pages","text","cover","endCover","page","ref","leaves","computed","lastChange","stageStyle","onChange","value","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_cache","_hoisted_4","_toDisplayString","_hoisted_5","_createVNode","_unref","$event","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","DemoPreview","LevaPlayground","BookFlipSchema"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ .map-demo[data-v-2506479f]{display:flex;flex-direction:column;gap:10px}.map-demo__head[data-v-2506479f]{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:baseline;justify-content:space-between}.map-demo__head .demo-page__section-title[data-v-2506479f]{margin:0}.map-demo__tag[data-v-2506479f]{padding:2px 8px;border-radius:999px;background:var(--demo-accent-soft);color:var(--demo-accent);font-size:12px;font-weight:600}.map-demo__toolbar[data-v-2506479f]{display:flex;flex-wrap:wrap;gap:10px;align-items:center}.map-demo__count[data-v-2506479f]{color:var(--demo-muted);font-size:13px}.map-demo__stage[data-v-2506479f]{position:relative;height:460px;overflow:hidden;border-radius:16px;background:#eef6ff}.map-demo__row[data-v-2506479f]{display:flex;flex-wrap:wrap;gap:20px;margin-top:6px}.map-demo__cell[data-v-2506479f]{flex:1 1 320px;min-width:280px}.map-demo__cell[data-v-2506479f]>:first-child{height:240px;overflow:hidden;border-radius:14px}.map-demo__cell p[data-v-2506479f]{margin:8px 0 0;color:var(--demo-muted);font-size:12px}.map-preview.is-demo-fullscreen[data-v-2506479f]{padding:0;background:#eef6ff}
@@ -0,0 +1,8 @@
1
+ import{_ as h,a as C}from"./_plugin-vue_export-helper-BbhRsZUO.js";import{L as w}from"./LevaPlayground-ChwSFC5s.js";import{d as E,f as q,g as e,t as m,m as i,A as n,u as l,l as x,D as R,H as k,b as r,o as M}from"./index-CthyPCf3.js";import"./loadApp-BA8EV_l7.js";const V=[{name:"points",type:"() => RoutePoint[]",description:"路线站点(经纬度),默认给一组示例景点",default:`() => [
2
+ { name: '故宫', lon: 116.4, lat: 39.92 },
3
+ { name: '兵马俑', lon: 109.27, lat: 34.38 },
4
+ { name: '九寨沟', lon: 103.92, lat: 33.25 },
5
+ { name: '丽江古城', lon: 100.23, lat: 26.87 },
6
+ { name: '西湖', lon: 120.14, lat: 30.25 },
7
+ ]`,required:!1,version:"",isEvent:!1,bindable:!1},{name:"editable",type:"boolean",description:"是否允许点地图追加站点",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"duration",type:"number",description:"播放一遍的时长,单位秒",default:"8",required:!1,version:"",isEvent:!1,bindable:!0,value:8,control:{min:2,max:20,step:1}},{name:"color",type:"string",description:"路线主色",default:"'#c64024'",required:!1,version:"",isEvent:!1,bindable:!0,value:"#c64024"},{name:"followZoom",type:"number",description:"播放时的跟随缩放倍数",default:"2.2",required:!1,version:"",isEvent:!1,bindable:!0,value:2.2,control:{min:1,max:4,step:.1}},{name:"showCities",type:"boolean",description:"是否显示城市点",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"showLabels",type:"boolean",description:"是否显示站点名称",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"controls",type:"boolean",description:"是否显示缩放 / 播放控制条",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"tip",type:"boolean",description:"是否显示底部提示",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"tipText",type:"string",description:"提示文案",default:"'点地图加点 · 滚轮缩放 · 拖动平移'",required:!1,version:"",isEvent:!1,bindable:!0,value:"点地图加点 · 滚轮缩放 · 拖动平移"},{name:"playing",type:"boolean",description:"是否正在播放(可用 v-model:playing)",default:"false",required:!1,version:"",isEvent:!1,bindable:!0,value:!1}],$={class:"demo-page"},L={class:"map-demo"},N={class:"map-demo__head"},S={class:"map-demo__tag"},A={class:"map-demo__toolbar"},B=["disabled"],D={class:"map-demo__count"},P={class:"map-demo__stage"},T={class:"map-demo__row"},U={class:"map-demo__cell"},Z={class:"map-demo__cell"},F=E({__name:"ChinaRouteMapView",setup(O){const s=k("ChinaRouteMap"),c=[{name:"故宫",lon:116.4,lat:39.92},{name:"兵马俑",lon:109.27,lat:34.38},{name:"九寨沟",lon:103.92,lat:33.25},{name:"丽江古城",lon:100.23,lat:26.87},{name:"西湖",lon:120.14,lat:30.25}],v=[{name:"哈尔滨",lon:126.53,lat:45.8},{name:"广州",lon:113.26,lat:23.13},{name:"乌鲁木齐",lon:87.62,lat:43.82}],f=r(null),o=r([...c]),u=r(!1),p=r("(还没加过)"),b={display:"block",height:"520px",padding:"0",background:"#eef6ff"};function _(){var t;(t=f.value)==null||t.play()}function y(){o.value=c.map(t=>({...t})),p.value="(已恢复示例路线)"}function g(t){p.value=`${t.name}(${t.lon.toFixed(1)}, ${t.lat.toFixed(1)})`}return(t,a)=>(M(),q("div",$,[a[7]||(a[7]=e("header",null,[e("p",{class:"demo-page__eyebrow"},"远程组件"),e("h1",{class:"demo-page__title"},"comps-vue3 · ChinaRouteMap"),e("p",{class:"demo-page__lead"}," 来自 video-code 的「中国地图轨迹路线图」:canvas 上画中国轮廓和省份线,按经纬度铺路线和站点, 点地图可以加站(自动找最近的景点 / 城市命名),播放时镜头放大跟着箭头走完全程。原 demo 是一整套 路线编辑器(右侧列表拖动排序、录像导出、换箭头贴图、时长滑杆、缩放控件),组件里只保留可复用的核心: 地图 + 路线 + 播放 / 缩放 / 加点。 ")],-1)),e("section",L,[e("div",N,[a[3]||(a[3]=e("h2",{class:"demo-page__section-title"},"点地图加点 · 播放镜头跟随",-1)),e("code",S,m(o.value.length)+" 站",1)]),a[6]||(a[6]=e("p",{class:"demo-card__text"}," 默认是原 demo 的五个景点(故宫 → 兵马俑 → 九寨沟 → 丽江古城 → 西湖)。在地图上点一下就能追加一站, 滚轮缩放、拖动平移;点左下角「播放路线」看镜头跟着箭头走。 ",-1)),e("div",A,[e("button",{class:"demo-btn",type:"button",onClick:_},"播放路线"),e("button",{class:"demo-btn demo-btn--ghost",type:"button",onClick:y},"恢复示例路线"),e("button",{class:"demo-btn demo-btn--ghost",type:"button",disabled:!o.value.length,onClick:a[0]||(a[0]=d=>o.value=[])}," 清空站点 ",8,B),e("span",D,[i(m(u.value?"播放中…":"已停止")+" · 最近新增:",1),e("code",null,m(p.value),1)])]),e("div",P,[n(l(s),{ref_key:"mapRef",ref:f,points:o.value,"onUpdate:points":a[1]||(a[1]=d=>o.value=d),playing:u.value,"onUpdate:playing":a[2]||(a[2]=d=>u.value=d),duration:8,onAdd:g},null,8,["points","playing"])]),e("div",T,[e("div",U,[n(l(s),{points:o.value.slice(0,3),editable:!1,controls:!1,tip:!1,"show-labels":!1,duration:5},null,8,["points"]),a[4]||(a[4]=e("p",null,"只读版:3 站、无控件、不显示名称(适合当缩略图)",-1))]),e("div",Z,[n(l(s),{points:v,editable:!1,controls:!1,tip:!1,color:"#0f766e",duration:6}),a[5]||(a[5]=e("p",null,[i("换主色 + 另一条路线("),e("code",null,"color"),i(" / "),e("code",null,"points"),i(")")],-1))])])]),n(h,{class:"map-preview"},{default:x(()=>[n(w,{name:"ChinaRouteMap",component:l(s),schema:l(V),presets:{duration:8,followZoom:2.2},"stage-style":b},null,8,["component","schema"])]),_:1}),a[8]||(a[8]=R('<p class="demo-note" data-v-2506479f><span data-v-2506479f> 面板里 <code data-v-2506479f>duration</code> 是播放一遍的秒数,<code data-v-2506479f>followZoom</code> 是播放时的跟随放大倍数, <code data-v-2506479f>editable</code> 控制能否点地图加点,<code data-v-2506479f>showCities</code> / <code data-v-2506479f>showLabels</code> / <code data-v-2506479f>controls</code> / <code data-v-2506479f>tip</code> 是几处显示开关,<code data-v-2506479f>color</code> 是路线主色。 <code data-v-2506479f>points</code> 是数组(面板不生成控件),要自定义路线直接传 <code data-v-2506479f>:points=&quot;[{ name: &#39;故宫&#39;, lon: 116.4, lat: 39.92 }]&quot;</code>。 </span></p>',1))]))}}),G=C(F,[["__scopeId","data-v-2506479f"]]);export{G as default};
8
+ //# sourceMappingURL=ChinaRouteMapView-CgjpTWF3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChinaRouteMapView-CgjpTWF3.js","sources":["../../src/views/ChinaRouteMapView.vue"],"sourcesContent":["<template>\n <div class=\"demo-page\">\n <header>\n <p class=\"demo-page__eyebrow\">远程组件</p>\n <h1 class=\"demo-page__title\">comps-vue3 · ChinaRouteMap</h1>\n <p class=\"demo-page__lead\">\n 来自 video-code 的「中国地图轨迹路线图」:canvas 上画中国轮廓和省份线,按经纬度铺路线和站点,\n 点地图可以加站(自动找最近的景点 / 城市命名),播放时镜头放大跟着箭头走完全程。原 demo 是一整套\n 路线编辑器(右侧列表拖动排序、录像导出、换箭头贴图、时长滑杆、缩放控件),组件里只保留可复用的核心:\n 地图 + 路线 + 播放 / 缩放 / 加点。\n </p>\n </header>\n\n <section class=\"map-demo\">\n <div class=\"map-demo__head\">\n <h2 class=\"demo-page__section-title\">点地图加点 · 播放镜头跟随</h2>\n <code class=\"map-demo__tag\">{{ points.length }} 站</code>\n </div>\n <p class=\"demo-card__text\">\n 默认是原 demo 的五个景点(故宫 → 兵马俑 → 九寨沟 → 丽江古城 → 西湖)。在地图上点一下就能追加一站,\n 滚轮缩放、拖动平移;点左下角「播放路线」看镜头跟着箭头走。\n </p>\n\n <div class=\"map-demo__toolbar\">\n <button class=\"demo-btn\" type=\"button\" @click=\"play\">播放路线</button>\n <button class=\"demo-btn demo-btn--ghost\" type=\"button\" @click=\"resetPoints\">恢复示例路线</button>\n <button class=\"demo-btn demo-btn--ghost\" type=\"button\" :disabled=\"!points.length\" @click=\"points = []\">\n 清空站点\n </button>\n <span class=\"map-demo__count\">\n {{ playing ? '播放中…' : '已停止' }} · 最近新增:<code>{{ lastAdded }}</code>\n </span>\n </div>\n\n <div class=\"map-demo__stage\">\n <ChinaRouteMap\n ref=\"mapRef\"\n v-model:points=\"points\"\n v-model:playing=\"playing\"\n :duration=\"8\"\n @add=\"onAdd\"\n />\n </div>\n\n <div class=\"map-demo__row\">\n <div class=\"map-demo__cell\">\n <ChinaRouteMap\n :points=\"points.slice(0, 3)\"\n :editable=\"false\"\n :controls=\"false\"\n :tip=\"false\"\n :show-labels=\"false\"\n :duration=\"5\"\n />\n <p>只读版:3 站、无控件、不显示名称(适合当缩略图)</p>\n </div>\n <div class=\"map-demo__cell\">\n <ChinaRouteMap\n :points=\"triangle\"\n :editable=\"false\"\n :controls=\"false\"\n :tip=\"false\"\n color=\"#0f766e\"\n :duration=\"6\"\n />\n <p>换主色 + 另一条路线(<code>color</code> / <code>points</code>)</p>\n </div>\n </div>\n </section>\n\n <DemoPreview class=\"map-preview\">\n <LevaPlayground\n name=\"ChinaRouteMap\"\n :component=\"ChinaRouteMap\"\n :schema=\"ChinaRouteMapSchema\"\n :presets=\"{ duration: 8, followZoom: 2.2 }\"\n :stage-style=\"stageStyle\"\n />\n </DemoPreview>\n\n <p class=\"demo-note\">\n <span>\n 面板里 <code>duration</code> 是播放一遍的秒数,<code>followZoom</code> 是播放时的跟随放大倍数,\n <code>editable</code> 控制能否点地图加点,<code>showCities</code> / <code>showLabels</code> /\n <code>controls</code> / <code>tip</code> 是几处显示开关,<code>color</code> 是路线主色。\n <code>points</code> 是数组(面板不生成控件),要自定义路线直接传\n <code>:points=\"[{ name: '故宫', lon: 116.4, lat: 39.92 }]\"</code>。\n </span>\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport ChinaRouteMapSchema from '@melt-js/comps-vue3/src/components/ChinaRouteMap.propsSchema.json'\nimport DemoPreview from '../components/DemoPreview.vue'\nimport LevaPlayground from '../components/LevaPlayground.vue'\nimport { remoteComponent } from '../configs/remote'\n\nconst ChinaRouteMap = remoteComponent('ChinaRouteMap')\n\ntype RoutePoint = { name?: string; lon: number; lat: number }\n\nconst DEMO_ROUTE: RoutePoint[] = [\n { name: '故宫', lon: 116.4, lat: 39.92 },\n { name: '兵马俑', lon: 109.27, lat: 34.38 },\n { name: '九寨沟', lon: 103.92, lat: 33.25 },\n { name: '丽江古城', lon: 100.23, lat: 26.87 },\n { name: '西湖', lon: 120.14, lat: 30.25 },\n]\n\nconst triangle: RoutePoint[] = [\n { name: '哈尔滨', lon: 126.53, lat: 45.8 },\n { name: '广州', lon: 113.26, lat: 23.13 },\n { name: '乌鲁木齐', lon: 87.62, lat: 43.82 },\n]\n\nconst mapRef = ref<{ play: () => void; stop: () => void; reset: () => void } | null>(null)\nconst points = ref<RoutePoint[]>([...DEMO_ROUTE])\nconst playing = ref(false)\nconst lastAdded = ref('(还没加过)')\n\nconst stageStyle = {\n display: 'block',\n height: '520px',\n padding: '0',\n background: '#eef6ff',\n}\n\nfunction play() {\n mapRef.value?.play()\n}\n\nfunction resetPoints() {\n points.value = DEMO_ROUTE.map((point) => ({ ...point }))\n lastAdded.value = '(已恢复示例路线)'\n}\n\nfunction onAdd(point: RoutePoint) {\n lastAdded.value = `${point.name}(${point.lon.toFixed(1)}, ${point.lat.toFixed(1)})`\n}\n</script>\n\n<style scoped>\n.map-demo {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.map-demo__head {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: baseline;\n justify-content: space-between;\n}\n\n.map-demo__head .demo-page__section-title {\n margin: 0;\n}\n\n.map-demo__tag {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--demo-accent-soft);\n color: var(--demo-accent);\n font-size: 12px;\n font-weight: 600;\n}\n\n.map-demo__toolbar {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n align-items: center;\n}\n\n.map-demo__count {\n color: var(--demo-muted);\n font-size: 13px;\n}\n\n.map-demo__stage {\n position: relative;\n height: 460px;\n overflow: hidden;\n border-radius: 16px;\n background: #eef6ff;\n}\n\n.map-demo__row {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n margin-top: 6px;\n}\n\n.map-demo__cell {\n flex: 1 1 320px;\n min-width: 280px;\n}\n\n.map-demo__cell > :first-child {\n height: 240px;\n overflow: hidden;\n border-radius: 14px;\n}\n\n.map-demo__cell p {\n margin: 8px 0 0;\n color: var(--demo-muted);\n font-size: 12px;\n}\n\n.map-preview.is-demo-fullscreen {\n padding: 0;\n background: #eef6ff;\n}\n</style>\n"],"names":["ChinaRouteMap","remoteComponent","DEMO_ROUTE","triangle","mapRef","ref","points","playing","lastAdded","stageStyle","play","_a","resetPoints","point","onAdd","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_cache","_hoisted_4","_toDisplayString","_hoisted_5","_hoisted_6","_hoisted_7","_createTextVNode","_hoisted_8","_createVNode","_unref","$event","_hoisted_9","_hoisted_10","_hoisted_11","DemoPreview","LevaPlayground","ChinaRouteMapSchema"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{_ as h,a as g}from"./_plugin-vue_export-helper-BbhRsZUO.js";import{L as y}from"./LevaPlayground-ChwSFC5s.js";import{d as x,f as c,D as u,g as e,A as a,u as o,m as i,F as w,j as k,l as C,t as p,E,b as q,o as b,G as M}from"./index-CthyPCf3.js";import"./loadApp-BA8EV_l7.js";const I=[{name:"items",type:"CircularMenuItem[]",description:"菜单项,label 是文案,href 给了就走链接,active 是高亮态",default:"",required:!0,version:"",isEvent:!1,bindable:!1},{name:"variant",type:"'wheel' | 'fan'",description:"形态:wheel 是半圆扇环,fan 是整圆扇环",default:'"wheel"',required:!1,version:"",isEvent:!1,bindable:!0,value:"wheel",control:{options:{wheel:"wheel",fan:"fan"}}},{name:"position",type:"'relative' | 'absolute' | 'sticky' | 'fixed'",description:"定位方式:relative 随文档流滚动,sticky 吸附,fixed 贴视口,absolute 贴定位祖先",default:'"relative"',required:!1,version:"",isEvent:!1,bindable:!0,value:"relative",control:{options:{relative:"relative",absolute:"absolute",sticky:"sticky",fixed:"fixed"}}},{name:"anchor",type:"'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'",description:"锚点:非 relative 时按钮中心贴在哪条边,left/right 会在另一轴上居中",default:'"bottom"',required:!1,version:"",isEvent:!1,bindable:!0,value:"bottom",control:{options:{center:"center",top:"top",bottom:"bottom",left:"left",right:"right","top-left":"top-left","top-right":"top-right","bottom-left":"bottom-left","bottom-right":"bottom-right"}}},{name:"offset",type:"number",description:"距锚点边的距离,单位 px",default:"0",required:!1,version:"",isEvent:!1,bindable:!0,value:0},{name:"direction",type:"'up' | 'down' | 'auto'",description:"展开方向:up 向上展开,down 向下展开,auto 按锚点推断(贴顶时向下)",default:'"auto"',required:!1,version:"",isEvent:!1,bindable:!0,value:"auto",control:{options:{up:"up",down:"down",auto:"auto"}}},{name:"overlay",type:"boolean",description:"是否显示半透明遮罩(一般配 fixed/absolute 使用)",default:"false",required:!1,version:"",isEvent:!1,bindable:!0,value:!1},{name:"openText",type:"string",description:"展开时的按钮文案,不传则按形态用默认符号",default:"",required:!1,version:"",isEvent:!1,bindable:!0,value:""},{name:"closeText",type:"string",description:"收起时的按钮文案,不传则按形态用默认符号",default:"",required:!1,version:"",isEvent:!1,bindable:!0,value:""}],S={class:"demo-page"},V={class:"scroll-demo"},L={class:"scroll-demo__stage"},N={class:"scroll-demo"},j={class:"scroll-demo__box"},B={class:"demo-note"},T=x({__name:"CircularMenuView",setup(A){const s=E({loader:async()=>(await M()).CircularMenu,timeout:1e4}),m=[{label:"图"},{label:"乐"},{label:"家"},{label:"视"},{label:"信"}],f=[{label:"关于"},{label:"教程"},{label:"文章"},{label:"片段"},{label:"插件"},{label:"联系"},{label:"关注"}],v=[{label:"目录"},{label:"收藏"},{label:"分享"},{label:"置顶"}],_={position:"relative",display:"flex",alignItems:"center",justifyContent:"center",height:"520px",background:"#2b3440"},n=q("—");function l(d){n.value=d.label}return(d,t)=>(b(),c("div",S,[t[6]||(t[6]=u('<header data-v-8a0b0601><p class="demo-page__eyebrow" data-v-8a0b0601>远程组件</p><h1 class="demo-page__title" data-v-8a0b0601>comps-vue3 · CircularMenu</h1><p class="demo-page__lead" data-v-8a0b0601> 定位方式从形态里拆出来了:组件 root 就是「按钮中心」这个锚点盒子,<code data-v-8a0b0601>position</code> / <code data-v-8a0b0601>anchor</code> / <code data-v-8a0b0601>offset</code> 决定它贴在哪里。默认 <code data-v-8a0b0601>position=&quot;relative&quot;</code>, 菜单就留在文档流里,跟着页面一起滚动;要 <code data-v-8a0b0601>sticky</code> / <code data-v-8a0b0601>fixed</code> / <code data-v-8a0b0601>absolute</code> 只是换一个属性值。下面两块示例分别演示随页面滚动和容器内吸顶, 再往下是属性面板,可以把 <code data-v-8a0b0601>position</code> 切到 <code data-v-8a0b0601>fixed</code> / <code data-v-8a0b0601>absolute</code> 直接看效果。 </p></header>',1)),e("section",V,[t[0]||(t[0]=e("div",{class:"scroll-demo__head"},[e("h2",{class:"demo-page__section-title"},"随页面滚动"),e("code",{class:"scroll-demo__tag"},'position="relative"')],-1)),t[1]||(t[1]=e("p",{class:"demo-card__text"},"滚动页面,菜单会跟着这一块一起移动。",-1)),e("div",L,[a(o(s),{variant:"wheel",items:m,onSelect:l})])]),e("section",N,[t[2]||(t[2]=e("div",{class:"scroll-demo__head"},[e("h2",{class:"demo-page__section-title"},"容器内吸顶"),e("code",{class:"scroll-demo__tag"},'position="sticky" anchor="top"')],-1)),t[3]||(t[3]=e("p",{class:"demo-card__text"},[i(" 在下面这个滚动容器里往下滚,按钮会吸在容器顶部;"),e("code",null,'direction="auto"'),i(" 让贴顶的锚点自动向下展开, 再点按钮或按 Esc 收起。 ")],-1)),e("div",j,[a(o(s),{variant:"wheel",position:"sticky",anchor:"top",offset:12,items:v,onSelect:l}),(b(),c(w,null,k(12,r=>e("p",{key:r,class:"scroll-demo__para"}," 第 "+p(r)+" 段:这是一段用来撑开滚动条的内容。滚到这一段时,按钮会停在容器顶部, 点开就是向下的扇形菜单。 ",1)),64))])]),a(h,{class:"menu-preview"},{default:C(()=>[a(y,{name:"CircularMenu",component:o(s),schema:o(I),presets:{items:f},listeners:{select:l},"stage-style":_},null,8,["component","schema","presets","listeners"])]),_:1}),e("p",B,[e("span",null,[t[4]||(t[4]=i(" 最近点击:",-1)),e("strong",null,p(n.value),1),t[5]||(t[5]=u("。上面的面板来自组件包源码里的 <code data-v-8a0b0601>propsSchema.json</code>:<code data-v-8a0b0601>position</code> / <code data-v-8a0b0601>anchor</code> / <code data-v-8a0b0601>offset</code> / <code data-v-8a0b0601>direction</code> 都生成了控件,改完马上能看到菜单换个贴法;<code data-v-8a0b0601>items</code> 这类复杂 props 由 <code data-v-8a0b0601>presets</code> 固定传入。 ",15))])])]))}}),R=g(T,[["__scopeId","data-v-8a0b0601"]]);export{R as default};
2
+ //# sourceMappingURL=CircularMenuView-CHXEBHII.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CircularMenuView-CHXEBHII.js","sources":["../../src/views/CircularMenuView.vue"],"sourcesContent":["<template>\n <div class=\"demo-page\">\n <header>\n <p class=\"demo-page__eyebrow\">远程组件</p>\n <h1 class=\"demo-page__title\">comps-vue3 · CircularMenu</h1>\n <p class=\"demo-page__lead\">\n 定位方式从形态里拆出来了:组件 root 就是「按钮中心」这个锚点盒子,<code>position</code> /\n <code>anchor</code> / <code>offset</code> 决定它贴在哪里。默认 <code>position=\"relative\"</code>,\n 菜单就留在文档流里,跟着页面一起滚动;要 <code>sticky</code> / <code>fixed</code> /\n <code>absolute</code> 只是换一个属性值。下面两块示例分别演示随页面滚动和容器内吸顶,\n 再往下是属性面板,可以把 <code>position</code> 切到 <code>fixed</code> / <code>absolute</code> 直接看效果。\n </p>\n </header>\n\n <section class=\"scroll-demo\">\n <div class=\"scroll-demo__head\">\n <h2 class=\"demo-page__section-title\">随页面滚动</h2>\n <code class=\"scroll-demo__tag\">position=\"relative\"</code>\n </div>\n <p class=\"demo-card__text\">滚动页面,菜单会跟着这一块一起移动。</p>\n <div class=\"scroll-demo__stage\">\n <CircularMenu variant=\"wheel\" :items=\"wheelItems\" @select=\"onSelect\" />\n </div>\n </section>\n\n <section class=\"scroll-demo\">\n <div class=\"scroll-demo__head\">\n <h2 class=\"demo-page__section-title\">容器内吸顶</h2>\n <code class=\"scroll-demo__tag\">position=\"sticky\" anchor=\"top\"</code>\n </div>\n <p class=\"demo-card__text\">\n 在下面这个滚动容器里往下滚,按钮会吸在容器顶部;<code>direction=\"auto\"</code> 让贴顶的锚点自动向下展开,\n 再点按钮或按 Esc 收起。\n </p>\n <div class=\"scroll-demo__box\">\n <CircularMenu\n variant=\"wheel\"\n position=\"sticky\"\n anchor=\"top\"\n :offset=\"12\"\n :items=\"sheetItems\"\n @select=\"onSelect\"\n />\n <p v-for=\"n in 12\" :key=\"n\" class=\"scroll-demo__para\">\n 第 {{ n }} 段:这是一段用来撑开滚动条的内容。滚到这一段时,按钮会停在容器顶部,\n 点开就是向下的扇形菜单。\n </p>\n </div>\n </section>\n\n <DemoPreview class=\"menu-preview\">\n <LevaPlayground\n name=\"CircularMenu\"\n :component=\"CircularMenu\"\n :schema=\"CircularMenuSchema\"\n :presets=\"{ items: menuItems }\"\n :listeners=\"{ select: onSelect }\"\n :stage-style=\"stageStyle\"\n />\n </DemoPreview>\n\n <p class=\"demo-note\">\n <span>\n 最近点击:<strong>{{ picked }}</strong>。上面的面板来自组件包源码里的\n <code>propsSchema.json</code>:<code>position</code> / <code>anchor</code> / <code>offset</code> /\n <code>direction</code> 都生成了控件,改完马上能看到菜单换个贴法;<code>items</code> 这类复杂 props\n 由 <code>presets</code> 固定传入。\n </span>\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { defineAsyncComponent, ref } from 'vue'\nimport CircularMenuSchema from '@melt-js/comps-vue3/src/components/CircularMenu.propsSchema.json'\nimport DemoPreview from '../components/DemoPreview.vue'\nimport LevaPlayground from '../components/LevaPlayground.vue'\nimport { loadRemoteLib } from '../configs/remote'\n\n/**\n * 与 RemoteView 相同的远程加载方式:入口处已 preFetchLib,这里只取组件。\n */\nconst CircularMenu = defineAsyncComponent({\n loader: async () => (await loadRemoteLib()).CircularMenu,\n timeout: 10000,\n})\n\n// 原 demo1 的 5 个图标位(图片/耳机/首页/视频/邮件),无图标字体,用单字代替\nconst wheelItems = [{ label: '图' }, { label: '乐' }, { label: '家' }, { label: '视' }, { label: '信' }]\n\n// 原 demo2 的 7 个入口,由面板的 presets 固定传入(数组暂不生成控件)\nconst menuItems = [\n { label: '关于' },\n { label: '教程' },\n { label: '文章' },\n { label: '片段' },\n { label: '插件' },\n { label: '联系' },\n { label: '关注' },\n]\n\n// 吸顶那份换几个短标签,和上面两份区分开\nconst sheetItems = [{ label: '目录' }, { label: '收藏' }, { label: '分享' }, { label: '置顶' }]\n\n/**\n * 属性面板的预览舞台:菜单默认是 relative,舞台用 flex 居中摆好;\n * 面板切到 absolute 时,stage 的 position: relative 就是它的定位祖先;\n * 切到 fixed 时按视口定位,不会再被 overflow 裁掉。\n * 背景用深色,白色按钮和红/绿扇环在上面看得清。\n */\nconst stageStyle = {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '520px',\n background: '#2b3440',\n}\n\nconst picked = ref('—')\n\nfunction onSelect(item: { label: string }) {\n picked.value = item.label\n}\n</script>\n\n<style scoped>\n/* 文档流示例:按钮压在舞台底部中间,扇形朝上展开,滚动页面时整块跟着走 */\n.scroll-demo {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.scroll-demo__head {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: baseline;\n justify-content: space-between;\n}\n\n/* section-title 自带下边距,这里在头部行里去掉 */\n.scroll-demo__head .demo-page__section-title {\n margin: 0;\n}\n\n.scroll-demo__tag {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--demo-accent-soft);\n color: var(--demo-accent);\n font-size: 12px;\n font-weight: 600;\n}\n\n.scroll-demo__stage {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: flex-end;\n min-height: 300px;\n padding: 14px;\n border: 1px dashed var(--demo-border);\n border-radius: 14px;\n background: var(--demo-bg);\n}\n\n/* sticky 示例的滚动容器:菜单是第一个子元素,吸在容器顶部 */\n.scroll-demo__box {\n height: 260px;\n padding: 12px;\n overflow: auto;\n border: 1px dashed var(--demo-border);\n border-radius: 14px;\n background: var(--demo-bg);\n overscroll-behavior: contain;\n}\n\n/* sticky 只负责纵向吸附,横向居中交给宿主(scoped 样式能命中子组件根节点) */\n.scroll-demo__box > .cm {\n margin-inline: auto;\n}\n\n.scroll-demo__para {\n margin: 10px 0 0;\n color: var(--demo-muted);\n font-size: 13px;\n line-height: 1.8;\n}\n\n.menu-preview.is-demo-fullscreen {\n padding: 16px;\n background: var(--demo-bg);\n}\n</style>\n"],"names":["CircularMenu","defineAsyncComponent","loadRemoteLib","wheelItems","menuItems","sheetItems","stageStyle","picked","ref","onSelect","item","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_cache","_hoisted_3","_createVNode","_unref","_hoisted_4","_hoisted_5","_Fragment","_renderList","n","_toDisplayString","DemoPreview","LevaPlayground","CircularMenuSchema","_hoisted_6"],"mappings":"40EAkFA,MAAMA,EAAeC,EAAqB,CACxC,OAAQ,UAAa,MAAMC,EAAA,GAAiB,aAC5C,QAAS,GAAA,CACV,EAGKC,EAAa,CAAC,CAAE,MAAO,KAAO,CAAE,MAAO,GAAA,EAAO,CAAE,MAAO,GAAA,EAAO,CAAE,MAAO,KAAO,CAAE,MAAO,IAAK,EAG5FC,EAAY,CAChB,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,EACT,CAAE,MAAO,IAAA,CAAK,EAIVC,EAAa,CAAC,CAAE,MAAO,IAAA,EAAQ,CAAE,MAAO,IAAA,EAAQ,CAAE,MAAO,IAAA,EAAQ,CAAE,MAAO,KAAM,EAQhFC,EAAa,CACjB,SAAU,WACV,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,OAAQ,QACR,WAAY,SAAA,EAGRC,EAASC,EAAI,GAAG,EAEtB,SAASC,EAASC,EAAyB,CACzCH,EAAO,MAAQG,EAAK,KACtB,eA1HEC,EAAA,EAAAC,EAoEM,MApENC,EAoEM,mvBAvDJC,EASU,UATVC,EASU,aARRD,EAGM,MAAA,CAHD,MAAM,qBAAmB,CAC5BA,EAA+C,KAAA,CAA3C,MAAM,0BAAA,EAA2B,OAAK,EAC1CA,EAAyD,OAAA,CAAnD,MAAM,kBAAA,EAAmB,qBAAmB,CAAA,OAEpDE,EAAA,CAAA,IAAAA,EAAA,CAAA,EAAAF,EAAiD,IAAA,CAA9C,MAAM,iBAAA,EAAkB,qBAAkB,EAAA,GAC7CA,EAEM,MAFNG,EAEM,CADJC,EAAuEC,EAAAnB,CAAA,EAAA,CAAzD,QAAQ,QAAS,MAAOG,EAAa,SAAAM,CAAA,OAIvDK,EAuBU,UAvBVM,EAuBU,aAtBRN,EAGM,MAAA,CAHD,MAAM,qBAAmB,CAC5BA,EAA+C,KAAA,CAA3C,MAAM,0BAAA,EAA2B,OAAK,EAC1CA,EAAoE,OAAA,CAA9D,MAAM,kBAAA,EAAmB,gCAA8B,CAAA,mBAE/DA,EAGI,IAAA,CAHD,MAAM,mBAAiB,GAAC,2BACD,EAAAA,EAA6B,YAAvB,kBAAgB,IAAO,gCAEvD,CAAA,OACAA,EAaM,MAbNO,EAaM,CAZJH,EAOEC,EAAAnB,CAAA,EAAA,CANA,QAAQ,QACR,SAAS,SACT,OAAO,MACN,OAAQ,GACR,MAAOK,EACP,SAAAI,CAAA,QAEHG,EAGIU,EAAA,KAAAC,EAHW,GAALC,GAAVV,EAGI,IAAA,CAHgB,IAAKU,EAAG,MAAM,mBAAA,EAAoB,MAClDC,EAAGD,CAAC,EAAG,oDAEX,CAAA,YAIJN,EAScQ,EAAA,CATD,MAAM,gBAAc,WAC/B,IAOE,CAPFR,EAOES,EAAA,CANA,KAAK,eACJ,UAAWR,EAAAnB,CAAA,EACX,OAAQmB,EAAAS,CAAA,EACR,eAAkBxB,CAAA,EAClB,kBAAqBK,CAAA,EACrB,cAAaH,CAAA,+DAIlBQ,EAOI,IAPJe,EAOI,CANFf,EAKO,OAAA,KAAA,eALD,SACC,EAAA,GAAAA,EAA6B,gBAAlBP,EAAA,KAAM,EAAA,CAAA"}
@@ -0,0 +1 @@
1
+ .scroll-demo[data-v-8a0b0601]{display:flex;flex-direction:column;gap:10px}.scroll-demo__head[data-v-8a0b0601]{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:baseline;justify-content:space-between}.scroll-demo__head .demo-page__section-title[data-v-8a0b0601]{margin:0}.scroll-demo__tag[data-v-8a0b0601]{padding:2px 8px;border-radius:999px;background:var(--demo-accent-soft);color:var(--demo-accent);font-size:12px;font-weight:600}.scroll-demo__stage[data-v-8a0b0601]{display:flex;flex-direction:column;align-items:center;justify-content:flex-end;min-height:300px;padding:14px;border:1px dashed var(--demo-border);border-radius:14px;background:var(--demo-bg)}.scroll-demo__box[data-v-8a0b0601]{height:260px;padding:12px;overflow:auto;border:1px dashed var(--demo-border);border-radius:14px;background:var(--demo-bg);overscroll-behavior:contain}.scroll-demo__box>.cm[data-v-8a0b0601]{margin-inline:auto}.scroll-demo__para[data-v-8a0b0601]{margin:10px 0 0;color:var(--demo-muted);font-size:13px;line-height:1.8}.menu-preview.is-demo-fullscreen[data-v-8a0b0601]{padding:16px;background:var(--demo-bg)}
@@ -0,0 +1 @@
1
+ .cmp-demo[data-v-42a55184]{display:flex;flex-direction:column;gap:10px}.cmp-demo__head[data-v-42a55184]{display:flex;flex-wrap:wrap;gap:8px 12px;align-items:baseline;justify-content:space-between}.cmp-demo__head .demo-page__section-title[data-v-42a55184]{margin:0}.cmp-demo__tag[data-v-42a55184]{padding:2px 8px;border-radius:999px;background:var(--demo-accent-soft);color:var(--demo-accent);font-size:12px;font-weight:600;font-variant-numeric:tabular-nums}.cmp-demo__stage[data-v-42a55184]{overflow:hidden;border-radius:16px;background:#f4efe7;padding:10px}.cmp-demo__toolbar[data-v-42a55184]{display:flex;flex-wrap:wrap;gap:10px;align-items:center}.cmp-demo__file[data-v-42a55184]{display:inline-flex;align-items:center;gap:6px;padding:7px 12px;border:1px dashed rgba(37,31,26,.28);border-radius:8px;background:#fffaf1;color:#3f342c;font-size:13px;font-weight:600;cursor:pointer}.cmp-demo__file input[data-v-42a55184]{display:none}.cmp-demo__count[data-v-42a55184]{color:var(--demo-muted);font-size:13px}.cmp-demo__row[data-v-42a55184]{display:flex;flex-wrap:wrap;gap:20px;margin-top:6px}.cmp-demo__cell[data-v-42a55184]{flex:1 1 320px;min-width:280px}.cmp-demo__cell p[data-v-42a55184]{margin:8px 0 0;color:var(--demo-muted);font-size:12px}.cmp-preview.is-demo-fullscreen[data-v-42a55184]{padding:16px;background:#f4efe7}
@@ -0,0 +1,21 @@
1
+ import{_ as $,a as E}from"./_plugin-vue_export-helper-BbhRsZUO.js";import{L as k}from"./LevaPlayground-ChwSFC5s.js";import{d as q,b as d,f as B,g as t,m as a,t as g,A as p,u as f,l as R,D as A,H as z,o as F}from"./index-CthyPCf3.js";import"./loadApp-BA8EV_l7.js";const G=[{name:"before",type:"string",description:"对比左侧(Before)图片地址",default:'""',required:!1,version:"",isEvent:!1,bindable:!0,value:""},{name:"after",type:"string",description:"对比右侧(After)图片地址",default:'""',required:!1,version:"",isEvent:!1,bindable:!0,value:""},{name:"position",type:"number",description:"中线位置 0 ~ 100(可用 v-model:position 双向绑定)",default:"50",required:!1,version:"",isEvent:!1,bindable:!0,value:50,control:{min:0,max:100,step:1}},{name:"ratio",type:"number",description:"卡片宽高比,默认 16:9(约 1.78)",default:"1.78",required:!1,version:"",isEvent:!1,bindable:!0,value:1.78,control:{min:.6,max:2.4,step:.1}},{name:"beforeLabel",type:"string",description:"左侧标签文字,传空字符串则不显示",default:'"Before"',required:!1,version:"",isEvent:!1,bindable:!0,value:"Before"},{name:"afterLabel",type:"string",description:"右侧标签文字",default:'"After"',required:!1,version:"",isEvent:!1,bindable:!0,value:"After"},{name:"showLabels",type:"boolean",description:"是否显示左上 / 右上两个标签",default:"true",required:!1,version:"",isEvent:!1,bindable:!0,value:!0},{name:"handleSize",type:"number",description:"圆形把手直径,单位 px",default:"62",required:!1,version:"",isEvent:!1,bindable:!0,value:62,control:{min:32,max:96,step:2}},{name:"accent",type:"string",description:"中线 / 把手的主色",default:'"#f26d3d"',required:!1,version:"",isEvent:!1,bindable:!0,value:"#f26d3d"},{name:"autoSweep",type:"boolean",description:"是否开启自动巡航(中线来回扫)",default:"false",required:!1,version:"",isEvent:!1,bindable:!0,value:!1},{name:"sweepSpeed",type:"number",description:"自动巡航速度(每帧移动的百分比)",default:"0.35",required:!1,version:"",isEvent:!1,bindable:!0,value:.35,control:{min:.05,max:1.5,step:.05}}],V={class:"demo-page"},M={class:"cmp-demo"},N={class:"cmp-demo__head"},U={class:"cmp-demo__tag"},D={class:"cmp-demo__stage"},T={class:"cmp-demo__toolbar"},Z={class:"cmp-demo__file"},H={class:"cmp-demo__file"},I={class:"cmp-demo__count"},O={class:"cmp-demo__row"},P={class:"cmp-demo__cell"},Q={class:"cmp-demo__cell"},W=q({__name:"CompareSliderView",setup(Y){const u=z("CompareSlider");function _(l){const e=l==="raw",o=e?["#9fb0b8","#c9c3b8"]:["#3d7ea6","#f0c08a"],s=e?"#8a9088":"#2f6b46",r=e?"#a5aaa2":"#5f9a67",v=e?"#b6b0a4":"#e0b276",S=e?"0.28":"0.6",C=`<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540">
2
+ <defs>
3
+ <linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0" stop-color="${o[0]}"/><stop offset="1" stop-color="${o[1]}"/>
5
+ </linearGradient>
6
+ <linearGradient id="land" x1="0" y1="0" x2="0" y2="1">
7
+ <stop offset="0" stop-color="${r}"/><stop offset="1" stop-color="${s}"/>
8
+ </linearGradient>
9
+ <linearGradient id="beach" x1="0" y1="0" x2="0" y2="1">
10
+ <stop offset="0" stop-color="${v}"/><stop offset="1" stop-color="${s}"/>
11
+ </linearGradient>
12
+ </defs>
13
+ <rect width="960" height="540" fill="url(#sky)"/>
14
+ <circle cx="742" cy="126" r="58" fill="#fff" opacity="${S}"/>
15
+ <path d="M0 300 L196 214 L352 292 L520 196 L676 286 L820 226 L960 300 L960 540 L0 540 Z" fill="url(#land)"/>
16
+ <path d="M0 406 L120 372 L268 404 L420 356 L560 398 L700 362 L840 400 L960 368 L960 540 L0 540 Z" fill="url(#beach)" opacity=".92"/>
17
+ <path d="M0 470 Q240 436 480 468 T960 452 L960 540 L0 540 Z" fill="${s}" opacity=".55"/>
18
+ <text x="58" y="76" font-family="PingFang SC, Microsoft YaHei, sans-serif" font-size="26"
19
+ font-weight="700" fill="rgba(255,255,255,.86)">${e?"原片 / 未调色":"调色后 / 暖调"}</text>
20
+ </svg>`;return`data:image/svg+xml;utf8,${encodeURIComponent(C)}`}const n=d(_("raw")),i=d(_("graded")),b=d(50),c=d(!1),m=d("(还没动过)"),y=d(null),L={display:"block",height:"520px",padding:"24px",background:"#f4efe7"};function x(l){m.value=`中线停在 ${l.toFixed(1)}%`}function h(){const l=n.value;n.value=i.value,i.value=l,m.value="交换了左右两张图"}function w(l,e){var v;const o=l.target,s=(v=o.files)==null?void 0:v[0];if(!s)return;if(!s.type.startsWith("image/")){o.value="";return}const r=new FileReader;r.onload=()=>{e==="before"?n.value=String(r.result):i.value=String(r.result),m.value=`${e==="before"?"原片":"调色"}换成了 ${s.name}`},r.readAsDataURL(s),o.value=""}return(l,e)=>(F(),B("div",V,[e[12]||(e[12]=t("header",null,[t("p",{class:"demo-page__eyebrow"},"远程组件"),t("h1",{class:"demo-page__title"},"comps-vue3 · CompareSlider"),t("p",{class:"demo-page__lead"},[a(" 来自 video-code 的「图片对比」:两张图叠在一起,上层用 "),t("code",null,"clip-path"),a(" 按中线裁掉右半边, 中间一条发光中线和圆形把手,拖动、方向键、双击回正都能调;原 demo 还带「上传两张图 / 交换 / 重置 / 自动巡航」这套编辑器。组件只保留对比卡本体(拖动和键盘交给一个透明的 range 输入,无障碍是现成的), 上传、交换这类操作留在页面里,通过 "),t("code",null,"v-model:position"),a(" 和 "),t("code",null,"swap()"),a(" / "),t("code",null,"reset()"),a(" 串起来。 ")])],-1)),t("section",M,[t("div",N,[e[5]||(e[5]=t("h2",{class:"demo-page__section-title"},"拖中线 · 看前后差别",-1)),t("code",U,g(b.value.toFixed(1))+"%",1)]),e[11]||(e[11]=t("p",{class:"demo-card__text"}," 左右拖动中线(或者点卡片上任意位置直接跳过去),焦点在卡片上时用 ← → 微调,双击回到中间。 下面两张图是同一场景的「原片 / 调色后」,可以换成你自己的照片。 ",-1)),t("div",D,[p(f(u),{ref_key:"sliderRef",ref:y,position:b.value,"onUpdate:position":e[0]||(e[0]=o=>b.value=o),before:n.value,after:i.value,"auto-sweep":c.value,"before-label":"原片","after-label":"调色后",onChange:x},null,8,["position","before","after","auto-sweep"])]),t("div",T,[t("label",Z,[e[6]||(e[6]=a(" 上传原片 ",-1)),t("input",{type:"file",accept:"image/*",onChange:e[1]||(e[1]=o=>w(o,"before"))},null,32)]),t("label",H,[e[7]||(e[7]=a(" 上传调色 ",-1)),t("input",{type:"file",accept:"image/*",onChange:e[2]||(e[2]=o=>w(o,"after"))},null,32)]),t("button",{class:"demo-btn demo-btn--ghost",type:"button",onClick:h},"交换"),t("button",{class:"demo-btn demo-btn--ghost",type:"button",onClick:e[3]||(e[3]=o=>{var s;return(s=y.value)==null?void 0:s.reset()})}," 重置 "),t("button",{class:"demo-btn",type:"button",onClick:e[4]||(e[4]=o=>c.value=!c.value)},g(c.value?"停止巡航":"自动巡航"),1),t("span",I,[e[8]||(e[8]=a("最近一次:",-1)),t("code",null,g(m.value),1)])]),t("div",O,[t("div",P,[p(f(u),{before:n.value,after:i.value,position:28,ratio:4/3,"show-labels":!1,"handle-size":44,accent:"#0f766e"},null,8,["before","after"]),e[9]||(e[9]=t("p",null,[a("4:3 + 不显示标签 + 小把手 + 换主色("),t("code",null,"ratio"),a(" / "),t("code",null,"handleSize"),a(" / "),t("code",null,"accent"),a(")")],-1))]),t("div",Q,[p(f(u),{before:n.value,after:i.value,position:60,ratio:1,"handle-size":76,"auto-sweep":!0,"sweep-speed":.6,"before-label":"BEFORE","after-label":"AFTER"},null,8,["before","after"]),e[10]||(e[10]=t("p",null,[a("1:1 + 自动巡航("),t("code",null,"autoSweep"),a(" / "),t("code",null,"sweepSpeed"),a(")")],-1))])])]),p($,{class:"cmp-preview"},{default:R(()=>[p(k,{name:"CompareSlider",component:f(u),schema:f(G),presets:{before:n.value,after:i.value,position:50},"stage-style":L},null,8,["component","schema","presets"])]),_:1}),e[13]||(e[13]=A('<p class="demo-note" data-v-42a55184><span data-v-42a55184> 面板里 <code data-v-42a55184>position</code> 是中线位置(也能直接拖),<code data-v-42a55184>ratio</code> 是宽高比, <code data-v-42a55184>handleSize</code> 是把手直径,<code data-v-42a55184>accent</code> 是中线颜色, <code data-v-42a55184>beforeLabel</code> / <code data-v-42a55184>afterLabel</code> / <code data-v-42a55184>showLabels</code> 控制两个标签, <code data-v-42a55184>autoSweep</code> + <code data-v-42a55184>sweepSpeed</code> 是自动巡航。两张图的地址是字符串控件, 换成别的链接即可。 </span></p>',1))]))}}),ee=E(W,[["__scopeId","data-v-42a55184"]]);export{ee as default};
21
+ //# sourceMappingURL=CompareSliderView-CLZsMU7c.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CompareSliderView-CLZsMU7c.js","sources":["../../src/views/CompareSliderView.vue"],"sourcesContent":["<template>\n <div class=\"demo-page\">\n <header>\n <p class=\"demo-page__eyebrow\">远程组件</p>\n <h1 class=\"demo-page__title\">comps-vue3 · CompareSlider</h1>\n <p class=\"demo-page__lead\">\n 来自 video-code 的「图片对比」:两张图叠在一起,上层用 <code>clip-path</code> 按中线裁掉右半边,\n 中间一条发光中线和圆形把手,拖动、方向键、双击回正都能调;原 demo 还带「上传两张图 / 交换 / 重置 /\n 自动巡航」这套编辑器。组件只保留对比卡本体(拖动和键盘交给一个透明的 range 输入,无障碍是现成的),\n 上传、交换这类操作留在页面里,通过 <code>v-model:position</code> 和 <code>swap()</code> /\n <code>reset()</code> 串起来。\n </p>\n </header>\n\n <section class=\"cmp-demo\">\n <div class=\"cmp-demo__head\">\n <h2 class=\"demo-page__section-title\">拖中线 · 看前后差别</h2>\n <code class=\"cmp-demo__tag\">{{ position.toFixed(1) }}%</code>\n </div>\n <p class=\"demo-card__text\">\n 左右拖动中线(或者点卡片上任意位置直接跳过去),焦点在卡片上时用 ← → 微调,双击回到中间。\n 下面两张图是同一场景的「原片 / 调色后」,可以换成你自己的照片。\n </p>\n\n <div class=\"cmp-demo__stage\">\n <CompareSlider\n ref=\"sliderRef\"\n v-model:position=\"position\"\n :before=\"before\"\n :after=\"after\"\n :auto-sweep=\"autoSweep\"\n before-label=\"原片\"\n after-label=\"调色后\"\n @change=\"onChange\"\n />\n </div>\n\n <div class=\"cmp-demo__toolbar\">\n <label class=\"cmp-demo__file\">\n 上传原片\n <input type=\"file\" accept=\"image/*\" @change=\"onUpload($event, 'before')\" />\n </label>\n <label class=\"cmp-demo__file\">\n 上传调色\n <input type=\"file\" accept=\"image/*\" @change=\"onUpload($event, 'after')\" />\n </label>\n <button class=\"demo-btn demo-btn--ghost\" type=\"button\" @click=\"swap\">交换</button>\n <button class=\"demo-btn demo-btn--ghost\" type=\"button\" @click=\"sliderRef?.reset()\">\n 重置\n </button>\n <button class=\"demo-btn\" type=\"button\" @click=\"autoSweep = !autoSweep\">\n {{ autoSweep ? '停止巡航' : '自动巡航' }}\n </button>\n <span class=\"cmp-demo__count\">最近一次:<code>{{ lastAction }}</code></span>\n </div>\n\n <div class=\"cmp-demo__row\">\n <div class=\"cmp-demo__cell\">\n <CompareSlider\n :before=\"before\"\n :after=\"after\"\n :position=\"28\"\n :ratio=\"4 / 3\"\n :show-labels=\"false\"\n :handle-size=\"44\"\n accent=\"#0f766e\"\n />\n <p>4:3 + 不显示标签 + 小把手 + 换主色(<code>ratio</code> / <code>handleSize</code> / <code>accent</code>)</p>\n </div>\n <div class=\"cmp-demo__cell\">\n <CompareSlider\n :before=\"before\"\n :after=\"after\"\n :position=\"60\"\n :ratio=\"1\"\n :handle-size=\"76\"\n :auto-sweep=\"true\"\n :sweep-speed=\"0.6\"\n before-label=\"BEFORE\"\n after-label=\"AFTER\"\n />\n <p>1:1 + 自动巡航(<code>autoSweep</code> / <code>sweepSpeed</code>)</p>\n </div>\n </div>\n </section>\n\n <DemoPreview class=\"cmp-preview\">\n <LevaPlayground\n name=\"CompareSlider\"\n :component=\"CompareSlider\"\n :schema=\"CompareSliderSchema\"\n :presets=\"{ before, after, position: 50 }\"\n :stage-style=\"stageStyle\"\n />\n </DemoPreview>\n\n <p class=\"demo-note\">\n <span>\n 面板里 <code>position</code> 是中线位置(也能直接拖),<code>ratio</code> 是宽高比,\n <code>handleSize</code> 是把手直径,<code>accent</code> 是中线颜色,\n <code>beforeLabel</code> / <code>afterLabel</code> / <code>showLabels</code> 控制两个标签,\n <code>autoSweep</code> + <code>sweepSpeed</code> 是自动巡航。两张图的地址是字符串控件,\n 换成别的链接即可。\n </span>\n </p>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\nimport CompareSliderSchema from '@melt-js/comps-vue3/src/components/CompareSlider.propsSchema.json'\nimport DemoPreview from '../components/DemoPreview.vue'\nimport LevaPlayground from '../components/LevaPlayground.vue'\nimport { remoteComponent } from '../configs/remote'\n\nconst CompareSlider = remoteComponent('CompareSlider')\n\n/** 同一场景生成两个版本:左边偏灰偏冷(原片),右边加饱和加暖(调色后) */\nfunction scene(variant: 'raw' | 'graded'): string {\n const isRaw = variant === 'raw'\n const sky = isRaw ? ['#9fb0b8', '#c9c3b8'] : ['#3d7ea6', '#f0c08a']\n const hill = isRaw ? '#8a9088' : '#2f6b46'\n const hillBack = isRaw ? '#a5aaa2' : '#5f9a67'\n const sand = isRaw ? '#b6b0a4' : '#e0b276'\n const sunOpacity = isRaw ? '0.28' : '0.6'\n const svg = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"960\" height=\"540\">\n <defs>\n <linearGradient id=\"sky\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0\" stop-color=\"${sky[0]}\"/><stop offset=\"1\" stop-color=\"${sky[1]}\"/>\n </linearGradient>\n <linearGradient id=\"land\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0\" stop-color=\"${hillBack}\"/><stop offset=\"1\" stop-color=\"${hill}\"/>\n </linearGradient>\n <linearGradient id=\"beach\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0\" stop-color=\"${sand}\"/><stop offset=\"1\" stop-color=\"${hill}\"/>\n </linearGradient>\n </defs>\n <rect width=\"960\" height=\"540\" fill=\"url(#sky)\"/>\n <circle cx=\"742\" cy=\"126\" r=\"58\" fill=\"#fff\" opacity=\"${sunOpacity}\"/>\n <path d=\"M0 300 L196 214 L352 292 L520 196 L676 286 L820 226 L960 300 L960 540 L0 540 Z\" fill=\"url(#land)\"/>\n <path d=\"M0 406 L120 372 L268 404 L420 356 L560 398 L700 362 L840 400 L960 368 L960 540 L0 540 Z\" fill=\"url(#beach)\" opacity=\".92\"/>\n <path d=\"M0 470 Q240 436 480 468 T960 452 L960 540 L0 540 Z\" fill=\"${hill}\" opacity=\".55\"/>\n <text x=\"58\" y=\"76\" font-family=\"PingFang SC, Microsoft YaHei, sans-serif\" font-size=\"26\"\n font-weight=\"700\" fill=\"rgba(255,255,255,.86)\">${isRaw ? '原片 / 未调色' : '调色后 / 暖调'}</text>\n </svg>`\n return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`\n}\n\nconst before = ref(scene('raw'))\nconst after = ref(scene('graded'))\nconst position = ref(50)\nconst autoSweep = ref(false)\nconst lastAction = ref('(还没动过)')\nconst sliderRef = ref<{ reset: () => void } | null>(null)\n\nconst stageStyle = {\n display: 'block',\n height: '520px',\n padding: '24px',\n background: '#f4efe7',\n}\n\nfunction onChange(value: number) {\n lastAction.value = `中线停在 ${value.toFixed(1)}%`\n}\n\nfunction swap() {\n const previous = before.value\n before.value = after.value\n after.value = previous\n lastAction.value = '交换了左右两张图'\n}\n\nfunction onUpload(event: Event, side: 'before' | 'after') {\n const input = event.target as HTMLInputElement\n const file = input.files?.[0]\n if (!file) return\n if (!file.type.startsWith('image/')) {\n input.value = ''\n return\n }\n const reader = new FileReader()\n reader.onload = () => {\n if (side === 'before') before.value = String(reader.result)\n else after.value = String(reader.result)\n lastAction.value = `${side === 'before' ? '原片' : '调色'}换成了 ${file.name}`\n }\n reader.readAsDataURL(file)\n input.value = ''\n}\n</script>\n\n<style scoped>\n.cmp-demo {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.cmp-demo__head {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 12px;\n align-items: baseline;\n justify-content: space-between;\n}\n\n.cmp-demo__head .demo-page__section-title {\n margin: 0;\n}\n\n.cmp-demo__tag {\n padding: 2px 8px;\n border-radius: 999px;\n background: var(--demo-accent-soft);\n color: var(--demo-accent);\n font-size: 12px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n}\n\n.cmp-demo__stage {\n overflow: hidden;\n border-radius: 16px;\n background: #f4efe7;\n padding: 10px;\n}\n\n.cmp-demo__toolbar {\n display: flex;\n flex-wrap: wrap;\n gap: 10px;\n align-items: center;\n}\n\n.cmp-demo__file {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n border: 1px dashed rgba(37, 31, 26, 0.28);\n border-radius: 8px;\n background: #fffaf1;\n color: #3f342c;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.cmp-demo__file input {\n display: none;\n}\n\n.cmp-demo__count {\n color: var(--demo-muted);\n font-size: 13px;\n}\n\n.cmp-demo__row {\n display: flex;\n flex-wrap: wrap;\n gap: 20px;\n margin-top: 6px;\n}\n\n.cmp-demo__cell {\n flex: 1 1 320px;\n min-width: 280px;\n}\n\n.cmp-demo__cell p {\n margin: 8px 0 0;\n color: var(--demo-muted);\n font-size: 12px;\n}\n\n.cmp-preview.is-demo-fullscreen {\n padding: 16px;\n background: #f4efe7;\n}\n</style>\n"],"names":["CompareSlider","remoteComponent","scene","variant","isRaw","sky","hill","hillBack","sand","sunOpacity","svg","before","ref","after","position","autoSweep","lastAction","sliderRef","stageStyle","onChange","value","swap","previous","onUpload","event","side","input","file","_a","reader","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_hoisted_3","_cache","_hoisted_4","_toDisplayString","_hoisted_5","_createVNode","_unref","$event","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","DemoPreview","LevaPlayground","CompareSliderSchema"],"mappings":"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"}