easecut-react 1.1.0 → 1.1.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.
- package/README.md +94 -181
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,224 +1,137 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<h1 align="center">EaseCut</h1>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://underhear.github.io/EaseCut/"><img src="https://img.shields.io/badge/演示地址-https://underhear.github.io/EaseCut/-01ab85" alt="在线演示"></a>
|
|
7
|
+
</p>
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="https://underhear.github.io/EaseCut/"><img src="https://img.shields.io/badge/Github-https://github.com/UnderHear/EaseCut-6543ae" alt="在线演示"></a>
|
|
10
|
+
</p>
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
<a href="https://www.npmjs.com/package/easecut-react"><img src="https://img.shields.io/npm/v/easecut-react?label=npm" alt="npm 版本"></a>
|
|
14
|
+
<img src="https://img.shields.io/badge/license-MIT-22c55e" alt="MIT License">
|
|
15
|
+
</p>
|
|
8
16
|
|
|
9
|
-
>
|
|
17
|
+
<p align="center">
|
|
18
|
+
一个 React 视频编辑器,能够快速接入到任何项目中,支持npm导入。
|
|
19
|
+
</p>
|
|
20
|
+
<p align="center">
|
|
21
|
+
具备进行快速视频编辑的完备功能。
|
|
22
|
+
</p>
|
|
10
23
|
|
|
11
|
-
##
|
|
24
|
+
## 效果图
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
<table>
|
|
27
|
+
<tr>
|
|
28
|
+
<td><img src="https://i.postimg.cc/PxR8yqt2/image.png" alt="效果图 1"></td>
|
|
29
|
+
<td><img src="https://i.postimg.cc/gJC3d0fb/image.png" alt="效果图 2"></td>
|
|
30
|
+
</tr>
|
|
31
|
+
<tr>
|
|
32
|
+
<td><img src="https://i.postimg.cc/Y0BLZsWB/image.png" alt="效果图 3"></td>
|
|
33
|
+
<td><img src="https://i.postimg.cc/RhYyvwvN/image.png" alt="效果图 4"></td>
|
|
34
|
+
</tr>
|
|
35
|
+
</table>
|
|
36
|
+
|
|
37
|
+
## 快速开始
|
|
38
|
+
|
|
39
|
+
随着AI生成视频的需求越来越大,视频编辑器的重要性日益体现,适合中小型开发团队或个人开发者的...
|
|
40
|
+
|
|
41
|
+
废话到此为止!让我们快速开始!
|
|
14
42
|
|
|
15
43
|
```bash
|
|
16
|
-
npm
|
|
17
|
-
npm run dev
|
|
44
|
+
npm i easecut-react
|
|
18
45
|
```
|
|
19
46
|
|
|
20
|
-
|
|
47
|
+
`home.tsx`
|
|
21
48
|
|
|
22
|
-
|
|
49
|
+
```tsx
|
|
50
|
+
import { EaseCut } from "easecut-react";
|
|
51
|
+
import "easecut-react/styles.css";
|
|
23
52
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
npm run test
|
|
28
|
-
npm run build
|
|
29
|
-
npm run preview
|
|
53
|
+
export function App() {
|
|
54
|
+
return <EaseCut />;
|
|
55
|
+
}
|
|
30
56
|
```
|
|
31
57
|
|
|
32
|
-
|
|
58
|
+
好了。
|
|
33
59
|
|
|
34
|
-
|
|
35
|
-
- `demo-dist/`:可部署的示例应用。
|
|
60
|
+
## 导入素材 & 导出视频 & 关闭编辑器
|
|
36
61
|
|
|
37
|
-
|
|
62
|
+
### 导入素材
|
|
38
63
|
|
|
39
64
|
```tsx
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
} from 'easecut';
|
|
44
|
-
import 'easecut/styles.css';
|
|
65
|
+
import { useRef } from "react";
|
|
66
|
+
import { EaseCut, type EaseCutHandle } from "easecut-react";
|
|
67
|
+
import "easecut-react/styles.css";
|
|
45
68
|
|
|
46
|
-
export function
|
|
47
|
-
const
|
|
48
|
-
localStorage.setItem('timeline-draft', JSON.stringify(draft));
|
|
49
|
-
};
|
|
69
|
+
export function App() {
|
|
70
|
+
const editorRef = useRef<EaseCutHandle>(null);
|
|
50
71
|
|
|
51
72
|
return (
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
</div>
|
|
73
|
+
<>
|
|
74
|
+
<button
|
|
75
|
+
onClick={() =>
|
|
76
|
+
void editorRef.current?.source.add("/video.mp4")
|
|
77
|
+
}>
|
|
78
|
+
导入素材
|
|
79
|
+
</button>
|
|
80
|
+
<EaseCut ref={editorRef} />
|
|
81
|
+
</>
|
|
62
82
|
);
|
|
63
83
|
}
|
|
64
84
|
```
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
新建项目的“原纵横比”取素材列表中第一个具有有效宽高的视频原始尺寸;没有符合条件的视频时使用 `1280 × 720`。预览区左侧的纵横比面板还提供 `16:9`(`1280 × 720`)、`4:3`(`960 × 720`)、`2:1`(`1440 × 720`)、`9:16`(`720 × 1280`)、`1:1`(`720 × 720`)和 `3:4`(`720 × 960`)项目预设。切换画布时会围绕画布中心等比调整现有视觉内容,并形成一个可撤销、可重做的编辑记录。
|
|
69
|
-
|
|
70
|
-
`VideoTimelineDraft.tracks` 使用从合成底层到顶层的数组顺序,且每条轨道的
|
|
71
|
-
`zIndex` 等于数组下标。轨道固定规范化为 `[音频…, 视频…, 文字…]`:
|
|
72
|
-
`tracks[0]` 是最低层,最后一条轨道是最高层;主视频是视频组的最低层。
|
|
73
|
-
时间线会反向展示该数组,因此最高层显示在顶部、音频显示在底部,符合
|
|
74
|
-
“上方轨道覆盖下方轨道”的剪辑软件习惯。
|
|
75
|
-
|
|
76
|
-
## 媒体源
|
|
77
|
-
|
|
78
|
-
视频源建议提供时长、宽度和高度;音频源建议提供时长;图片源建议提供宽度和高度。如果缺失,编辑器会通过对应的浏览器媒体元素异步读取。
|
|
79
|
-
|
|
80
|
-
工具栏的在线素材弹窗会根据 URL 路径中的文件后缀自动识别视频、音频或图片,注册 source 后直接创建 clip;查询参数和签名不会影响识别。图片仅接受 PNG、JPEG 和 JPG,加载时还会校验实际文件签名,不支持 WebP、GIF 或 SVG。
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
type VideoTimelineSourceBase = {
|
|
84
|
-
id: string;
|
|
85
|
-
fileName: string;
|
|
86
|
-
src: string;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
type VideoTimelineSource =
|
|
90
|
-
| (VideoTimelineSourceBase & {
|
|
91
|
-
type: 'video' | 'audio';
|
|
92
|
-
durationUs?: number;
|
|
93
|
-
waveformSrc?: string;
|
|
94
|
-
width?: number;
|
|
95
|
-
height?: number;
|
|
96
|
-
})
|
|
97
|
-
| (VideoTimelineSourceBase & {
|
|
98
|
-
type: 'image';
|
|
99
|
-
// 仅表示初始展示时长,省略时默认 5 秒。
|
|
100
|
-
durationUs?: number;
|
|
101
|
-
width?: number;
|
|
102
|
-
height?: number;
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
后续先调用 `editor.source.add(...)` 注册素材,再调用 `editor.clip.add({ sourceId })` 创建片段。source 和 clip 相互独立;仍被 clip 引用的 source 不能删除。
|
|
86
|
+
### 导出视频
|
|
107
87
|
|
|
108
88
|
```tsx
|
|
109
|
-
import {
|
|
110
|
-
import
|
|
111
|
-
|
|
112
|
-
const editorRef = useRef<EaseCutHandle>(null);
|
|
89
|
+
import { EaseCut } from "easecut-react";
|
|
90
|
+
import "easecut-react/styles.css";
|
|
113
91
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
92
|
+
export function App() {
|
|
93
|
+
return (
|
|
94
|
+
<EaseCut
|
|
95
|
+
onExport={async ({ payload }) => {
|
|
96
|
+
await fetch("/api/export", {
|
|
97
|
+
method: "POST",
|
|
98
|
+
body: JSON.stringify(payload),
|
|
99
|
+
});
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
117
102
|
);
|
|
118
|
-
|
|
119
|
-
await editorRef.current?.clip.add({ sourceId: source.id });
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
<EaseCut ref={editorRef} />;
|
|
103
|
+
}
|
|
124
104
|
```
|
|
125
105
|
|
|
126
|
-
|
|
127
|
-
`startUs`、`durationUs`、`sourceDurationUs`、`trimStartUs` 和 `trimEndUs`
|
|
128
|
-
均为整数微秒;浏览器媒体元素使用的浮点秒只在媒体边界换算。
|
|
129
|
-
每个 clip 必须持有布尔字段 `hidden`,新建 clip 默认为 `false`。隐藏 clip 仍参与
|
|
130
|
-
时间线时长、布局、碰撞和吸附并可继续编辑,但不会被预览、播放或写入导出轨道;
|
|
131
|
-
复制、粘贴和分割会保留该状态。
|
|
132
|
-
每个音视频 clip 持有独立的 `volume`(`0` 至 `1`);轨道仅持有 `muted`,静音时不会改写
|
|
133
|
-
clip 的已保存音量。
|
|
134
|
-
|
|
135
|
-
图片 Clip 与视频 Clip 混排在视频轨,支持选择、移动、吸附、分割、复制、隐藏、画布变换和撤销重做。图片默认展示 5 秒,source 的 `durationUs` 可覆盖首次创建时长;创建后由 clip 的 `durationUs` 作为权威状态。图片两侧裁剪直接改变时间线区间,可像文字 Clip 一样延长,不保存 `sourceDurationUs`、`trimStartUs`、`trimEndUs`、`speed`、`volume` 或 `waveformSrc`。视频轨静音不会隐藏图片。
|
|
106
|
+
`EaseCut` 不直接编码视频,`onExport` 用于将导出数据交给后端渲染。
|
|
136
107
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
当前字体预设固定为站酷仓耳渔阳体、站酷高端黑、站酷酷黑体、站酷快乐体、站酷文艺体、站酷小薇体、思源黑体和阿里巴巴普惠体,不包含方正字体。八款字体均随组件库打包并按需加载。
|
|
140
|
-
|
|
141
|
-
每个音视频 clip 还必须持有 `speed`,取值范围为 `0.1` 至 `4`,新建 clip
|
|
142
|
-
默认为 `1`。倍速作用于裁剪后的源区间,`durationUs` 是变速后的时间线时长;
|
|
143
|
-
修改倍速会保持 clip 起点和裁剪范围,并联动平移同轨后续片段。
|
|
144
|
-
音视频预览会优先通过 AudioWorklet 和 SoundTouch 对媒体原声进行实时音调补偿,
|
|
145
|
-
使变速主要改变时长并保持原音调;浏览器不支持 AudioWorklet 时会显示降级提示,
|
|
146
|
-
并回退到 `preservesPitch`。视频 Canvas 优先由
|
|
147
|
-
`requestVideoFrameCallback` 驱动,播放期间不会按时间线动画帧反复 seek;
|
|
148
|
-
每条轨道还会预加载五秒内的下一个 clip,减少连接点等待。慢放只延长现有视频帧,
|
|
149
|
-
不执行光流插帧,因此低帧率素材在极慢速下仍会看到重复帧。
|
|
150
|
-
|
|
151
|
-
## 私有媒体加载
|
|
152
|
-
|
|
153
|
-
默认加载器执行不带 token、cookie 或自定义 header 的 `fetch`。私有媒体可以注入加载器:
|
|
108
|
+
### 关闭编辑器
|
|
154
109
|
|
|
155
110
|
```tsx
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const response = await fetch(url, {
|
|
160
|
-
signal,
|
|
161
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
162
|
-
});
|
|
163
|
-
if (!response.ok) throw new Error('媒体加载失败');
|
|
164
|
-
return response.blob();
|
|
165
|
-
},
|
|
166
|
-
}}
|
|
167
|
-
/>
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
同一编辑器实例会复用 Blob、Object URL、波形、帧预览和元数据缓存;卸载时会中止未完成请求并释放 Object URL。不同编辑器实例之间不会共享这些资源。
|
|
111
|
+
import { useState } from "react";
|
|
112
|
+
import { EaseCut } from "easecut-react";
|
|
113
|
+
import "easecut-react/styles.css";
|
|
171
114
|
|
|
172
|
-
|
|
115
|
+
export function App() {
|
|
116
|
+
const [open, setOpen] = useState(true);
|
|
173
117
|
|
|
174
|
-
|
|
118
|
+
if (!open) return null;
|
|
175
119
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
保持音调的预览处理使用 `@soundtouchjs/audio-worklet`,音频节点、处理器和
|
|
180
|
-
`AudioContext` 均随编辑器预览实例创建并在卸载时释放。
|
|
181
|
-
|
|
182
|
-
## 导出
|
|
183
|
-
|
|
184
|
-
- 标题栏“导出”菜单中的“导出 JSON”下载当前 `CompositionExportPayload`。
|
|
185
|
-
- “导出到本地”在传入 `onExport` 后调用该回调,并提供最新 `draft` 和 `payload`;未传入时保留静态菜单项。
|
|
186
|
-
- EaseCut React 不包含 MP4 编码器或渲染后端。
|
|
187
|
-
- 可使用 `createCompositionExportPayload(draft)` 在组件外创建同样的导出数据。
|
|
188
|
-
- `CompositionExportPayload.Track` 与草稿采用相同的从下到上顺序:
|
|
189
|
-
`Track[0]` 是最低层,最后一个 Track 是最高层。
|
|
190
|
-
- `CompositionExportPayload.Duration` 是包含隐藏尾段在内的项目总时长,单位为毫秒;
|
|
191
|
-
隐藏 clip 不会出现在 `Track` 中,因此对应区间输出黑画面或静音。
|
|
192
|
-
- 每个音视频导出元素都会包含 `{ Type: 'speed', Speed }`。视频过滤器顺序为
|
|
193
|
-
`trim → speed → transform → a_volume`,音频过滤器顺序为
|
|
194
|
-
`a_volume → trim → speed`。
|
|
195
|
-
- 图片导出元素包含 `Type: 'image'`、`Source`、`TargetTime` 和唯一的
|
|
196
|
-
`transform` Extra,不包含 `trim`、`speed` 或 `a_volume`。
|
|
197
|
-
- 文字导出元素包含 `Type`、`TargetTime`、`Text`、`FontType`、`FontSize`、`FontColor`、`Bold`、`Italic`、`Underline` 以及唯一的 `transform` Extra;三个样式字段始终显式输出布尔值,其中 Width、Height 来自自然尺寸测量,不包含 `AlignType` 或媒体专属的 `Source`、`trim`、`speed`、`a_volume`。
|
|
198
|
-
- 草稿只读取当前 schema v12,不迁移旧版本。
|
|
199
|
-
|
|
200
|
-
## 快捷键
|
|
120
|
+
return <EaseCut onClose={() => setOpen(false)} />;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
201
123
|
|
|
202
|
-
|
|
124
|
+
## 重要
|
|
203
125
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
| 复制片段 | `Ctrl/Cmd + C` |
|
|
209
|
-
| 粘贴片段 | `Ctrl/Cmd + V` |
|
|
210
|
-
| 分割片段 | `Ctrl/Cmd + B` |
|
|
211
|
-
| 删除片段 | `Backspace` |
|
|
212
|
-
| 播放/暂停 | `Space` |
|
|
213
|
-
| 缩放时间线 | `Ctrl + 滚轮` |
|
|
126
|
+
实在懒得写README了,还有好多操作API没列出来,功能还是比较全面的,很多东西都考虑到了,如果有疑惑的话请通过以下方式解决:
|
|
127
|
+
- 问你的AI,它知道怎么做
|
|
128
|
+
- EaseCut\docs\npm-component-usage.md 有较为详细的使用说明
|
|
129
|
+
- 如果确实是EaseCut没做好,请提交Issues
|
|
214
130
|
|
|
215
|
-
|
|
131
|
+
**官方文档在制作路上...**
|
|
216
132
|
|
|
217
|
-
|
|
218
|
-
- 远程媒体必须允许 CORS,并使用浏览器支持的封装和编解码格式。
|
|
219
|
-
- 音频波形的 WebCodecs 加速需要安全上下文和浏览器可用的音频解码器;不满足时会使用 `AudioContext.decodeAudioData()`。
|
|
220
|
-
- Canvas 预览是编辑体验,不等同于最终离线渲染结果。
|
|
133
|
+
**也欢迎任何人进行贡献**
|
|
221
134
|
|
|
222
|
-
##
|
|
135
|
+
## 许可证
|
|
223
136
|
|
|
224
|
-
|
|
137
|
+
基于 MIT 许可证发布。
|
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "easecut-react",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "独立、可嵌入的 React
|
|
5
|
+
"description": "独立、可嵌入的 React 视频编辑器",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/你的用户名/你的仓库名.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://underhear.github.io/EaseCut/",
|
|
7
12
|
"main": "./dist/index.js",
|
|
8
13
|
"module": "./dist/index.js",
|
|
9
14
|
"types": "./dist/types/index.d.ts",
|