cineview 1.0.0 → 1.0.1

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 (52) hide show
  1. package/README.md +87 -251
  2. package/README.zh-CN.md +87 -251
  3. package/dist/animations/presets/index.d.ts +1 -1
  4. package/dist/artifacts.json +2 -2
  5. package/dist/cineview-dev.es.mjs +1 -1
  6. package/dist/cineview-drag.umd.js +1 -1
  7. package/dist/cineview-scroll.umd.js +1 -1
  8. package/dist/cineview.es.mjs +1 -1
  9. package/dist/cineview.umd.js +1 -1
  10. package/dist/components/Animate/animateSemantics.d.ts +0 -1
  11. package/dist/components/{CineView/CineView.d.ts → Cineview/Cineview.d.ts} +3 -5
  12. package/dist/components/Cineview/CineviewDispatch.d.ts +2 -0
  13. package/dist/components/Cineview/DirectScrollCineview.d.ts +8 -0
  14. package/dist/components/{CineView → Cineview}/directScrollHelpers.d.ts +4 -2
  15. package/dist/components/Cineview/index.d.ts +5 -0
  16. package/dist/components/{CineView → Cineview}/preloadTargets.d.ts +2 -2
  17. package/dist/components/{CineView → Cineview}/regroupCallbacks.d.ts +5 -5
  18. package/dist/components/{CineView/useCineViewImperativeApi.d.ts → Cineview/useCineviewImperativeApi.d.ts} +3 -3
  19. package/dist/components/{CineView → Cineview}/useScrollZoneRegistry.d.ts +3 -2
  20. package/dist/components/Scene/sceneScrollBudget.d.ts +2 -0
  21. package/dist/components/Scene/sceneScrollRuntime.d.ts +0 -1
  22. package/dist/components/Scene/useSceneAnimationRegistry.d.ts +2 -2
  23. package/dist/components/Scene/useSceneScrollTakeover.d.ts +0 -1
  24. package/dist/components/runtime/runtimeContext.d.ts +3 -3
  25. package/dist/components/runtime/scrollSceneFrameStore.d.ts +1 -1
  26. package/dist/context/{CineViewContext.d.ts → CineviewContext.d.ts} +8 -8
  27. package/dist/dev/PerfPanel.d.ts +1 -1
  28. package/dist/dev/usePerfMonitor.d.ts +4 -4
  29. package/dist/entry-drag.d.ts +5 -5
  30. package/dist/entry-scroll.d.ts +5 -5
  31. package/dist/hooks/useFirstSceneEnter.d.ts +3 -3
  32. package/dist/hooks/usePrefersReducedMotion.d.ts +1 -1
  33. package/dist/hooks/useSceneManager.d.ts +1 -0
  34. package/dist/index.d.ts +1 -1
  35. package/dist/public-api.d.ts +1 -1
  36. package/dist/types/index.d.ts +39 -42
  37. package/dist/utils/animationHelpers.d.ts +2 -2
  38. package/dist/utils/devLog.d.ts +1 -1
  39. package/dist/utils/performanceMonitor.d.ts +1 -1
  40. package/package.json +17 -13
  41. package/dist/components/CineView/CineViewDispatch.d.ts +0 -2
  42. package/dist/components/CineView/DirectScrollCineView.d.ts +0 -5
  43. package/dist/components/CineView/index.d.ts +0 -5
  44. /package/dist/components/{CineView → Cineview}/DragSceneStack.d.ts +0 -0
  45. /package/dist/components/{CineView → Cineview}/ScrollSceneSlot.d.ts +0 -0
  46. /package/dist/components/{CineView → Cineview}/ScrollSceneStack.d.ts +0 -0
  47. /package/dist/components/{CineView → Cineview}/ScrollbarOverlay.d.ts +0 -0
  48. /package/dist/components/{CineView → Cineview}/useNativeScrollController.d.ts +0 -0
  49. /package/dist/components/{CineView → Cineview}/useScrollInputBindings.d.ts +0 -0
  50. /package/dist/components/{CineView → Cineview}/useScrollSceneLayout.d.ts +0 -0
  51. /package/dist/components/{CineView → Cineview}/useScrollSceneSnapshots.d.ts +0 -0
  52. /package/dist/components/{CineView → Cineview}/useScrollViewport.d.ts +0 -0
package/README.zh-CN.md CHANGED
@@ -1,284 +1,120 @@
1
- # CineView
2
-
3
- [English](./README.md)
4
-
5
- ![版本 1.0.0](https://img.shields.io/badge/version-1.0.0-8A5B43?style=flat-square)
6
- [![React 19](https://img.shields.io/badge/React-19-287EA3?style=flat-square&logo=react&logoColor=white)](https://react.dev/)
7
- ![附带 TypeScript 类型](https://img.shields.io/badge/TypeScript-types_included-3178C6?style=flat-square&logo=typescript&logoColor=white)
8
- [![框架测试:1582 项通过](https://img.shields.io/badge/framework_tests-1582_passing-4F7562?style=flat-square)](#验证)
9
- [![行覆盖率:96.27%](https://img.shields.io/badge/line_coverage-96.27%25-4F7562?style=flat-square)](#验证)
10
- [![MIT 许可证](https://img.shields.io/badge/license-MIT-625D54?style=flat-square)](./LICENSE)
11
-
12
- CineView 是一个用 React 编写分场景动画页面的框架。它支持拖动切换场景、让动画跟随滚动,以及在场景内安排动画顺序;也提供设计稿坐标定位、图片预加载和由动画进度控制的视频。
13
-
14
- 测试和覆盖率徽章对应[验证](#验证)中记录的本地运行结果。
15
-
16
- [安装](#安装) · [运行示例](#运行示例) · [工作方式](#工作方式) · [站点与文档](#站点与文档) · [参与开发](#参与开发)
17
-
18
- ## 安装
19
-
20
- 在 React 应用中安装 CineView 1.0.0 和 peer 依赖:
21
-
22
- ```bash
23
- npm install cineview@1.0.0 react@19 react-dom@19 framer-motion@13
24
- ```
25
-
26
- 使用 pnpm:
27
-
28
- ```bash
29
- pnpm add cineview@1.0.0 react@19 react-dom@19 framer-motion@13
30
- ```
31
-
32
- 应用需提供 React `^19.0.0`、React DOM `^19.0.0` 和 Framer Motion `^13.0.0`。TypeScript 类型声明随包提供。
33
-
34
- ## 运行示例
35
-
36
- 仓库开发使用 Node.js 22.22.1 或更新的 22.x 版本,也支持 Node.js 24+;包管理器为 pnpm 10.22.0。
37
-
38
- 在当前仓库中运行:
39
-
40
- ```bash
41
- pnpm install --frozen-lockfile
42
- pnpm build
43
- pnpm --dir examples/minimal install --frozen-lockfile
44
- pnpm --dir examples/minimal dev
45
- ```
46
-
47
- 打开 Vite 输出的地址。[最小示例](./examples/minimal)包含三个场景,可以通过按钮切换导航模式并重新开始。
48
-
49
- 要在其他应用中测试本地修改,先构建并打包:
50
-
51
- ```bash
52
- pnpm build
53
- pnpm pack
54
- ```
55
-
56
- 如果应用目录与 `cineview` 仓库同级,在应用中安装生成的包和 peer 依赖:
1
+ <p align="center">
2
+ <a href="https://cineview.pages.dev">
3
+ <img src="./site/public/favicon.svg" width="64" height="64" alt="Cineview" />
4
+ </a>
5
+ </p>
6
+
7
+ <h1 align="center">Cineview</h1>
8
+
9
+ <p align="center">以时间线为核心,支持拖拽与滚动双模式的 React 动画框架。</p>
10
+
11
+ <p align="center">
12
+ <a href="./README.md">English</a> · <a href="./README.zh-CN.md">简体中文</a>
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://www.npmjs.com/package/cineview"><img src="https://img.shields.io/npm/v/cineview?style=flat-square&amp;color=8A5B43&amp;logo=npm" alt="npm version" /></a>
17
+ <a href="https://www.npmjs.com/package/cineview"><img src="https://img.shields.io/npm/dm/cineview?style=flat-square&amp;color=8A5B43" alt="npm monthly downloads" /></a>
18
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-555?style=flat-square" alt="MIT license" /></a>
19
+ <br />
20
+ <a href="https://react.dev/"><img src="https://img.shields.io/badge/React-19-287EA3?style=flat-square&amp;logo=react&amp;logoColor=white" alt="React 19" /></a>
21
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&amp;logo=typescript&amp;logoColor=white" alt="TypeScript" /></a>
22
+ <a href="https://cineview.pages.dev/coverage.json"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fcineview.pages.dev%2Fcoverage.json&amp;style=flat-square" alt="Framework line coverage" /></a>
23
+ </p>
24
+
25
+ <p align="center">
26
+ <a href="https://cineview.pages.dev">试试滚动</a> ·
27
+ <a href="https://cineview.pages.dev/drag">试试拖拽</a> ·
28
+ <a href="https://cineview.pages.dev/docs">查看文档</a> ·
29
+ <a href="#ai-看这里">AI 看这里</a>
30
+ </p>
31
+
32
+ 我想让落地页更容易维护,也让 AI 少做重复的工作。场景切换、动画编排和响应式布局,没必要每个项目都重新开发一遍。Cineview 把这些通用能力放进同一套场景和时间线结构,常用的展示片段可以封装成 React 组件,在不同页面中复用。
33
+
34
+ 做新页面时,组合已有的场景、素材和动画;后续修改时,找到对应组件,调整内容和时间线配置。配合按任务拆分的 AI 文档,让 AI 先读索引,再读取本次改动涉及的指南和代码,减少重复生成代码和反复梳理整页实现所用的 token,把更多上下文留给具体需求。
35
+
36
+ ## 可以做什么
37
+
38
+ | 能力 | 用法 |
39
+ | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
40
+ | 场景与组件复用 | 用 Scene 拆分内容,把常用片段封装成 React 组件,复用到产品介绍、活动页或交互展示中。 |
41
+ | 时间线编排 | 声明动画时长、延迟和先后关系;前一个动画的入场时长变化后,依赖它的动画会随之调整。 |
42
+ | 拖拽与滚动双模式 | 拖拽切换整屏场景,或让页面连续滚动;需要逐步展开内容时,让动画跟随滚动进度。 |
43
+ | [动画预设](https://cineview.pages.dev/docs/08-presets) | 使用淡入淡出、位移、缩放、旋转、翻转、弹跳、模糊等预设,快速搭出基础效果。 |
44
+ | [组合与自定义动画](https://cineview.pages.dev/docs/05-custom-animation) | 在一个元素上组合预设和自定义属性,用关键帧定义更细的变化,再用时间线安排不同元素的出场顺序。 |
45
+ | 入场、循环与交错效果 | 按需配置入场、循环和退场;让一组子元素依次出现,处理标题、卡片和列表的展示节奏。 |
46
+ | [视频控制](https://cineview.pages.dev/docs/11-video-timeline) | 用 AnimateVideo 让视频画面跟随拖拽或滚动,选择需要控制的片段,并与文字动画配合。 |
47
+ | [扩展自定义绘制](https://cineview.pages.dev/docs/09-use-animate-timeline) | 通过 useAnimateTimeline 读取动画进度,连接 Canvas、SVG、WebGL 或自己的组件,无需另写一套进度控制。 |
48
+ | [响应式布局](https://cineview.pages.dev/docs/05-responsive) | 根据设计稿宽度换算尺寸,用 Position 和 Container 配置位置与大小,并结合 CSS 调整布局。 |
49
+ | [资源预加载](https://cineview.pages.dev/docs/02-preload) | 提前准备场景图片,处理首屏加载、加载进度和资源失败,减少切换场景时等待素材的情况。 |
50
+
51
+ ## 试试看
52
+
53
+ 在 React 19 项目中,安装 Cineview 及其依赖:
57
54
 
58
55
  ```bash
59
- pnpm add ../cineview/cineview-1.0.0.tgz react@19 react-dom@19 framer-motion@13
56
+ npm install cineview@1.0.1 react@19 react-dom@19 framer-motion@13
60
57
  ```
61
58
 
62
- 按实际目录调整安装包路径。
59
+ 示例使用 1.0.1 的 `Cineview` API。1.0.0 使用早期的 `CineView` API,升级时需要更新导入名称和配置。
63
60
 
64
- ## 两个场景的页面
61
+ 把以下内容放进 `App.tsx`,在全局样式中设置 `body { margin: 0; }`。无需准备媒体素材。
65
62
 
66
63
  ```tsx
67
- import { Animate, CineView, Scene } from 'cineview';
64
+ import { Animate, Cineview, Scene } from 'cineview';
68
65
 
69
66
  export default function App() {
70
67
  return (
71
- <CineView mode="drag" designWidth={750} a11y={{ label: 'Product tour' }}>
72
- <Scene sceneId="opening">
73
- <div style={{ height: '100%', display: 'grid', placeItems: 'center' }}>
74
- <Animate enterAnimation="fade-in" duration={{ enter: 600 }}>
75
- <h1>Start here</h1>
76
- </Animate>
77
- </div>
78
- </Scene>
79
-
80
- <Scene sceneId="details">
81
- <div style={{ height: '100%', display: 'grid', placeItems: 'center' }}>
82
- <Animate enterAnimation="slide-up" duration={{ enter: 800 }}>
83
- <h2>Take a closer look</h2>
84
- </Animate>
85
- </div>
86
- </Scene>
87
- </CineView>
88
- );
89
- }
90
- ```
91
-
92
- 上下拖动即可切换场景。聚焦 CineView 容器后,也能使用方向键、PageUp、PageDown、Home 和 End。场景里的按钮和输入框保留各自的键盘操作。
93
-
94
- ## 工作方式
95
-
96
- `CineView` 选择页面的导航模式,`Scene` 组织内容并定义布局,`Animate` 为内容添加动画。
97
-
98
- | 配置 | 动画如何推进 |
99
- | ------------------------------------------------------------------- | -------------------------------------------- |
100
- | `mode="drag"`,`timeline.driver: 'scene'` | 场景内的元素时间线跟随手势,释放后继续播放。 |
101
- | `mode="drag"`,`timeline.driver: 'clock'` | 场景到达后,动画按时间播放。 |
102
- | `mode="scroll"`,位于 `Scene.scroll` 区域中,使用 `driver: 'scene'` | 区域内的实际滚动位置控制进度。 |
103
- | `mode="scroll"`,不在滚动区域中,或使用 `driver: 'clock'` | 元素满足可见性条件后,动画按时间播放。 |
104
-
105
- 默认模式是 `drag`,默认驱动是 `scene`。模式决定页面如何移动,驱动决定元素动画如何推进。
106
-
107
- 切换模式会使用另一套引擎。[示例](./examples/minimal/src/App.tsx)通过不同分支和 `key` 明确重新开始,不在两个模式之间传递原有进度。
108
-
109
- ### 让动画跟随滚动
110
-
111
- scroll 模式中的场景按文档流排列。添加 `Scene.scroll` 后,场景会在指定区域内保持可见,由滚动推进动画:
112
-
113
- ```tsx
114
- import { Animate, CineView, Scene } from 'cineview';
115
-
116
- export default function ScrollPage() {
117
- return (
118
- <CineView mode="scroll" designWidth={750}>
119
- <Scene sceneId="intro" layout={{ height: '100vh' }}>
120
- <h1>A page that scrolls</h1>
68
+ <Cineview mode="drag" unit="percent" scale={1}>
69
+ <Scene sceneId="intro" style={{ background: '#f5e8d8', padding: 40 }}>
70
+ <h1>向上拖动,查看下一个场景</h1>
121
71
  </Scene>
122
-
123
- <Scene
124
- sceneId="detail"
125
- layout={{ height: '100vh' }}
126
- scroll={{ zoneId: 'detail', trigger: 'center-lock' }}
127
- >
72
+ <Scene sceneId="details" style={{ background: '#e8edf0', padding: 40 }}>
73
+ <Animate animateId="title" enterAnimation="fade-in" duration={{ enter: 600 }}>
74
+ <h2>标题先出现</h2>
75
+ </Animate>
128
76
  <Animate
129
- enterAnimation={{
130
- initial: { opacity: 0, y: 60 },
131
- animate: { opacity: 1, y: 0 },
132
- }}
133
- duration={{ enter: 1600, exit: 0 }}
134
- timeline={{ driver: 'scene' }}
77
+ enterAnimation="slide-up"
78
+ duration={{ enter: 400 }}
79
+ timeline={{ after: 'title', delay: 100 }}
135
80
  >
136
- <h2>Scroll to reveal the detail</h2>
81
+ <p>说明在标题之后出现。</p>
137
82
  </Animate>
138
83
  </Scene>
139
- </CineView>
140
- );
141
- }
142
- ```
143
-
144
- 区域中声明的 1 毫秒动画时长对应 1 CSS 像素的滚动距离。本例的动画占 1,600 像素,向回滚动即可反向播放。场景本身的可见高度与这段动画距离分别计算。
145
-
146
- 大幅输入可能先停在区域边界,下一次输入继续前进。滚轮、触摸、键盘和滚动条操作都影响同一个滚动位置。[滚动说明](./site/src/content/docs/zh/scroll/01-centerlock.md)介绍了边界与零时长的行为。
147
-
148
- ### 顺序与循环
149
-
150
- 用 `timeline.after` 连接同一 Scene 内的动画,用 `timeline.delay` 增加间隔。例如,`timeline={{ after: 'heading', delay: 120 }}` 表示在名为 `heading` 的动画之后,再延迟 120 毫秒开始。
151
-
152
- `stagger={{ each: 110 }}` 按时间间隔启动容器的直接子元素。如果每项都需要独立跟随滚动,使用各自的 Animate。
153
-
154
- `loopAnimation` 在动画活动期间重复效果,`exitAnimation` 定义可选的退场。[时间线说明](./site/src/content/docs/zh/concepts/02-timeline.md)列出了不同驱动的差异:drag 模式中的 `driver: 'clock'` 不参与 `after` 编排,也不使用 `exitAnimation`。
155
-
156
- ### 响应式坐标
157
-
158
- `designWidth` 是设计稿的像素宽度,默认值为 `750`。Position 坐标和 Container 的数值长度按以下比例换算:
159
-
160
- ```text
161
- scale = viewport width / designWidth
162
- ```
163
-
164
- 横纵两个方向使用相同比例,不另按视口高度换算。CSS 字符串保留原有单位,透明度这类无单位数值也不会缩放。需要在手机上重新排列的文字和布局,仍需通过 CSS 断点处理。
165
-
166
- ## 组件与扩展
167
-
168
- | API | 用途 |
169
- | -------------------- | ---------------------------------------------------------------------------- |
170
- | `CineView` | 导航模式、公共配置、回调和导航 ref |
171
- | `Scene` | 场景内容、布局、资源声明和可选的滚动区域 |
172
- | `Animate` | 预设、自定义属性变化、并行或顺序组合,以及时间配置 |
173
- | `Position` | 相对包含块进行坐标定位和居中;`fixed` 内容限定在所属 Scene 内 |
174
- | `Container` | 将布局长度从设计像素换算为实际尺寸 |
175
- | `Image` | 通过共享预加载缓存加载图片 |
176
- | `AnimateVideo` | 根据动画进度定位视频帧,或使用普通播放 |
177
- | `useAnimateTimeline` | 以 MotionValue 读取最近一层 Animate 的进度,供自定义 DOM、SVG 或 Canvas 使用 |
178
-
179
- 自定义组件可以直接读取进度,不必将每一帧存入 React state:
180
-
181
- ```tsx
182
- import { motion } from 'framer-motion';
183
- import { useAnimateTimeline } from 'cineview';
184
-
185
- export function ProgressLine() {
186
- const { progress } = useAnimateTimeline();
187
-
188
- return (
189
- <motion.div
190
- style={{
191
- height: 4,
192
- width: '100%',
193
- background: '#d59273',
194
- transformOrigin: 'left',
195
- scaleX: progress,
196
- }}
197
- />
84
+ </Cineview>
198
85
  );
199
86
  }
200
87
  ```
201
88
 
202
- 将 `ProgressLine` 放在 `Animate` 内部,它会读取该 Animate 的时间线。也可以使用 render-prop 子函数,但这种用法的进度更新会触发 React 渲染;连续绘制优先使用 MotionValue hook。
203
-
204
- `ref.current.goToScene(index)` 切换场景。ref 还提供 `refreshLayout()`、`preload()`、`getCurrentIndex()` 和 `getPerformanceMetrics()`;scroll 模式另有 `goToZone()`。`onReady` 在这些 API 可用时提供引用;等待声明资源的加载过程则使用 `preload()`。
205
-
206
- 场景内的固定内容受 Scene 裁剪。需要跨场景保留的站点导航放在 CineView 外部。完整配置和回调见[组件参考](./site/src/content/docs/zh/components/01-cineview.md)。
207
-
208
- ## 包入口
209
-
210
- | 入口 | 支持的用法 |
211
- | ------------------------ | --------------------------------------------------------- |
212
- | `cineview` | 主 ESM 和 CommonJS 入口,包含两种导航引擎 |
213
- | `cineview/drag` | drag 模式的 CommonJS `require` 入口和类型 |
214
- | `cineview/scroll` | scroll 模式的 CommonJS `require` 入口和类型 |
215
- | `cineview/dev` | 可选的 ESM 开发工具,包含 `PerfPanel` 和 `usePerfMonitor` |
216
- | `cineview/dev/style.css` | 开发面板样式 |
217
-
218
- ESM 应用使用 `import { CineView } from 'cineview'`。两个模式子路径没有 ESM import 条件。浏览器脚本版本为 `cineview.umd.js`、`cineview-drag.umd.js` 和 `cineview-scroll.umd.js`。
219
-
220
- React、React DOM 和 Framer Motion 都是外部 peer 依赖。开发工具和面板 CSS 按需引入。
221
-
222
- ## 无障碍与动态效果偏好
223
-
224
- drag 容器支持键盘导航、可配置的无障碍名称和场景位置播报。非活动的 drag 场景使用 `inert` 和 `aria-hidden`,场景内的原生控件保留正常交互。
89
+ 向上拖动进入第二幕,标题先出现,说明随后跟上。试着改改文案和动画时长,做成自己的页面。想用滚动模式,可以接着试试[快速上手中的交互案例](https://cineview.pages.dev/docs/03-quickstart)。
225
90
 
226
- 开启 `prefers-reduced-motion` 后,`loopAnimation` 效果停止。由元素可见性触发的入场和退场动画直接显示结束状态。拖动或滚动控制的动画仍跟随输入。页面内容仍需具备可读文字、控件名称和可用的焦点顺序;无障碍验收需要覆盖实际内容和完整交互。
91
+ ## AI 看这里
227
92
 
228
- ## 验证
93
+ 如果你是正在使用 Cineview 的 AI 编程助手,**请先阅读 [llms.txt](./llms.txt)**,再按当前任务打开其中的指南:
229
94
 
230
- **2026 年 9 月 8 日**,使用 Node.js 22.22.1 运行框架检查:
95
+ - [快速开始](./docs/ai/quickstart.md):安装框架,完成第一个页面。
96
+ - [时间线](./docs/ai/timeline.md):安排动画顺序、时长和延迟。
97
+ - [模式选择](./docs/ai/modes.md):选择拖拽或滚动,确认各自的限制。
98
+ - [完整案例](./docs/ai/recipes.md):实现滚动叙事或加入视频。
231
99
 
232
- | 指标 | 结果 |
233
- | ------------ | ------------ |
234
- | 框架测试套件 | 118 套通过 |
235
- | 框架测试 | 1,582 项通过 |
236
- | 语句覆盖率 | 94.85% |
237
- | 分支覆盖率 | 90.22% |
238
- | 函数覆盖率 | 95.15% |
239
- | 行覆盖率 | 96.27% |
100
+ 这套指南对应 Cineview 1.0.1 API。写代码前先确认索引中的版本,遇到不确定的参数,再沿链接查阅类型定义和详细文档。只读取当前任务需要的内容,比一次加载整套文档更省输入 token,也能为页面代码和需求留出更多上下文。
240
101
 
241
- 数据来自 `pnpm test:coverage:framework`,不包含站点测试。四项覆盖率的配置下限均为 90%。修改框架后和发布前,重新运行命令更新这些数据。
102
+ ## 文档与案例
242
103
 
243
- [验证记录](./VERIFICATION.md)包含命令、检查范围和浏览器结果。运行检查:
104
+ 组件 API 和更多示例都在[文档](https://cineview.pages.dev/docs)中,可以按需要继续阅读:
244
105
 
245
- ```bash
246
- pnpm install --frozen-lockfile
247
- pnpm --dir site install --frozen-lockfile
248
- pnpm --dir examples/minimal install --frozen-lockfile
249
- pnpm --dir examples/performance-test install --frozen-lockfile
250
-
251
- pnpm verify:framework:static
252
- pnpm type-check
253
- pnpm type-check:site
254
- pnpm test:site-contracts
255
- pnpm docs:style:static
256
- pnpm --dir site build
257
- ```
106
+ - [动画顺序](https://cineview.pages.dev/docs/04-orchestration)
107
+ - [视频控制](https://cineview.pages.dev/docs/11-video-timeline)
108
+ - [Canvas、SVG 与 WebGL](https://cineview.pages.dev/docs/09-use-animate-timeline)
109
+ - [响应式布局](https://cineview.pages.dev/docs/05-responsive)
110
+ - [资源预加载](https://cineview.pages.dev/docs/02-preload)
258
111
 
259
- 安装 Chrome 后,`pnpm test:browser` 运行框架浏览器检查。静态检查和覆盖率本身不能证明拖动或滚动行为正确。[CI 配置](./.github/workflows/ci.yml)包含 Node.js 22.22.1 与 24.x 检查;[发布流程](./.github/workflows/release.yml)要求浏览器验收通过后再发布。
260
-
261
- ## 站点与文档
262
-
263
- 仓库尚未配置公开站点地址。可以在本地运行双语站点:
264
-
265
- ```bash
266
- pnpm install --frozen-lockfile
267
- pnpm --dir site install --frozen-lockfile
268
- pnpm build
269
- pnpm --dir site dev
270
- ```
271
-
272
- 默认地址是 [http://localhost:4000](http://localhost:4000);端口被占用时,以 Vite 输出的地址为准。[文档](http://localhost:4000/docs)和[拖动演示](http://localhost:4000/drag)是同一站点中的页面。
273
-
274
- 文档源码:[English](./site/src/content/docs/en) · [简体中文](./site/src/content/docs/zh)。两种语言均包含安装、概念、拖动与滚动行为、组件和进阶用法。
112
+ 想在本地运行源码,可以从[最小示例](./examples/minimal/README.md)开始,其中包含启动步骤和两种模式。
275
113
 
276
114
  ## 参与开发
277
115
 
278
- 开发流程见 [CONTRIBUTING.md](./CONTRIBUTING.md),仓库规则见 [AGENTS.md](./AGENTS.md)。运行时改动除了单元测试,还需要浏览器证据。文档修改同步维护中英文版本。
279
-
280
- 通过 [GitHub issues](https://github.com/AlienMu/cineview/issues)提交问题。
116
+ 欢迎反馈问题、分享案例或一起改进框架。本地环境和检查步骤见 [Contributing](./CONTRIBUTING.md),版本变化见 [Changelog](./CHANGELOG.md),发布流程见[发布说明](./RELEASING.md)。
281
117
 
282
118
  ## 许可证
283
119
 
284
- [MIT](./LICENSE),著作权人 Alien.mu。
120
+ [MIT](./LICENSE) © Alien.mu.
@@ -23,7 +23,7 @@ export declare class PresetLoadError extends Error {
23
23
  export declare function isPresetLoadError(error: unknown): error is PresetLoadError;
24
24
  type AnimationModule = Record<string, PresetAnimation>;
25
25
  type CategoryLoader = (category: string) => Promise<AnimationModule>;
26
- /** Load one category, coalescing concurrent requests across all CineView roots. */
26
+ /** Load one category, coalescing concurrent requests across all Cineview roots. */
27
27
  export declare function loadAnimationModule(category: string): Promise<AnimationModule>;
28
28
  /** Resolve one preset. Unknown names/missing exports are permanent failures. */
29
29
  export declare function getPresetAnimation(name: string): Promise<PresetAnimation>;
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "file": "cineview.umd.js",
13
13
  "module": false,
14
- "budgetKB": 56,
14
+ "budgetKB": 62,
15
15
  "label": "全量 UMD(运行时按 mode 派发)",
16
16
  "entry": "src/index.ts"
17
17
  },
@@ -25,7 +25,7 @@
25
25
  {
26
26
  "file": "cineview-scroll.umd.js",
27
27
  "module": false,
28
- "budgetKB": 50,
28
+ "budgetKB": 54,
29
29
  "label": "UMD(仅滚动引擎)",
30
30
  "entry": "src/entry-scroll.ts"
31
31
  },
@@ -1,2 +1,2 @@
1
- import{useRef as e,useState as i,useSyncExternalStore as r}from"react";import{jsx as a,jsxs as t}from"react/jsx-runtime";var n=new WeakMap,l=()=>null,s=()=>()=>{};function c(e,i=!0){const a=e&&i?function(e){const i=n.get(e.getPerformanceMetrics);if(i)return i;const r=function(e){let i,r=null;const a=new Set,t=()=>{const i=e.getPerformanceMetrics();r={current:i,history:[...(r?.history??[]).slice(-59),i]},a.forEach(e=>e())};return{getSnapshot:()=>r,subscribe:e=>(a.add(e),1===a.size&&(t(),i=setInterval(t,500)),()=>{a.delete(e),0===a.size&&void 0!==i&&(clearInterval(i),i=void 0)})}}(e);return n.set(e.getPerformanceMetrics,r),r}(e):null;return r(a?.subscribe??s,a?.getSnapshot??l,l)}function o({position:r="top-right",defaultExpanded:n=!0,source:l=null,enabled:s=!0,draggable:o=!0,lang:d="en"}){const[p,m]=i(n),f=e(null),v=e(null),u=c(l,s),h=u?.current??null,w=h?.fps??0,g=h?.avgFrameTime??0,b=u?.history.filter(e=>e.avgFrameTime>0).map(e=>e.fps)??[],y=null!==h&&g>32,N=null!==h&&g>50,x=w>=55?"#6ee7b7":w>=45?"#fbbf24":"#ef4444",C=g<=20?"#6ee7b7":g<=32?"#fbbf24":"#ef4444",P="zh"===d?{expand:"展开 CineView 性能面板",collapse:"收起 CineView 性能面板",title:"CineView 性能",fps:"帧率",frame:"平均帧时",loaded:"已加载代码",history:"帧率历史",memory:"内存",samples:"采样数",monitor:"监视器",waiting:"等待 CineView 引用",warning:"性能警告",issue:"性能问题",details:e=>`平均帧时:${e}(目标 <16.67ms)`,loadedTitle:"根据 Resource Timing 估算的页面 JavaScript 和 CSS,不是包 gzip 大小。"}:{expand:"Expand CineView performance panel",collapse:"Collapse CineView performance panel",title:"CineView Performance",fps:"FPS",frame:"Avg Frame",loaded:"Loaded code",history:"FPS History",memory:"Memory",samples:"Samples",monitor:"Monitor",waiting:"Waiting for CineView ref",warning:"Performance warning",issue:"Performance issue",details:e=>`Average frame time: ${e} (target <16.67ms)`,loadedTitle:"Estimated loaded page JavaScript and CSS from Resource Timing; not package gzip size."},S=e=>{v.current?.pointerId===e.pointerId&&(v.current=null,f.current?.removeAttribute("data-dragging"),e.currentTarget.hasPointerCapture(e.pointerId)&&e.currentTarget.releasePointerCapture(e.pointerId))};return p?t("section",{ref:f,className:`cineview-perf-panel cineview-perf-panel--${r}`,"aria-label":P.title,children:[t("div",{className:"cineview-perf-header",onPointerDown:e=>{if(!o||0!==e.button||e.target.closest("button"))return;const i=f.current;if(!i)return;const r=i.getBoundingClientRect();i.style.left=`${r.left}px`,i.style.top=`${r.top}px`,i.style.right="auto",i.style.bottom="auto",i.dataset.dragging="true",v.current={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,left:r.left,top:r.top},e.currentTarget.setPointerCapture(e.pointerId)},onPointerMove:e=>{const i=v.current,r=f.current;!i||!r||i.pointerId!==e.pointerId||(r.style.left=`${Math.max(8,i.left+e.clientX-i.startX)}px`,r.style.top=`${Math.max(8,i.top+e.clientY-i.startY)}px`)},onPointerUp:S,onPointerCancel:S,"data-draggable":o?"true":"false",children:[a("h3",{className:"cineview-perf-title",children:P.title}),a("button",{type:"button",className:"cineview-perf-toggle",onClick:()=>m(!1),"aria-label":P.collapse,children:"−"})]}),t("div",{className:"cineview-perf-body",children:[t("div",{className:"cineview-perf-metrics",children:[t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",children:P.fps}),a("div",{className:"cineview-perf-metric-value",style:{color:x},children:h?w:"--"})]}),t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",children:P.frame}),a("div",{className:"cineview-perf-metric-value",style:{color:C},children:h?`${g.toFixed(1)}ms`:"--"})]}),t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",title:P.loadedTitle,children:P.loaded}),a("div",{className:"cineview-perf-metric-value",children:h?`~${h.bundleSize.toFixed(1)} KB`:"--"})]})]}),t("div",{className:"cineview-perf-chart",children:[a("div",{className:"cineview-perf-chart-title",children:P.history}),t("svg",{className:"cineview-perf-chart-svg",viewBox:"0 0 300 60",preserveAspectRatio:"none","aria-hidden":"true",children:[a("line",{x1:"0",y1:"30",x2:"300",y2:"30",stroke:"#374151",strokeWidth:"0.5",strokeDasharray:"2,2"}),a("line",{x1:"0",y1:"50",x2:"300",y2:"50",stroke:"#374151",strokeWidth:"0.5",strokeDasharray:"2,2"}),a("polyline",{points:b.map((e,i)=>`${i/Math.max(59,1)*300},${60-e/60*60}`).join(" "),fill:"none",stroke:x,strokeWidth:"2"})]}),t("div",{className:"cineview-perf-chart-labels",children:[a("span",{children:"60"}),a("span",{children:"30"}),a("span",{children:"0"})]})]}),t("div",h?{className:"cineview-perf-stats",children:[t("div",{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.memory}),a("span",{className:"cineview-perf-stat-value",children:void 0===h.memoryUsage?"n/a":`${h.memoryUsage.toFixed(1)} MB`})]}),t("div",{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.samples}),a("span",{className:"cineview-perf-stat-value",children:u?.history.length??0})]})]}:{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.monitor}),a("span",{className:"cineview-perf-stat-value",children:P.waiting})]}),y&&t("div",{className:"cineview-perf-warning "+(N?"cineview-perf-warning--critical":""),children:[N?P.issue:P.warning,a("div",{className:"cineview-perf-warning-details",children:P.details(`${g.toFixed(1)}ms`)})]})]})]}):a("button",{type:"button",className:`cineview-perf-panel cineview-perf-panel--collapsed cineview-perf-panel--${r}`,onClick:()=>m(!0),"aria-label":P.expand,children:t("span",{className:"cineview-perf-collapsed-label",children:[a("span",{style:{color:x},children:h?w:"--"})," fps"]})})}export{o as PerfPanel,c as usePerfMonitor};
1
+ import{useRef as e,useState as i,useSyncExternalStore as r}from"react";import{jsx as a,jsxs as t}from"react/jsx-runtime";var n=new WeakMap,l=()=>null,s=()=>()=>{};function c(e,i=!0){const a=e&&i?function(e){const i=n.get(e.getPerformanceMetrics);if(i)return i;const r=function(e){let i,r=null;const a=new Set,t=()=>{const i=e.getPerformanceMetrics();r={current:i,history:[...(r?.history??[]).slice(-59),i]},a.forEach(e=>e())};return{getSnapshot:()=>r,subscribe:e=>(a.add(e),1===a.size&&(t(),i=setInterval(t,500)),()=>{a.delete(e),0===a.size&&void 0!==i&&(clearInterval(i),i=void 0)})}}(e);return n.set(e.getPerformanceMetrics,r),r}(e):null;return r(a?.subscribe??s,a?.getSnapshot??l,l)}function o({position:r="top-right",defaultExpanded:n=!0,source:l=null,enabled:s=!0,draggable:o=!0,lang:d="en"}){const[p,m]=i(n),v=e(null),f=e(null),u=c(l,s),h=u?.current??null,w=h?.fps??0,g=h?.avgFrameTime??0,b=u?.history.filter(e=>e.avgFrameTime>0).map(e=>e.fps)??[],y=null!==h&&g>32,N=null!==h&&g>50,x=w>=55?"#6ee7b7":w>=45?"#fbbf24":"#ef4444",C=g<=20?"#6ee7b7":g<=32?"#fbbf24":"#ef4444",P="zh"===d?{expand:"展开 Cineview 性能面板",collapse:"收起 Cineview 性能面板",title:"Cineview 性能",fps:"帧率",frame:"平均帧时",loaded:"已加载代码",history:"帧率历史",memory:"内存",samples:"采样数",monitor:"监视器",waiting:"等待 Cineview 引用",warning:"性能警告",issue:"性能问题",details:e=>`平均帧时:${e}(目标 <16.67ms)`,loadedTitle:"根据 Resource Timing 估算的页面 JavaScript 和 CSS,不是包 gzip 大小。"}:{expand:"Expand Cineview performance panel",collapse:"Collapse Cineview performance panel",title:"Cineview Performance",fps:"FPS",frame:"Avg Frame",loaded:"Loaded code",history:"FPS History",memory:"Memory",samples:"Samples",monitor:"Monitor",waiting:"Waiting for Cineview ref",warning:"Performance warning",issue:"Performance issue",details:e=>`Average frame time: ${e} (target <16.67ms)`,loadedTitle:"Estimated loaded page JavaScript and CSS from Resource Timing; not package gzip size."},S=e=>{f.current?.pointerId===e.pointerId&&(f.current=null,v.current?.removeAttribute("data-dragging"),e.currentTarget.hasPointerCapture(e.pointerId)&&e.currentTarget.releasePointerCapture(e.pointerId))};return p?t("section",{ref:v,className:`cineview-perf-panel cineview-perf-panel--${r}`,"aria-label":P.title,children:[t("div",{className:"cineview-perf-header",onPointerDown:e=>{if(!o||0!==e.button||e.target.closest("button"))return;const i=v.current;if(!i)return;const r=i.getBoundingClientRect();i.style.left=`${r.left}px`,i.style.top=`${r.top}px`,i.style.right="auto",i.style.bottom="auto",i.dataset.dragging="true",f.current={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,left:r.left,top:r.top},e.currentTarget.setPointerCapture(e.pointerId)},onPointerMove:e=>{const i=f.current,r=v.current;!i||!r||i.pointerId!==e.pointerId||(r.style.left=`${Math.max(8,i.left+e.clientX-i.startX)}px`,r.style.top=`${Math.max(8,i.top+e.clientY-i.startY)}px`)},onPointerUp:S,onPointerCancel:S,"data-draggable":o?"true":"false",children:[a("h3",{className:"cineview-perf-title",children:P.title}),a("button",{type:"button",className:"cineview-perf-toggle",onClick:()=>m(!1),"aria-label":P.collapse,children:"−"})]}),t("div",{className:"cineview-perf-body",children:[t("div",{className:"cineview-perf-metrics",children:[t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",children:P.fps}),a("div",{className:"cineview-perf-metric-value",style:{color:x},children:h?w:"--"})]}),t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",children:P.frame}),a("div",{className:"cineview-perf-metric-value",style:{color:C},children:h?`${g.toFixed(1)}ms`:"--"})]}),t("div",{className:"cineview-perf-metric",children:[a("div",{className:"cineview-perf-metric-label",title:P.loadedTitle,children:P.loaded}),a("div",{className:"cineview-perf-metric-value",children:h?`~${h.bundleSize.toFixed(1)} KB`:"--"})]})]}),t("div",{className:"cineview-perf-chart",children:[a("div",{className:"cineview-perf-chart-title",children:P.history}),t("svg",{className:"cineview-perf-chart-svg",viewBox:"0 0 300 60",preserveAspectRatio:"none","aria-hidden":"true",children:[a("line",{x1:"0",y1:"30",x2:"300",y2:"30",stroke:"#374151",strokeWidth:"0.5",strokeDasharray:"2,2"}),a("line",{x1:"0",y1:"50",x2:"300",y2:"50",stroke:"#374151",strokeWidth:"0.5",strokeDasharray:"2,2"}),a("polyline",{points:b.map((e,i)=>`${i/Math.max(59,1)*300},${60-e/60*60}`).join(" "),fill:"none",stroke:x,strokeWidth:"2"})]}),t("div",{className:"cineview-perf-chart-labels",children:[a("span",{children:"60"}),a("span",{children:"30"}),a("span",{children:"0"})]})]}),t("div",h?{className:"cineview-perf-stats",children:[t("div",{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.memory}),a("span",{className:"cineview-perf-stat-value",children:void 0===h.memoryUsage?"n/a":`${h.memoryUsage.toFixed(1)} MB`})]}),t("div",{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.samples}),a("span",{className:"cineview-perf-stat-value",children:u?.history.length??0})]})]}:{className:"cineview-perf-stat",children:[a("span",{className:"cineview-perf-stat-label",children:P.monitor}),a("span",{className:"cineview-perf-stat-value",children:P.waiting})]}),y&&t("div",{className:"cineview-perf-warning "+(N?"cineview-perf-warning--critical":""),children:[N?P.issue:P.warning,a("div",{className:"cineview-perf-warning-details",children:P.details(`${g.toFixed(1)}ms`)})]})]})]}):a("button",{type:"button",className:`cineview-perf-panel cineview-perf-panel--collapsed cineview-perf-panel--${r}`,onClick:()=>m(!0),"aria-label":P.expand,children:t("span",{className:"cineview-perf-collapsed-label",children:[a("span",{style:{color:x},children:h?w:"--"})," fps"]})})}export{o as PerfPanel,c as usePerfMonitor};
2
2
  //# sourceMappingURL=cineview-dev.es.mjs.map