pa_encoder 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +83 -135
- package/package.json +2 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yongpa Han
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,191 +1,139 @@
|
|
|
1
|
-
|
|
1
|
+
HTML canvas frame encoder made for personal use (ツ)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Live(실시간) 캡처와 Frame(결정적/정밀) 캡처를 모두 지원하며, **DOM에 `<canvas>`가 존재하는 모든 웹 프로젝트**에서 사용할 수 있습니다.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
pa_encoder is an encoder that **extracts browser-based canvas sketches and animations into image frames (PNG)**.
|
|
10
|
-
|
|
11
|
-
It supports both **Live (realtime) capture** and **Frame (deterministic) capture**, and can be used with **any web project that renders to a DOM `<canvas>`**.
|
|
12
|
-
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
### 핵심 개념
|
|
16
|
-
|
|
17
|
-
- **Live mode (realtime)**
|
|
18
|
-
실제 브라우저 렌더 루프를 그대로 기록합니다. 마우스/키보드 등 **실시간 인터랙션**이 결과에 반영됩니다.
|
|
19
|
-
성능 저하나 프레임 드랍도 “있는 그대로” 기록됩니다.
|
|
20
|
-
|
|
21
|
-
- **Frame mode (deterministic)**
|
|
22
|
-
가상 시간(virtual time)을 사용해 `fps`와 `frames`에 맞춰 **프레임을 정확히 step**합니다.
|
|
23
|
-
`Date.now`, `performance.now`, `requestAnimationFrame`, timer를 훅하여 `deltaTime` 기반 애니메이션을 **정밀하게 재현**합니다.
|
|
24
|
-
동일한 설정/초기 상태에서 **항상 동일한 결과**를 목표로 합니다.
|
|
25
|
-
|
|
26
|
-
### 설치
|
|
3
|
+
# Installation
|
|
27
4
|
|
|
28
5
|
```bash
|
|
29
6
|
npm install pa_encoder
|
|
30
7
|
```
|
|
31
8
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
pnpm add pa_encoder
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### 빠른 시작
|
|
9
|
+
Provides:
|
|
39
10
|
|
|
40
|
-
|
|
11
|
+
- JavaScript API (ES module)
|
|
12
|
+
- CLI tool (`pa_encoder`)
|
|
41
13
|
|
|
42
|
-
|
|
43
|
-
npx pa_encoder
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
기본 가정:
|
|
14
|
+
---
|
|
47
15
|
|
|
48
|
-
|
|
49
|
-
- entry 파일: /src/main.js
|
|
16
|
+
# Usage
|
|
50
17
|
|
|
51
|
-
|
|
18
|
+
## CLI (recommended)
|
|
52
19
|
|
|
53
|
-
|
|
20
|
+
Starts a local proxy server and opens a browser UI.
|
|
54
21
|
|
|
55
22
|
```bash
|
|
56
|
-
|
|
23
|
+
pa_encoder --url http://localhost:5173 --entry /src/main.js
|
|
57
24
|
```
|
|
58
25
|
|
|
59
|
-
|
|
26
|
+
### Options
|
|
60
27
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
28
|
+
- `--url` Target site URL
|
|
29
|
+
- `--entry` Entry script imported in preview iframe
|
|
30
|
+
- `--port` UI server port (default: `8787`)
|
|
64
31
|
|
|
65
|
-
|
|
32
|
+
The UI shows a live preview, capture controls, and progress logs.
|
|
66
33
|
|
|
67
|
-
|
|
68
|
-
2. **Mode**를 선택합니다.
|
|
69
|
-
- Live: 실시간 인터랙션 포함 캡처
|
|
70
|
-
- Frame: fps/frames 기반 정밀 캡처
|
|
71
|
-
3. **Canvas**에서 캡처할 캔버스를 선택합니다(기본: auto).
|
|
72
|
-
4. **Start**로 시작, **Stop**으로 종료합니다.
|
|
34
|
+
---
|
|
73
35
|
|
|
74
|
-
|
|
36
|
+
## JavaScript API
|
|
75
37
|
|
|
76
|
-
|
|
77
|
-
- `H`: UI 숨김
|
|
78
|
-
- `P`: Passthrough 토글
|
|
79
|
-
- `C`: Compact 토글
|
|
80
|
-
- `D`: Dock 위치 변경
|
|
81
|
-
- `Space`: Start
|
|
82
|
-
- 녹화 **진행 중(Running)** 에는 UI가 키보드를 가로채지 않습니다.
|
|
83
|
-
즉, 키 입력이 스케치(캔버스) 쪽으로 들어가도록 설계되어 있습니다.
|
|
84
|
-
필요 시 `Focus Sketch` 버튼을 누른 뒤 캔버스를 한 번 클릭하면 포커스가 확실해집니다(브라우저 정책에 따라 자동 포커스가 제한될 수 있음).
|
|
38
|
+
### Live capture (real-time)
|
|
85
39
|
|
|
86
|
-
|
|
40
|
+
```js
|
|
41
|
+
import { startLiveCapture, createZipExporter } from "pa_encoder";
|
|
87
42
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- **Best (auto)**: 환경에 맞춰 ZIP 또는 FS 자동 선택
|
|
43
|
+
const canvas = document.querySelector("canvas");
|
|
44
|
+
const exporter = await createZipExporter({ zipName: "frames.zip" });
|
|
91
45
|
|
|
92
|
-
|
|
46
|
+
const { stop } = await startLiveCapture({
|
|
47
|
+
canvas,
|
|
48
|
+
exporter,
|
|
49
|
+
fps: 30,
|
|
50
|
+
});
|
|
93
51
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
오프스크린 결과를 화면 canvas로 출력하면 캡처 가능합니다.
|
|
97
|
-
- OffscreenCanvas + Worker 렌더링은 Live에서는 동작할 수 있으나, Frame에서 결정성을 보장하지 않습니다.
|
|
98
|
-
- cross-origin 이미지/비디오 등을 CORS 설정 없이 텍스처로 사용하면 canvas가 tainted 되어 `toBlob()` 캡처가 실패할 수 있습니다.
|
|
52
|
+
await stop();
|
|
53
|
+
```
|
|
99
54
|
|
|
100
55
|
---
|
|
101
56
|
|
|
102
|
-
###
|
|
57
|
+
### Deterministic frame capture (virtual time)
|
|
103
58
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
Performance drops and frame skips are captured as-is.
|
|
59
|
+
```js
|
|
60
|
+
import { virtualTimeCaptureFromStart, createZipExporter } from "pa_encoder";
|
|
107
61
|
|
|
108
|
-
|
|
109
|
-
Uses virtual time and advances frames in fixed steps according to `fps` and `frames`.
|
|
110
|
-
Hooks `Date.now`, `performance.now`, `requestAnimationFrame`, and timers to reproduce **deltaTime-based animation** precisely.
|
|
111
|
-
With the same code/settings/initial state, it aims to produce **reproducible output**.
|
|
62
|
+
const exporter = await createZipExporter({ zipName: "frames.zip" });
|
|
112
63
|
|
|
113
|
-
|
|
64
|
+
await virtualTimeCaptureFromStart({
|
|
65
|
+
fps: 60,
|
|
66
|
+
frameCount: 300,
|
|
67
|
+
start: async () => {
|
|
68
|
+
await import("/src/main.js");
|
|
69
|
+
},
|
|
70
|
+
onFrame: async (canvas, i) => {
|
|
71
|
+
const blob = await new Promise((r) => canvas.toBlob(r, "image/png"));
|
|
72
|
+
await exporter.write(i, blob);
|
|
73
|
+
},
|
|
74
|
+
});
|
|
114
75
|
|
|
115
|
-
|
|
116
|
-
npm install pa_encoder
|
|
76
|
+
await exporter.finalize();
|
|
117
77
|
```
|
|
118
78
|
|
|
119
|
-
|
|
79
|
+
Hooks `requestAnimationFrame`, time APIs, and timers to ensure deterministic output.
|
|
120
80
|
|
|
121
|
-
|
|
122
|
-
pnpm add pa_encoder
|
|
123
|
-
```
|
|
81
|
+
---
|
|
124
82
|
|
|
125
|
-
|
|
83
|
+
## Exporters
|
|
126
84
|
|
|
127
|
-
|
|
85
|
+
All exporters share the same interface:
|
|
128
86
|
|
|
129
|
-
```
|
|
130
|
-
|
|
87
|
+
```ts
|
|
88
|
+
{
|
|
89
|
+
write(frameIndex, blob);
|
|
90
|
+
finalize();
|
|
91
|
+
}
|
|
131
92
|
```
|
|
132
93
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
- dev server: http://localhost:5173
|
|
136
|
-
- entry file: /src/main.js
|
|
94
|
+
### ZIP exporter
|
|
137
95
|
|
|
138
|
-
|
|
96
|
+
```js
|
|
97
|
+
import { createZipExporter } from "pa_encoder";
|
|
98
|
+
await createZipExporter({ zipName: "frames.zip" });
|
|
99
|
+
```
|
|
139
100
|
|
|
140
|
-
|
|
101
|
+
Downloads a ZIP of PNG frames.
|
|
141
102
|
|
|
142
|
-
|
|
143
|
-
npx pa_encoder --url <dev_server_url> --entry <entry_path> --port <ui_port>
|
|
144
|
-
```
|
|
103
|
+
---
|
|
145
104
|
|
|
146
|
-
|
|
105
|
+
### File System exporter (Chromium)
|
|
147
106
|
|
|
148
|
-
```
|
|
149
|
-
|
|
107
|
+
```js
|
|
108
|
+
import { createFsExporter } from "pa_encoder";
|
|
109
|
+
await createFsExporter({ dirNameHint: "frames" });
|
|
150
110
|
```
|
|
151
111
|
|
|
152
|
-
|
|
112
|
+
Writes directly to a directory (secure context required).
|
|
153
113
|
|
|
154
|
-
|
|
155
|
-
2. Choose **Mode**:
|
|
156
|
-
- Live: realtime capture with interaction
|
|
157
|
-
- Frame: deterministic capture by fps/frames
|
|
158
|
-
3. Select **Canvas** (default: auto).
|
|
159
|
-
4. Click **Start** to begin and **Stop** to finish.
|
|
114
|
+
---
|
|
160
115
|
|
|
161
|
-
|
|
116
|
+
### Best exporter
|
|
162
117
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
- `D`: change dock position
|
|
168
|
-
- `Space`: start
|
|
169
|
-
- When **Running (recording)**, the UI does not consume keyboard events.
|
|
170
|
-
This is intended to keep keyboard input going to the sketch/canvas.
|
|
171
|
-
If focus is not acquired automatically (browser-dependent), click `Focus Sketch` and then click the canvas once.
|
|
118
|
+
```js
|
|
119
|
+
import { createBestExporter } from "pa_encoder";
|
|
120
|
+
await createBestExporter({ prefer: "fs" });
|
|
121
|
+
```
|
|
172
122
|
|
|
173
|
-
|
|
123
|
+
Uses File System export when available, otherwise ZIP.
|
|
174
124
|
|
|
175
|
-
|
|
176
|
-
- **FS (directory)**: writes files via the File System Access API
|
|
177
|
-
- **Best (auto)**: chooses ZIP or FS depending on the environment
|
|
125
|
+
---
|
|
178
126
|
|
|
179
|
-
|
|
127
|
+
## Supported Environments
|
|
180
128
|
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
|
|
184
|
-
- OffscreenCanvas
|
|
185
|
-
-
|
|
129
|
+
- Modern Chromium-based browsers recommended
|
|
130
|
+
- Requires:
|
|
131
|
+
- ES module Web Workers
|
|
132
|
+
- `OffscreenCanvas`
|
|
133
|
+
- `createImageBitmap`
|
|
134
|
+
- ZIP export works in most modern browsers
|
|
135
|
+
- File System export requires Chromium + secure context
|
|
186
136
|
|
|
187
137
|
---
|
|
188
138
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
MIT
|
|
139
|
+
(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)(ツ)
|