akari-video 0.1.38 → 0.1.40
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/package.json +1 -1
- package/vendor/docs/contract-2026-08-28-gpu-export-v0.md +27 -2
- package/vendor/docs/contract-2026-08-28-osr-export-v0.md +11 -0
- package/vendor/packages/akari-launcher/package.json +1 -1
- package/vendor/packages/edit-lint/src/edit-lint.mjs +19 -0
- package/vendor/packages/edit-store/lib/caption-store.js +39 -1
- package/vendor/packages/overlay-runtime/README.md +10 -2
- package/vendor/packages/overlay-runtime/package.json +2 -2
- package/vendor/packages/preview-server/package.json +1 -1
- package/vendor/skills/overlay-authoring/3d.md +15 -0
- package/vendor/skills/overlay-authoring/motion.md +33 -8
- package/vendor/skills/overlay-authoring/telop.md +78 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -123,11 +123,16 @@ cuts と layers が同時に空のフレームは、出力解像度の黒 1 枚
|
|
|
123
123
|
スプライト合成は通常どおりこの黒い frame-engine canvas の上へ重ねる。
|
|
124
124
|
|
|
125
125
|
3D は engine の時計から得た local seconds を `threeRuntime.render(container, t)` へ直接渡して駆動する。
|
|
126
|
-
GPU 出口は overlay sheet の `__akariSeek` を使用しない。毎コマの DOM animation
|
|
127
|
-
visibility
|
|
126
|
+
GPU 出口は overlay sheet の `__akariSeek` を使用しない。毎コマの DOM animation 同期と全 container の
|
|
127
|
+
visibility 更新を 3D canvas の texture 更新へ持ち込まないためである。sheet の
|
|
128
128
|
`__akariReady` は起動時に 1 回だけ待ち、各 scene が ready でない場合は overlay id と状態を示して
|
|
129
129
|
fail-closed にする。active 区間は最終 compositor の draw へ積むかどうかで決める。
|
|
130
130
|
|
|
131
|
+
**2026-09-04 改訂(issue #53)**: video seek 待ちだけは例外とし、sheet が公開する `__akariSeekVideos(seconds)`
|
|
132
|
+
(`__akariSeek` から切り出した video 部分・OSR と同一実装)を毎コマ、3D 描画の前に呼ぶ。3D 断片の動画テクスチャは
|
|
133
|
+
`<video>` の提示フレームから上がるため、シーク → 提示確定 → 3D 描画 の順序が必要で(`3d.md`)、呼ばないと
|
|
134
|
+
GPU 経路の動画テクスチャは起動時の 0 秒の絵に固定される。シートに `<video>` が無ければ呼ばない。
|
|
135
|
+
|
|
131
136
|
## 4. 読み戻しゼロ
|
|
132
137
|
|
|
133
138
|
製品実行経路は GPU frame surface を CPU へ読む API を使用しない。静的監査は
|
|
@@ -187,6 +192,26 @@ software MP4 SHA はエンコーダが決定論的な場合だけ必須とし、
|
|
|
187
192
|
GPU と OSR の decode 比較は、engine-only 区間の per-frame MAD 1.0 以下、字幕 cue の代表 5 時刻の
|
|
188
193
|
下半分 MAD 1.0 以下、3D 区間 MAD 1.0 以下を固定閾値とする。
|
|
189
194
|
|
|
195
|
+
**2026-09-04 追加(issue #53)— 2 経路で同じでなければならない 4 点**:
|
|
196
|
+
|
|
197
|
+
1. **overlay へ渡す時刻は `frameNumber / fps`**。µs へ丸めてはならない。overlay の `start` は必ず
|
|
198
|
+
`atFrames / fps` なので、丸めると比較が 1 ulp で反転し、カット境界の 1 コマだけ絵が食い違う。
|
|
199
|
+
この `seconds` は時間窓判定・CSS アニメ位相・item keyframes のフレーム番号すべてに流れる。
|
|
200
|
+
2. **時間窓の外の container も毎コマ pause して `currentTime` を書く**。飛ばすと窓の外の断片の CSS アニメが
|
|
201
|
+
壁時計(書き出しは分単位)で走り切り、`animation-fill-mode: both/forwards` の最終姿勢に張り付いたまま
|
|
202
|
+
窓へ入ってくる = 同じ時刻でも直前に何を撮ったかで絵が変わる。OSR の `__akariSyncAnimations` は
|
|
203
|
+
active 判定を持たない。
|
|
204
|
+
3. **DOM ステージのルートに `data-no-timeline`**。断片の規約は
|
|
205
|
+
`[data-akari-active] .x, [data-no-timeline] .x { animation: … }` の 2 アームで、OSR のシートは `#stage` に
|
|
206
|
+
これを持つ。GPU 側に無いと no-timeline アームだけで宣言した断片が GPU でのみ動かない。
|
|
207
|
+
4. **静的スプライトは overlay の `transform` を落とさない**。`.akari-sprite-root` に OSR の
|
|
208
|
+
`.akari-overlay-container` と同じ `translate/scale/rotate` + `transform-origin: center` を宣言する
|
|
209
|
+
(`role: "background"` は両経路とも恒等固定)。
|
|
210
|
+
|
|
211
|
+
あわせて、manifest 生成時に overlay の `start` / `duration` が有限数でなければ fail-closed とする。
|
|
212
|
+
既定値(`?? 0` / `?? duration`)を置くと、欠けたときに「OSR は絶対に出さない・GPU は全尺出す」という
|
|
213
|
+
最悪の非対称になる(OSR は `formatNumber(undefined)` が `"NaN"` を書き、`seconds >= NaN` が常に偽になる)。
|
|
214
|
+
|
|
190
215
|
## 7. receipt
|
|
191
216
|
|
|
192
217
|
`.akari/render.json` は `provenance.engine = "gpu"` と GPU receipt を持つ。GPU receipt は少なくとも
|
|
@@ -117,6 +117,17 @@ ffprobe timeoutは `max(120000, frames × 100)` msとする。尺、フレーム
|
|
|
117
117
|
- `AKARI_OSR_MEMORY_WARN_MIB` / `AKARI_OSR_MEMORY_HARD_STOP_MIB`で正の整数MiBへ上書きでき(絶対値・スケールも下限も上限も受けない)、
|
|
118
118
|
適用値はwarning < hard stopを必須とする。hard stop だけを上書きし既定 warning がそれ以上になるときは warning を hard stop の 75% に追従させる。
|
|
119
119
|
同じ変数を GPU 直結出口(gpu-export)も読む。
|
|
120
|
+
- 書き出しは厳密に前方順で過去フレームを読み直さないため、**評価 plan から外れたカットのデコーダセッションは解放する**
|
|
121
|
+
(`StreamReaper`。frame-engine が `plan.base` / `plan.layers` の `streamId` を集め、最後に使ったフレームから 1 秒ぶんの
|
|
122
|
+
猶予を過ぎたものを `LookaheadFrameSource.releaseStream` で落とす)。解放しないとカット本数ぶんのセッションが最後まで
|
|
123
|
+
積み上がり、RSS が単調に伸びて長尺ほど後ろで hard stop に当たる(2026-09-04 追加・issue #52。
|
|
124
|
+
244 秒 / 7,320 コマの実機報告で 98% 地点・RSS 4.01 GB)。トランジション中の送出カットは plan に載るので残る。
|
|
125
|
+
- receipt / run.json の `memory.decoderSessions` に生存セッション数(`live`)と累計解放数(`released`)を記録する。
|
|
126
|
+
RSS はセッション数に比例するため、ランプの原因を後から突き合わせられるようにする(同・issue #52。
|
|
127
|
+
#28 の時点で比例は分かっていたが記録が無く、再発時にまた手探りになった)。
|
|
128
|
+
- hard stop に当たった GPU 直結出口の失敗は reasonCode `memory-hard-stop` とし、`--engine auto` のときは OSR で
|
|
129
|
+
走り直して完走させる(`FALLBACK_REASONS`。同・issue #52。それまでは成果物ゼロで終わり、前版で出せていたものが
|
|
130
|
+
出せない退行になっていた)。`--engine gpu` 明示は従来どおり fail-closed。
|
|
120
131
|
- 並列予算1 worker = 1 GiBはGPU前提の値である。v0のworker数は1。
|
|
121
132
|
- 10秒ごとにRSSを記録し、ウィンドウ破棄後も採る。
|
|
122
133
|
- 固定Nコマごとのページ再生成は行わない。再生成を許すのはページ境界、renderer crash、watchdog回復時だけである。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akari-video",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "AKARI Video launcher CLI — start an AI-edited video project from any directory: scaffold, connection check, then hand over to Claude Code (or opencode). AKARI Video を opencode や Claude Code で、どのディレクトリからでも始めるための `akari` ランチャー CLI。接続確認(doctor)→ 未セットアップならプロジェクト雛形を作成 → AI エージェントを起動する。外部 npm 依存ゼロ(Node.js 組み込みモジュールのみ)。 [akari-video npm vendor: bin/akari.mjs is reference-only. These CLI entrypoints are not included in the akari-video npm package. Use `akari doctor --json` and run the path reported in `render_cut.path`. Full installations provide it in a monorepo checkout, ~/.akari/app, /Applications/AKARI Video.app/Contents/Resources/packages, or %LOCALAPPDATA%\\Programs\\@akari-videoshell\\resources\\packages.]",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -2339,6 +2339,25 @@ async function validateOverlays(overlays, timeline, findings, paths) {
|
|
|
2339
2339
|
path: relativePath(paths.projectRoot, htmlPath),
|
|
2340
2340
|
});
|
|
2341
2341
|
}
|
|
2342
|
+
// テキスト分割断片の CSS animation は [data-akari-active] ゲートの中で宣言する
|
|
2343
|
+
// (skills/overlay-authoring/telop.md「テキスト分割と stagger 規約」)。
|
|
2344
|
+
// getAnimations() のコストはドキュメント全体の animation 総数に比例するため、
|
|
2345
|
+
// ゲート無しの断片が 1 つでも混ざると全体の tick が落ちる。分割はその危険を
|
|
2346
|
+
// 分割数ぶんに増幅する(実測: 1,200 断片 × 8 分割 = 9,600 本で 221ms/tick。
|
|
2347
|
+
// akari-video-internal contract-2026-08-15-telop-motion-grammar-v0 §6)。
|
|
2348
|
+
if (/\bdata-akari-split\s*=/.test(html) && /(^|[^-\w])animation\s*:/.test(html)) {
|
|
2349
|
+
const gated = /\[data-akari-active\][^{}]*\.[^{}]*\{[^{}]*animation\s*:/.test(html);
|
|
2350
|
+
if (!gated) {
|
|
2351
|
+
addFinding(findings, {
|
|
2352
|
+
severity: "error",
|
|
2353
|
+
check: "overlays.split-animation-gate",
|
|
2354
|
+
message:
|
|
2355
|
+
"text-split fragment must declare animations under a [data-akari-active] selector",
|
|
2356
|
+
path: relativePath(paths.projectRoot, htmlPath),
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2342
2361
|
for (const [attribute, expected] of [
|
|
2343
2362
|
["data-start", overlay.start],
|
|
2344
2363
|
["data-duration", overlay.duration],
|
|
@@ -296,7 +296,10 @@ function updateCaptionStylePresetInSource(source, captionIds, presetId) {
|
|
|
296
296
|
changed++;
|
|
297
297
|
continue;
|
|
298
298
|
}
|
|
299
|
-
|
|
299
|
+
const shadowed = shadowedPresetStyleKeys(presetId, record.text_style);
|
|
300
|
+
// 同じテンプレの再適用でも、そのテンプレを覆い隠している字幕個別の指定が残っていれば
|
|
301
|
+
// 掃除する仕事が残っている(「変更はありません」で終わらせない)。
|
|
302
|
+
if (hasPreset && record.style_preset === presetId && shadowed.length === 0)
|
|
300
303
|
continue;
|
|
301
304
|
let nextElement;
|
|
302
305
|
if (hasPreset) {
|
|
@@ -317,11 +320,46 @@ function updateCaptionStylePresetInSource(source, captionIds, presetId) {
|
|
|
317
320
|
+ element.text.slice(textStyle.start);
|
|
318
321
|
}
|
|
319
322
|
}
|
|
323
|
+
nextElement = pruneShadowedTextStyle(nextElement, shadowed, captionId);
|
|
320
324
|
output = replaceElement(output, array.openIndex + 1, element, nextElement);
|
|
321
325
|
changed++;
|
|
322
326
|
}
|
|
323
327
|
return { source: output, changed };
|
|
324
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* そのテンプレが決めるツマミのうち、字幕個別の text_style が上書きしてしまっているキーを挙げる。
|
|
331
|
+
*
|
|
332
|
+
* 合成規則は `{ ...presetStyle, ...text_style }`(caption-style-preset.ts)で **字幕側が強い**。
|
|
333
|
+
* そのため text_style に既定値が丸ごと書かれていると、テンプレを当てても見た目が変わらない
|
|
334
|
+
* (オーナー報告 2026-09-04:「ニュース帯だけ効く」= ニュース風の background だけが text_style に
|
|
335
|
+
* 無いツマミだった)。テンプレを選ぶ操作は「このツマミはテンプレに任せる」という意思表示なので、
|
|
336
|
+
* 適用時に該当キーを落としてテンプレを表に出す。テンプレが決めないツマミ(ドラッグした position /
|
|
337
|
+
* zone / max_characters など)は字幕個別の指定として残す。
|
|
338
|
+
*/
|
|
339
|
+
function shadowedPresetStyleKeys(presetId, textStyle) {
|
|
340
|
+
const preset = Object.prototype.hasOwnProperty.call(textstyle_catalog_1.TEXTSTYLE_CATALOG, presetId)
|
|
341
|
+
? textstyle_catalog_1.TEXTSTYLE_CATALOG[presetId] : undefined;
|
|
342
|
+
if (!preset || textStyle === null || typeof textStyle !== 'object' || Array.isArray(textStyle)) {
|
|
343
|
+
return [];
|
|
344
|
+
}
|
|
345
|
+
const style = textStyle;
|
|
346
|
+
return Object.keys(preset.style)
|
|
347
|
+
.filter(key => Object.prototype.hasOwnProperty.call(style, key));
|
|
348
|
+
}
|
|
349
|
+
/** text_style から指定キーを取り除く。空になったら text_style ごと落とす。 */
|
|
350
|
+
function pruneShadowedTextStyle(element, keys, captionId) {
|
|
351
|
+
if (keys.length === 0) {
|
|
352
|
+
return element;
|
|
353
|
+
}
|
|
354
|
+
const located = locateTopLevelObjectProperty(element, 'text_style', `字幕 ${captionId}`);
|
|
355
|
+
let textStyle = located.text;
|
|
356
|
+
for (const key of keys) {
|
|
357
|
+
textStyle = removeObjectProperty(textStyle, key);
|
|
358
|
+
}
|
|
359
|
+
return Object.keys(JSON.parse(textStyle)).length === 0
|
|
360
|
+
? removeObjectProperty(element, 'text_style')
|
|
361
|
+
: element.slice(0, located.start) + textStyle + element.slice(located.end);
|
|
362
|
+
}
|
|
325
363
|
function insertCaptionLine(source, caption) {
|
|
326
364
|
const parsed = parseCaptions(source);
|
|
327
365
|
if (!normalizeCaption(caption)) {
|
|
@@ -53,8 +53,12 @@ Three.js + glTF シーンを決定的な時刻で描画し(`three-runtime.js`
|
|
|
53
53
|
自動でこの順に埋め込む — 同じ順序をホストの `<script>` タグでも守ること)。ランタイム読込後、
|
|
54
54
|
`font` 省略を許すホストは mount より前に
|
|
55
55
|
`window.akari.threeRuntime.configure({ defaultFontUrl })` を 1 回呼ぶ
|
|
56
|
-
4.
|
|
57
|
-
|
|
56
|
+
4. テキスト分割断片(`data-akari-split`)を扱うホストは、`src/interaction.js` より前に
|
|
57
|
+
`src/vendor/budoux-ja-bundle.js` → `src/text-split.js` の順で読み込む。
|
|
58
|
+
未読込でも他機能は動くが、日本語の文節分割が精度の落ちる近似になり
|
|
59
|
+
(実測 85% → 65%)、編集時の畳み/再分割も働かない
|
|
60
|
+
5. `src/motion-vocab.css`(イージング語彙 + 対象別既定尺の単一定義。断片の `var(--ease-*)` / `var(--anim-duration-*)` の解決先)と `src/interaction.css`・`src/minimap.css` を `<link>` する
|
|
61
|
+
6. edit.json ロード後、`window.akari.runtime.mount(summary)` を呼ぶ
|
|
58
62
|
(`summary` = `EditSummary`。下記参照)。以降はタイムライン更新のたびに
|
|
59
63
|
`window.akari.runtime.tick(t, playing)` を呼ぶ
|
|
60
64
|
|
|
@@ -238,11 +242,15 @@ src/
|
|
|
238
242
|
vendor/opentype.js-LICENSE.txt opentype.js の MIT License
|
|
239
243
|
vendor/matter-js-LICENSE.txt matter-js の MIT License
|
|
240
244
|
vendor/poly-decomp-LICENSE.txt poly-decomp の MIT License
|
|
245
|
+
vendor/budoux-ja-bundle.js BudouX(日本語の表示単位分割)の単一 IIFE
|
|
246
|
+
vendor/budoux-LICENSE.txt BudouX の Apache License 2.0
|
|
247
|
+
text-split.js data-akari-split の分割 / 畳み / 再分割(v0.5.0〜)
|
|
241
248
|
three-runtime.js 宣言型 3D scene の load / setTime / render / dispose
|
|
242
249
|
viewport-units.js 断片 CSS の vw/vh 系単位をステージ(出力サイズ)基準へ書き換え
|
|
243
250
|
overlay-runtime.js DOM mount/tick と 3D 可視ライフサイクル
|
|
244
251
|
interaction.js legacy ui/interaction.js を無改変移送
|
|
245
252
|
interaction.css legacy ui/interaction.css を無改変移送
|
|
253
|
+
motion-vocab.css イージング語彙 + 対象別既定尺(正典。skills/overlay-authoring/motion.md 参照)
|
|
246
254
|
minimap.js legacy ui/minimap.js を無改変移送
|
|
247
255
|
minimap.css legacy ui/style.css 206〜234 行(#minimap ブロック)を抽出
|
|
248
256
|
docs/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akari-video/overlay-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Shell-agnostic overlay DOM runtime: mount/tick of edit.json overlays (#overlay-stage), pointer-driven select/drag/resize/edit interaction layer, and zoom minimap. Host-agnostic — see README.md for the window.akari.* adapter contract a hosting shell must provide.",
|
|
6
6
|
"main": "src/overlay-runtime.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"README.md"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"check": "node --check src/parts.mjs && node --check src/vendor/three-bundle.js && node --check src/vendor/vendor-3d-text-bundle.js && node --check src/three-runtime.js && node --check src/slot-params.js && node --check src/video-fx.js && node --check src/viewport-units.js && node --check src/overlay-runtime.js && node --check src/interaction.js && node --check src/minimap.js",
|
|
14
|
+
"check": "node --check src/parts.mjs && node --check src/vendor/three-bundle.js && node --check src/vendor/vendor-3d-text-bundle.js && node --check src/three-runtime.js && node --check src/slot-params.js && node --check src/video-fx.js && node --check src/viewport-units.js && node --check src/vendor/budoux-ja-bundle.js && node --check src/text-split.js && node --check src/overlay-runtime.js && node --check src/interaction.js && node --check src/minimap.js",
|
|
15
15
|
"test": "node --test test-harness/*.test.mjs"
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"build": "esbuild ../pen-visuals/src/index.ts --bundle --format=esm --outfile=public/pen-visuals.bundle.js --target=chrome122 --platform=browser && esbuild ../overlay-runtime/src/interaction.js --bundle --format=iife --outfile=public/overlay-interaction.bundle.js --target=chrome122 --platform=browser && esbuild ../overlay-runtime/src/interaction.css --bundle --outfile=public/overlay-interaction.css --target=chrome122 && esbuild ../edit-store/src/webview-kernel.ts --bundle --format=esm --outfile=public/edit-kernel.bundle.js --target=chrome122 --platform=browser && esbuild src/frame-engine-client.ts --bundle --format=esm --outfile=public/frame-engine.bundle.js --target=chrome122 --platform=browser",
|
|
7
|
+
"build": "esbuild ../pen-visuals/src/index.ts --bundle --format=esm --outfile=public/pen-visuals.bundle.js --target=chrome122 --platform=browser && esbuild ../overlay-runtime/src/interaction.js --bundle --format=iife --outfile=public/overlay-interaction.bundle.js --target=chrome122 --platform=browser && esbuild ../overlay-runtime/src/interaction.css --bundle --outfile=public/overlay-interaction.css --target=chrome122 && esbuild ../overlay-runtime/src/motion-vocab.css --bundle --outfile=public/overlay-motion-vocab.css --target=chrome122 && esbuild ../edit-store/src/webview-kernel.ts --bundle --format=esm --outfile=public/edit-kernel.bundle.js --target=chrome122 --platform=browser && esbuild src/frame-engine-client.ts --bundle --format=esm --outfile=public/frame-engine.bundle.js --target=chrome122 --platform=browser",
|
|
8
8
|
"pretest": "npm --prefix ../edit-store run build && npm --prefix ../../apps/shell/extensions/akari-preview run build && npm run build",
|
|
9
9
|
"test": "node --test test/*.test.mjs",
|
|
10
10
|
"test:frame-engine-browser": "node --test test/frame-engine-preview-browser.l1.mjs",
|
|
@@ -118,6 +118,21 @@ fragment は単一ルートとし、透明 canvas、任意の静的 fallback、
|
|
|
118
118
|
clip を分けるので、1 個のモデルに複数の動きがあるときは `"*"` で束ねる(1 本しか再生しないと
|
|
119
119
|
片方しか動かない)。存在しない clip 名を書いた場合はエラーになる。
|
|
120
120
|
- `materialOverrides` は `{ "<material 名>": { "texture": "<画像または動画の相対パス>" } }` の形で、名前が一致するマテリアルの `emissiveMap` を差し替える。**差し替え先が `emissiveMap` である以上、貼り先の材質が発光しない(glTF の `emissiveFactor` が未設定 = 黒)と「0 × テクスチャ」で何も出ない**(2026-08-14 実害。詳細は後述「発光しない材質には貼れない」)。edit.json のあるディレクトリからの相対 PNG / JPEG / WebP 等、または MP4 / MOV / WebM を指定し、URL や CDN を書かない。該当するマテリアル名がモデル内にない場合は警告して無視される。
|
|
121
|
+
- `textureVar` は画面に映すものをツマミ(CSS カスタムプロパティ)にする任意キー。`"--screen-src"` のような変数名を書き、overlay の `vars` でその変数に相対パスを入れると `texture` の代わりに差し込まれる。空・未設定なら `texture` がそのまま使われる。
|
|
122
|
+
- `textureVar` を使う場合も `texture` は実在する相対パスのままにする。書き出し前の宣言済み入力検査が実ファイルを要求するため、`texture` に `var(--screen-src)` を直接書く形はライブプレビューでは動くが書き出しでは失敗する。
|
|
123
|
+
- `brightness` は任意の発光倍率(0〜4、既定 1)で、差し替えたテクスチャの `emissiveIntensity` に掛かる。`var(--screen-brightness)` のような CSS 変数も書き出しを含めて使える。既定の 1 のときは `emissiveIntensity` に触れず、既存宣言の見た目を維持する。
|
|
124
|
+
- ツマミへ結線する宣言例:
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"materialOverrides": {
|
|
128
|
+
"ScreenMaterial": {
|
|
129
|
+
"texture": "placeholder.png",
|
|
130
|
+
"textureVar": "--screen-src",
|
|
131
|
+
"brightness": "var(--screen-brightness)"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
121
136
|
- **パスは「edit.json のあるディレクトリ」から。断片の場所からの相対ではない。** `model` も同様。
|
|
122
137
|
断片を `overlays/3d-phone/fragment.html` へ置いたなら `"overlays/3d-phone/model.glb"` と書く
|
|
123
138
|
(`"model.glb"` はプロジェクト直下を探して ENOENT になる)。
|
|
@@ -16,15 +16,40 @@
|
|
|
16
16
|
|
|
17
17
|
## イージング語彙
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
| 入場・減速して止まる | `ease-out` | 画面外から定位置へ入る要素 |
|
|
23
|
-
| 退場・加速して去る | `ease-in` | 定位置から画面外へ出る要素 |
|
|
24
|
-
| 姿勢 A と B の往復 | `ease-in-out` | カード反転、視線移動、穏やかな遷移 |
|
|
25
|
-
| 離散切替 | `steps()` | カウンタの桁、LED、コマ送り風表現 |
|
|
19
|
+
数値の `cubic-bezier()` を断片に直書きしない。ランタイムの語彙
|
|
20
|
+
(`packages/overlay-runtime/src/motion-vocab.css`。ホストが読み込み、書き出し側は
|
|
21
|
+
rasterize.mjs が同じ内容をシートへ埋め込む)を **名前で** 呼ぶ。
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
| 変数 | 質感 | 使いどころ |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `--ease-smooth` | **既定**。すっと出て長く減速 | 入場・移動・サイズ変化の第一候補。未指定の `var(--anim-easing)` はこれになる |
|
|
26
|
+
| `--ease-natural` | 中庸の立ち上がり + 長い減速の尾 | リストの送り、続きものの移動 |
|
|
27
|
+
| `--ease-slowdown` | 初速最大 → 匍匐して止まる | 勢いから静止へ。強い ease-out |
|
|
28
|
+
| `--ease-accelerate` | 深い溜め → 加速して去る | 退場。**直後にカットが来る前提**の動き |
|
|
29
|
+
| `--ease-overshoot` | バネ。目標を大きく越えてから戻る(約 +90%) | 強調の飛び込み・ポップイン |
|
|
30
|
+
| `--ease-overshoot-mid` / `--ease-overshoot-soft` | 同じバネの控えめ段(約 +66% / +32%) | 移動量がやや大きいときの逃がし先 |
|
|
31
|
+
| `--ease-impulse` | 突進して小さく上振れ | 高速スライドイン |
|
|
32
|
+
| `--ease-linear` | 等速 | 進捗・連続回転など、速度変化に意味がない動き |
|
|
33
|
+
| `--ease-snap-out` / `--ease-hard-out` / `--ease-deep-inout` / `--ease-snappy` | 切れ味系の補助語彙 | テロップの歯切れを出したいとき(telop.md) |
|
|
34
|
+
|
|
35
|
+
`--anim-duration` に入れる尺の相場も対象別の変数がある:
|
|
36
|
+
|
|
37
|
+
| 変数 | 値 | 対象 |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `--anim-duration-telop` | 500ms | テロップ(1 文字 / 1 文節あたり) |
|
|
40
|
+
| `--anim-duration-shape` | 800ms | 図形・帯・カード |
|
|
41
|
+
| `--anim-duration-group` | 1000ms | ラッパ `<div>` ごと動かすとき |
|
|
42
|
+
|
|
43
|
+
**overshoot / impulse は「小さい移動・スケールのポップ」専用**。越え量は移動量に
|
|
44
|
+
**比例**するため、画面幅級の搬入(数百 px の translate)に掛けると目標を数百 px
|
|
45
|
+
突き抜けて画面外へ飛ぶ。長距離の搬入は `--ease-smooth` / `--ease-natural`、退場は
|
|
46
|
+
`--ease-accelerate`、ポップ強調(scale 0.7→1、数十 px の移動)にだけ overshoot 系を使う。
|
|
47
|
+
どうしても大きめの移動で使うときは `-mid` / `-soft` へ落とす。
|
|
48
|
+
|
|
49
|
+
CSS 標準キーワード(`ease-out` 等)より上記の語彙を優先する。離散切替(カウンタの桁・
|
|
50
|
+
LED・コマ送り風)だけは標準の `steps()` を使う。語彙で意図を表せない場合に限り
|
|
51
|
+
`cubic-bezier()` を新設し、CSS 変数化して理由を残す。標準キーワードの定義は
|
|
52
|
+
[W3C CSS Easing Functions Level 2](https://www.w3.org/TR/css-easing-2/) を参照する。
|
|
28
53
|
|
|
29
54
|
## compositor 合成の制約
|
|
30
55
|
|
|
@@ -92,6 +92,7 @@ YouTube の safe zone も全端末保証ではない。オーガニック投稿
|
|
|
92
92
|
|
|
93
93
|
- 字幕の出入りは短い opacity / translate を使い、保持中は動かさない。
|
|
94
94
|
- 1 文字ずつの出現は可読速度とシーク再現性を損ねやすい。必要な演出だけに限定し、文字 DOM は先に確定しておく。
|
|
95
|
+
日本語で時間差の出現をやるなら 1 文字ではなく**文節単位**にする(下の「テキスト分割と stagger 規約」)。
|
|
95
96
|
- CSS animation / WAAPI を使い、ランタイムが `currentTime = (t - start) * 1000` を設定できる形にする。
|
|
96
97
|
- 位置移動の transform は断片内の子要素へ付け、AKARI が所有する外側コンテナの幾何 transform と分離する。
|
|
97
98
|
|
|
@@ -108,6 +109,83 @@ OUT の `both`(= backwards fill)は**遅延中に OUT の開始値(`opacit
|
|
|
108
109
|
- チェック: 断片の冒頭数フレームをシークし、IN の開始値(opacity 0 / 画面外)から始まることを目視する
|
|
109
110
|
- この罠は IN/OUT の 2 段に限らず一般化できる(3 段以上の連鎖、点滅ループの片端省略など)。`motion.md`「複数アニメーションを同一プロパティへ連鎖させるときの暗黙 0% 上書き」を参照
|
|
110
111
|
|
|
112
|
+
## テキスト分割と stagger 規約(2026-08-15)
|
|
113
|
+
|
|
114
|
+
文字・単語・文節ごとに時間差で出す演出は、**断片が分割済みで出荷し、
|
|
115
|
+
stagger は CSS の `calc()` で表現する**。断片に `<script>` は書かない。
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<div class="foo__line" data-akari-split="bunsetsu"
|
|
119
|
+
><span class="akari-u" style="--i:0">今日は</span
|
|
120
|
+
><span class="akari-u" style="--i:1">とても</span
|
|
121
|
+
><span class="akari-u" style="--i:2">いい</span
|
|
122
|
+
><span class="akari-u" style="--i:3">天気ですね</span></div>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```css
|
|
126
|
+
.foo__line{
|
|
127
|
+
--anim-duration: var(--anim-duration-telop, 500ms); /* 1 要素の尺(対象別の相場は motion.md) */
|
|
128
|
+
--anim-stagger: 150ms; /* ずらし */
|
|
129
|
+
--anim-easing: var(--ease-snap-out); /* 語彙は motion.md「イージング語彙」。未指定なら smooth */
|
|
130
|
+
}
|
|
131
|
+
/* ★ [data-akari-active] ゲートの中で宣言する(下の「性能」参照) */
|
|
132
|
+
[data-akari-active] .foo__line .akari-u{
|
|
133
|
+
animation: foo__in var(--anim-duration) var(--anim-easing) both paused;
|
|
134
|
+
animation-delay: calc(var(--i) * var(--anim-stagger));
|
|
135
|
+
}
|
|
136
|
+
@keyframes foo__in{
|
|
137
|
+
from{ opacity:0; transform: translateY(var(--anim-distance, 50px)); }
|
|
138
|
+
to { opacity:1; transform: none; }
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- **`animation-delay: calc(var(--i) * var(--anim-stagger))` の 1 行がすべて**。
|
|
143
|
+
どの `@keyframes`(= どの演出)にも同じ形で stagger が掛かる。
|
|
144
|
+
演出ごとに遅延を書き並べない
|
|
145
|
+
- **ツマミは CSS 変数**にする。`edit.json` の `vars` から上書きできる
|
|
146
|
+
- **`--i` は 0 始まりの通し番号**。`--n` に総数が入る(ランタイムが振る)
|
|
147
|
+
|
|
148
|
+
### 分割単位(`data-akari-split`)
|
|
149
|
+
|
|
150
|
+
| 値 | 単位 | 使いどころ |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `bunsetsu` | 文節(日本語の表示単位) | **日本語テロップの既定** |
|
|
153
|
+
| `chars` | 1 文字 | 演出用。可読速度を損ねるので多用しない |
|
|
154
|
+
| `words` | 単語 | 欧文向け。**日本語では分かち書きしないので機能しない** |
|
|
155
|
+
| `lines` | 行 | 行単位で送る |
|
|
156
|
+
| `none` | 分割しない | — |
|
|
157
|
+
|
|
158
|
+
日本語の文節分割は BudouX(`src/vendor/budoux-ja-bundle.js`・Apache-2.0)で行う。
|
|
159
|
+
`Intl.Segmenter` 単体の単語分割は助詞がバラけるため使わない
|
|
160
|
+
(`今日 | は | とても | いい | 天気 | です | ね` になる)。
|
|
161
|
+
|
|
162
|
+
### ランタイムの担当(断片は書かなくてよい)
|
|
163
|
+
|
|
164
|
+
`data-mirror="text"` と同じく、DOM 操作はランタイムが持つ:
|
|
165
|
+
|
|
166
|
+
- **mount 時**: 宣言はあるが未分割の要素を分割する(出荷漏れの安全網・冪等)
|
|
167
|
+
- **編集開始時**: 分割を素のテキストへ畳む(`<span>` のまま contenteditable にすると
|
|
168
|
+
打鍵で span が割れる・キャレットが単位境界で飛ぶ)
|
|
169
|
+
- **編集確定時**: 確定したテキストで分割し直し、`--i` を振り直す。
|
|
170
|
+
保存される HTML は**分割済みの状態**(書き出しは断片の HTML をそのまま使うため)
|
|
171
|
+
|
|
172
|
+
必要ランタイム: **0.5.0 以降**。素材の `meta.json` に
|
|
173
|
+
`min_overlay_runtime_version: "0.5.0"` を宣言する。
|
|
174
|
+
|
|
175
|
+
### 性能 — `[data-akari-active]` ゲートは必須(実測)
|
|
176
|
+
|
|
177
|
+
分割は 1 断片の CSS animation を分割数ぶんに増やす。ゲートが無いと即死する:
|
|
178
|
+
|
|
179
|
+
| 条件 | 現存 animation | 1 tick |
|
|
180
|
+
|---|---:|---:|
|
|
181
|
+
| ゲート有り・1,200 断片 × 16 分割・可視 60 | 960 | **0.023ms** |
|
|
182
|
+
| ゲート無し・1,200 断片 × 8 分割 | 9,600 | **221ms** |
|
|
183
|
+
| ゲート無し・上記 + 可視 60 | 9,600 | **11,783ms** |
|
|
184
|
+
|
|
185
|
+
`getAnimations()` のコストは「ドキュメント全体に現存する CSS animation の総数」に
|
|
186
|
+
比例する。ゲートの中で宣言すれば非可視分は現存しないので、**分割そのものは無害**。
|
|
187
|
+
ゲートを忘れた断片が 1 つあるだけで全体が落ちる。
|
|
188
|
+
|
|
111
189
|
## 多層テキスト断片と data-mirror 規約(2026-08-06)
|
|
112
190
|
|
|
113
191
|
同一テキストを太さ違いの `-webkit-text-stroke` 等で複数層重ねる断片(多重縁取り・ずらし影・裏打ち・二段押し出し等)は、編集対象を 1 層に決めないと打ち替えが層間でズレる。層間の同期は overlay-runtime(`packages/overlay-runtime/`)側の機能で行う。
|