@plotdb/lotion 0.1.0 → 0.3.0
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/CHANGELOG.md +37 -0
- package/README.md +93 -27
- package/cli.js +157 -77
- package/index.css +12 -1
- package/index.js +185 -4
- package/index.min.css +1 -1
- package/index.min.js +1 -1
- package/package.json +1 -1
- package/prompt/{explainer.md → lotion.md} +7 -29
- package/vector.js +817 -0
- package/vector.min.js +1 -0
- package/audio/beats.py +0 -93
- package/audio/bgm.py +0 -181
- package/audio/common.py +0 -30
- package/audio/mix.py +0 -51
- package/audio/sfx.py +0 -93
- package/prompt/ui-loop.md +0 -497
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(function(){
|
|
2
|
-
var PI, sin, cos, exp, sqrt, round, clamp, lerp, ss, spring, presets, track, vis, bump, typing, hex, mixc, rgb, mk, put, txt, NS, svg, player, ref$, lotion;
|
|
2
|
+
var PI, sin, cos, exp, sqrt, round, clamp, lerp, ss, spring, presets, track, vis, bump, typing, hex, mixc, rgb, mk, put, txt, NS, svg, player, ref$, libs, libCache, lib, encode, lotion;
|
|
3
3
|
PI = Math.PI, sin = Math.sin, cos = Math.cos, exp = Math.exp, sqrt = Math.sqrt, round = Math.round;
|
|
4
4
|
clamp = function(v, a, b){
|
|
5
5
|
a == null && (a = 0);
|
|
@@ -338,6 +338,9 @@
|
|
|
338
338
|
});
|
|
339
339
|
}, ref$.fit = function(){
|
|
340
340
|
var w, h, s;
|
|
341
|
+
if (this.encoding) {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
341
344
|
w = this.viewport.clientWidth;
|
|
342
345
|
h = this.viewport.clientHeight;
|
|
343
346
|
if (!w || !h) {
|
|
@@ -349,7 +352,7 @@
|
|
|
349
352
|
return Math.floor(t / 60) + ":" + ("0" + Math.floor(t % 60)).slice(-2);
|
|
350
353
|
}, ref$.seek = function(t){
|
|
351
354
|
var p, ch;
|
|
352
|
-
if (!this.started) {
|
|
355
|
+
if (!this.started || this.encoding) {
|
|
353
356
|
return;
|
|
354
357
|
}
|
|
355
358
|
this.t = t = clamp(t, 0, this.duration);
|
|
@@ -382,7 +385,7 @@
|
|
|
382
385
|
}, ref$.play = function(go){
|
|
383
386
|
var this$ = this;
|
|
384
387
|
go == null && (go = true);
|
|
385
|
-
if (!this.started) {
|
|
388
|
+
if (!this.started || this.encoding) {
|
|
386
389
|
return;
|
|
387
390
|
}
|
|
388
391
|
if (go && this.t >= this.duration) {
|
|
@@ -409,7 +412,177 @@
|
|
|
409
412
|
(this.root.requestFullscreen || this.root.webkitRequestFullscreen).call(this.root);
|
|
410
413
|
}
|
|
411
414
|
return this.root.focus();
|
|
415
|
+
}, ref$.encode = function(opt){
|
|
416
|
+
var t0, tf, cover, bar, done, progress, ref$, this$ = this;
|
|
417
|
+
opt == null && (opt = {});
|
|
418
|
+
if (!this.started) {
|
|
419
|
+
return Promise.reject(new Error('[lotion] encode() before start()'));
|
|
420
|
+
}
|
|
421
|
+
if (this.encoding) {
|
|
422
|
+
return Promise.reject(new Error('[lotion] already encoding'));
|
|
423
|
+
}
|
|
424
|
+
this.pause();
|
|
425
|
+
t0 = this.t;
|
|
426
|
+
this.encoding = true;
|
|
427
|
+
this.root.classList.add('lotion-encoding');
|
|
428
|
+
tf = this.stage.style.transform;
|
|
429
|
+
this.stage.style.transform = 'none';
|
|
430
|
+
cover = mk('lotion-encoding-screen', '<div class="lotion-spinner"></div><div class="lotion-progress"></div>', this.viewport);
|
|
431
|
+
bar = cover.querySelector('.lotion-progress');
|
|
432
|
+
done = function(){
|
|
433
|
+
cover.remove();
|
|
434
|
+
this$.stage.style.transform = tf;
|
|
435
|
+
this$.encoding = false;
|
|
436
|
+
this$.root.classList.remove('lotion-encoding');
|
|
437
|
+
this$.fit();
|
|
438
|
+
return this$.seek(t0);
|
|
439
|
+
};
|
|
440
|
+
progress = function(v){
|
|
441
|
+
bar.textContent = Math.round(v * 100) + "%";
|
|
442
|
+
if (opt.progress) {
|
|
443
|
+
return opt.progress(v);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
return encode((ref$ = import$({
|
|
447
|
+
el: this.stage,
|
|
448
|
+
seek: this.opt.seek,
|
|
449
|
+
duration: this.duration,
|
|
450
|
+
width: this.width,
|
|
451
|
+
height: this.height
|
|
452
|
+
}, opt), ref$.progress = progress, ref$)).then(function(b){
|
|
453
|
+
done();
|
|
454
|
+
return b;
|
|
455
|
+
})['catch'](function(e){
|
|
456
|
+
done();
|
|
457
|
+
throw e;
|
|
458
|
+
});
|
|
412
459
|
}, ref$);
|
|
460
|
+
libs = {
|
|
461
|
+
snapdom: 'https://cdn.jsdelivr.net/npm/@zumer/snapdom@3.3.0/dist/snapdom.mjs',
|
|
462
|
+
mediabunny: 'https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/dist/bundles/mediabunny.min.mjs'
|
|
463
|
+
};
|
|
464
|
+
libCache = {};
|
|
465
|
+
lib = function(name){
|
|
466
|
+
var ref$;
|
|
467
|
+
return (ref$ = libCache[name]) != null
|
|
468
|
+
? ref$
|
|
469
|
+
: libCache[name] = import(libs[name]);
|
|
470
|
+
};
|
|
471
|
+
encode = function(opt){
|
|
472
|
+
var even, w, h, fps, sub, d, ref$, from, to, n, bg, abort, output, that;
|
|
473
|
+
opt == null && (opt = {});
|
|
474
|
+
if (typeof VideoEncoder === 'undefined') {
|
|
475
|
+
return Promise.reject(new Error('[lotion] WebCodecs ( VideoEncoder ) is not supported in this browser'));
|
|
476
|
+
}
|
|
477
|
+
even = function(v){
|
|
478
|
+
return 2 * Math.round(v / 2);
|
|
479
|
+
};
|
|
480
|
+
w = even(opt.width || 1920);
|
|
481
|
+
h = even(opt.height || 1080);
|
|
482
|
+
fps = opt.fps || 30;
|
|
483
|
+
sub = Math.max(1, Math.round(opt.sub || 1));
|
|
484
|
+
d = ((ref$ = opt.shutter) != null ? ref$ : 0.5) / fps / sub;
|
|
485
|
+
from = opt.from || 0;
|
|
486
|
+
to = (ref$ = opt.to) != null
|
|
487
|
+
? ref$
|
|
488
|
+
: opt.duration;
|
|
489
|
+
n = Math.max(1, Math.round((to - from) * fps));
|
|
490
|
+
bg = opt.background || getComputedStyle(opt.el).backgroundColor;
|
|
491
|
+
if (!bg || bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent') {
|
|
492
|
+
bg = '#000';
|
|
493
|
+
}
|
|
494
|
+
abort = function(){
|
|
495
|
+
if (opt.signal && opt.signal.aborted) {
|
|
496
|
+
throw new DOMException('export aborted', 'AbortError');
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
output = null;
|
|
500
|
+
return Promise.all([
|
|
501
|
+
(that = opt.snapdom)
|
|
502
|
+
? Promise.resolve(that)
|
|
503
|
+
: lib('snapdom'), (that = opt.mediabunny)
|
|
504
|
+
? Promise.resolve(that)
|
|
505
|
+
: lib('mediabunny')
|
|
506
|
+
]).then(function(arg$){
|
|
507
|
+
var sd, mb, snap, canvas, ctx;
|
|
508
|
+
sd = arg$[0], mb = arg$[1];
|
|
509
|
+
snap = sd.snapdom || sd;
|
|
510
|
+
canvas = document.createElement('canvas');
|
|
511
|
+
canvas.width = w;
|
|
512
|
+
canvas.height = h;
|
|
513
|
+
ctx = canvas.getContext('2d');
|
|
514
|
+
return mb.getFirstEncodableVideoCodec(['avc', 'vp9', 'av1'], {
|
|
515
|
+
width: w,
|
|
516
|
+
height: h
|
|
517
|
+
}).then(function(codec){
|
|
518
|
+
var src;
|
|
519
|
+
if (!codec) {
|
|
520
|
+
throw new Error("[lotion] no encodable video codec for " + w + "x" + h);
|
|
521
|
+
}
|
|
522
|
+
output = new mb.Output({
|
|
523
|
+
format: new mb.Mp4OutputFormat({
|
|
524
|
+
fastStart: 'in-memory'
|
|
525
|
+
}),
|
|
526
|
+
target: new mb.BufferTarget()
|
|
527
|
+
});
|
|
528
|
+
src = new mb.CanvasSource(canvas, {
|
|
529
|
+
codec: codec,
|
|
530
|
+
bitrate: opt.bitrate || mb.QUALITY_HIGH
|
|
531
|
+
});
|
|
532
|
+
output.addVideoTrack(src, {
|
|
533
|
+
frameRate: fps
|
|
534
|
+
});
|
|
535
|
+
return output.start().then(function(){
|
|
536
|
+
return src;
|
|
537
|
+
});
|
|
538
|
+
}).then(function(src){
|
|
539
|
+
var pending, step;
|
|
540
|
+
pending = Promise.resolve();
|
|
541
|
+
step = function(i, j){
|
|
542
|
+
var t;
|
|
543
|
+
abort();
|
|
544
|
+
if (i >= n) {
|
|
545
|
+
return pending;
|
|
546
|
+
}
|
|
547
|
+
if (j >= sub) {
|
|
548
|
+
return pending.then(function(){
|
|
549
|
+
abort();
|
|
550
|
+
pending = src.add(i / fps, 1 / fps);
|
|
551
|
+
if (opt.progress) {
|
|
552
|
+
opt.progress((i + 1) / n);
|
|
553
|
+
}
|
|
554
|
+
return step(i + 1, 0);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
t = clamp(from + i / fps + (sub > 1 ? (j - (sub - 1) / 2) * d : 0), 0, opt.duration);
|
|
558
|
+
opt.seek(t);
|
|
559
|
+
return snap.toCanvas(opt.el, {
|
|
560
|
+
width: w,
|
|
561
|
+
height: h,
|
|
562
|
+
dpr: 1,
|
|
563
|
+
backgroundColor: bg
|
|
564
|
+
}).then(function(c){
|
|
565
|
+
ctx.globalAlpha = 1 / (j + 1);
|
|
566
|
+
ctx.drawImage(c, 0, 0, w, h);
|
|
567
|
+
return step(i, j + 1);
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
return step(0, 0);
|
|
571
|
+
});
|
|
572
|
+
}).then(function(){
|
|
573
|
+
return output.finalize();
|
|
574
|
+
}).then(function(){
|
|
575
|
+
return new Blob([output.target.buffer], {
|
|
576
|
+
type: 'video/mp4'
|
|
577
|
+
});
|
|
578
|
+
})['catch'](function(e){
|
|
579
|
+
var ref$;
|
|
580
|
+
if (output && ((ref$ = output.state) === 'started' || ref$ === 'pending')) {
|
|
581
|
+
output.cancel();
|
|
582
|
+
}
|
|
583
|
+
throw e;
|
|
584
|
+
});
|
|
585
|
+
};
|
|
413
586
|
lotion = {
|
|
414
587
|
clamp: clamp,
|
|
415
588
|
lerp: lerp,
|
|
@@ -427,11 +600,19 @@
|
|
|
427
600
|
put: put,
|
|
428
601
|
txt: txt,
|
|
429
602
|
svg: svg,
|
|
430
|
-
player: player
|
|
603
|
+
player: player,
|
|
604
|
+
encode: encode,
|
|
605
|
+
libs: libs,
|
|
606
|
+
lib: lib
|
|
431
607
|
};
|
|
432
608
|
if (typeof module != 'undefined' && module !== null) {
|
|
433
609
|
module.exports = lotion;
|
|
434
610
|
} else if (typeof window != 'undefined' && window !== null) {
|
|
435
611
|
window.lotion = lotion;
|
|
436
612
|
}
|
|
613
|
+
function import$(obj, src){
|
|
614
|
+
var own = {}.hasOwnProperty;
|
|
615
|
+
for (var key in src) if (own.call(src, key)) obj[key] = src[key];
|
|
616
|
+
return obj;
|
|
617
|
+
}
|
|
437
618
|
}).call(this);
|
package/index.min.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.lotion{--lotion-bg:#eceae5;--lotion-paper:#fbfaf7;--lotion-ink:#18181a;--lotion-mute:#8a877f;--lotion-line:#d9d6cf;position:relative;border:1px solid var(--lotion-line);border-radius:12px;overflow:hidden;outline:none}.lotion .lotion-viewport{position:relative;width:100%;overflow:hidden;background:var(--lotion-bg)}.lotion .lotion-stage{position:absolute;left:0;top:0;transform-origin:0 0;background:var(--lotion-bg);overflow:hidden;line-height:normal}.lotion .lotion-bar{display:flex;align-items:center;gap:18px;height:64px;padding:0 24px;background:var(--lotion-paper);border-top:1px solid var(--lotion-line);user-select:none}.lotion .lotion-btn{flex:none;width:40px;height:40px;border-radius:50%;background:var(--lotion-ink);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer}.lotion .lotion-play:before{content:"";margin-left:3px;border-style:solid;border-width:7px 0 7px 12px;border-color:transparent transparent transparent #fff}.lotion.lotion-playing .lotion-play:before{margin-left:0;width:4px;height:14px;border-width:0 4px;border-color:#fff}.lotion .lotion-fs{background:transparent;color:var(--lotion-ink);border:1px solid var(--lotion-line)}.lotion .lotion-time{font:15px/1 sans-serif;color:var(--lotion-mute);font-variant-numeric:tabular-nums;white-space:nowrap}.lotion .lotion-track{position:relative;flex:1;height:40px;cursor:pointer}.lotion .lotion-rail,.lotion .lotion-fill{position:absolute;left:0;top:18px;height:4px;border-radius:2px}.lotion .lotion-rail{right:0;background:var(--lotion-line)}.lotion .lotion-fill{background:var(--lotion-ink)}.lotion .lotion-knob{position:absolute;top:13px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:var(--lotion-ink)}.lotion .lotion-chapters{position:absolute;inset:0;pointer-events:none}.lotion .lotion-ch{position:absolute;top:14px;width:2px;height:12px;margin-left:-1px;border-radius:1px;background:var(--lotion-mute);opacity:.5}.lotion.lotion-loading .lotion-stage{visibility:hidden}.lotion.lotion-loading .lotion-bar{opacity:.5;pointer-events:none}.lotion .lotion-loading-screen{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--lotion-bg);color:var(--lotion-mute)}.lotion .lotion-spinner{width:36px;height:36px;border-radius:50%;border:3px solid var(--lotion-line);border-top-color:var(--lotion-ink);animation:lotion-spin .8s linear infinite}.lotion:fullscreen{display:flex;flex-direction:column;border:0;border-radius:0}.lotion:fullscreen .lotion-viewport{flex:1;aspect-ratio:auto !important}.lotion:fullscreen .lotion-bar{flex:none}.lotion.lotion-render{position:fixed;inset:0;z-index:99999;border:0;border-radius:0}.lotion.lotion-render .lotion-viewport{width:100vw;height:100vh;aspect-ratio:auto !important}.lotion.lotion-render .lotion-bar{display:none}@-moz-keyframes lotion-spin{to{transform:rotate(360deg)}}@-webkit-keyframes lotion-spin{to{transform:rotate(360deg)}}@-o-keyframes lotion-spin{to{transform:rotate(360deg)}}@keyframes lotion-spin{to{transform:rotate(360deg)}}
|
|
1
|
+
.lotion{--lotion-bg:#eceae5;--lotion-paper:#fbfaf7;--lotion-ink:#18181a;--lotion-mute:#8a877f;--lotion-line:#d9d6cf;position:relative;border:1px solid var(--lotion-line);border-radius:12px;overflow:hidden;outline:none}.lotion .lotion-viewport{position:relative;width:100%;overflow:hidden;background:var(--lotion-bg)}.lotion .lotion-stage{position:absolute;left:0;top:0;transform-origin:0 0;background:var(--lotion-bg);overflow:hidden;line-height:normal}.lotion .lotion-bar{display:flex;align-items:center;gap:18px;height:64px;padding:0 24px;background:var(--lotion-paper);border-top:1px solid var(--lotion-line);user-select:none}.lotion .lotion-btn{flex:none;width:40px;height:40px;border-radius:50%;background:var(--lotion-ink);color:#fff;display:flex;align-items:center;justify-content:center;cursor:pointer}.lotion .lotion-play:before{content:"";margin-left:3px;border-style:solid;border-width:7px 0 7px 12px;border-color:transparent transparent transparent #fff}.lotion.lotion-playing .lotion-play:before{margin-left:0;width:4px;height:14px;border-width:0 4px;border-color:#fff}.lotion .lotion-fs{background:transparent;color:var(--lotion-ink);border:1px solid var(--lotion-line)}.lotion .lotion-time{font:15px/1 sans-serif;color:var(--lotion-mute);font-variant-numeric:tabular-nums;white-space:nowrap}.lotion .lotion-track{position:relative;flex:1;height:40px;cursor:pointer}.lotion .lotion-rail,.lotion .lotion-fill{position:absolute;left:0;top:18px;height:4px;border-radius:2px}.lotion .lotion-rail{right:0;background:var(--lotion-line)}.lotion .lotion-fill{background:var(--lotion-ink)}.lotion .lotion-knob{position:absolute;top:13px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:var(--lotion-ink)}.lotion .lotion-chapters{position:absolute;inset:0;pointer-events:none}.lotion .lotion-ch{position:absolute;top:14px;width:2px;height:12px;margin-left:-1px;border-radius:1px;background:var(--lotion-mute);opacity:.5}.lotion.lotion-loading .lotion-stage{visibility:hidden}.lotion.lotion-loading .lotion-bar{opacity:.5;pointer-events:none}.lotion.lotion-encoding .lotion-bar{opacity:.5;pointer-events:none}.lotion .lotion-loading-screen,.lotion .lotion-encoding-screen{position:absolute;inset:0;display:flex;flex-direction:column;gap:12px;align-items:center;justify-content:center;background:var(--lotion-bg);color:var(--lotion-mute)}.lotion .lotion-progress{font-size:14px;font-variant-numeric:tabular-nums}.lotion .lotion-spinner{width:36px;height:36px;border-radius:50%;border:3px solid var(--lotion-line);border-top-color:var(--lotion-ink);animation:lotion-spin .8s linear infinite}.lotion:fullscreen{display:flex;flex-direction:column;border:0;border-radius:0}.lotion:fullscreen .lotion-viewport{flex:1;aspect-ratio:auto !important}.lotion:fullscreen .lotion-bar{flex:none}.lotion.lotion-render{position:fixed;inset:0;z-index:99999;border:0;border-radius:0}.lotion.lotion-render .lotion-viewport{width:100vw;height:100vh;aspect-ratio:auto !important}.lotion.lotion-render .lotion-bar{display:none}@-moz-keyframes lotion-spin{to{transform:rotate(360deg)}}@-webkit-keyframes lotion-spin{to{transform:rotate(360deg)}}@-o-keyframes lotion-spin{to{transform:rotate(360deg)}}@keyframes lotion-spin{to{transform:rotate(360deg)}}
|
package/index.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(){var t,n=Math.PI,r=Math.sin,s=Math.cos,o=Math.exp,l=Math.sqrt,e=Math.round,u=function(t,i,e){return null==e&&(e=1),t<(i=null==i?0:i)?i:e<t?e:t},a=function(t,i,e){return t+(i-t)*e},h=function(t){return(t=u(t))*t*(3-2*t)},c=function(t,i,e){var n;return null==i&&(i=14),t<=0?0:1<=(e=null==e?.8:e)?1-o(-i*t)*(1+i*t):(n=i*l(1-e*e),1-o(-e*i*t)*(s(n*t)+e*i/n*r(n*t)))},i={fast:{w:22,z:.86},std:{w:14,z:.8},soft:{w:9,z:.9},move:{w:11,z:.85},ease:{w:10,z:1}},d=function(l,t){var u,a=(u=function(t){return"string"==typeof t?i[t]:t})(t=null==t?"std":t);return function(t){for(var i,e,n,r=l[0][1],s=1,o=l.length;s<o;++s)n=(e=l[i=s])[2]?u(e[2]):a,r+=(e[1]-l[i-1][1])*c(t-e[0],n.w,n.z);return r}},f=function(t,i,e,n,r){return h((t-i)/(n=null==n?.4:n))*(1-h((t-(e=null==e?1e9:e))/(r=null==r?.35:r)))},p=function(t,i,e){return null==e&&(e=.6),t<i||i+e<t?0:Math.pow(r(n*(t-i)/e),2)},v=function(t,i,e,n){return null==n&&(n=.06),(e=Array.from(e)).slice(0,u(Math.floor((t-i)/n)+1,0,e.length)).join("")},g=function(i){return[1,3,5].map(function(t){return parseInt(i.slice(t,t+2),16)})},w=function(i,e,n){return[0,1,2].map(function(t){return a(i[t],e[t],n)})},y=function(t,i){return null!=i?"rgba("+t.map(function(t){return e(t)}).join(",")+","+i+")":"rgb("+t.map(function(t){return e(t)}).join(",")+")"},m=function(t,i,e,n){var r;return null==t&&(t=""),null==i&&(i=""),null==n&&(n=""),(r=document.createElement("div")).className=t.split(" ").filter(function(t){return t}).map(function(t){return n+t}).join(" "),r.innerHTML=i,e&&e.appendChild(r),r},k=function(t,i,e,n,r,s){var o;if(null==n&&(n=1),null==r&&(r=1),!((o=null==(o=(s=null==s?{}:s).hide)||o)&&r<=.002))return o&&!0!==t._lv&&(t.style.visibility="visible",t._lv=!0),t.style.transform="translate("+i.toFixed(2)+"px,"+e.toFixed(2)+"px) scale("+n.toFixed(4)+")",t.style.opacity=r.toFixed(3),null!=s.blur?t.style.filter=.05<s.blur?"blur("+s.blur.toFixed(2)+"px)":"":void 0;!1!==t._lv&&(t.style.visibility="hidden",t._lv=!1)},b=function(t,i){if(t._lt!==i)return t.innerHTML=i,t._lt=i},x=function(t,i,e){var n,r,s;for(r in null==i&&(i={}),n=document.createElementNS("http://www.w3.org/2000/svg",t),i)s=i[r],n.setAttribute(r,s);return e&&e.appendChild(n),n},M=function(t){var i=this;return this.opt=t=null==t?{}:t,this.root="string"==typeof t.root?document.querySelector(t.root):t.root,this.width=t.width||1920,this.height=t.height||1080,this.duration=t.duration||0,this.chapters=t.chapters||[],this.t=0,this.playing=!1,this.last=null,this.started=!1,this.ready=new Promise(function(t){return i.readyRes=t}),this.renderMode=/[?&]render\b/.test(location.search),this.init(),this};M.prototype=((t=Object.create(Object.prototype)).constructor=M,t.init=function(){var t,n=this,i=this.root;return i.classList.add("lotion"),i.hasAttribute("tabindex")||i.setAttribute("tabindex",0),this.viewport=m("lotion-viewport","",i),this.stage=m("lotion-stage","",this.viewport),(t=this.stage.style).width=this.width+"px",t.height=this.height+"px",this.viewport.style.aspectRatio=this.width+" / "+this.height,i.classList.add("lotion-loading"),!1!==this.opt.loading&&(t="string"==typeof this.opt.loading?this.opt.loading:'<div class="lotion-spinner"></div>',this.loading=m("lotion-loading-screen",t,this.viewport)),this.bar=m("lotion-bar",'<div class="lotion-btn lotion-play"></div>\n<div class="lotion-track"><div class="lotion-rail"></div><div class="lotion-fill"></div>\n<div class="lotion-chapters"></div><div class="lotion-knob"></div></div>\n<div class="lotion-time"></div>\n<div class="lotion-btn lotion-fs" title="全螢幕"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"\nstroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M2 6V2h4M10 2h4v4M14 10v4h-4M6 14H2v-4"/></svg></div>',i),this.el={play:(t=function(t){return n.bar.querySelector(".lotion-"+t)})("play"),fill:t("fill"),knob:t("knob"),time:t("time"),track:t("track"),chapters:t("chapters"),fs:t("fs")},this.chapters.map(function(t){var i=t[0],t=t[1],e=m("lotion-ch","",n.el.chapters);return e.title=t,e.style.left=100*i/n.duration+"%"}),this.bind(),this.renderMode&&i.classList.add("lotion-render"),this.fit(),"undefined"!=typeof ResizeObserver?new ResizeObserver(function(){return n.fit()}).observe(this.viewport):window.addEventListener("resize",function(){return n.fit()})},t.start=function(){var t,i=this;return this.started=!0,this.root.classList.remove("lotion-loading"),this.loading&&this.loading.remove(),this.renderMode&&(window.seek=function(t){return i.opt.seek(t)},window.DURATION=this.duration,this.opt.cues)&&(window.cues=this.opt.cues),this.readyRes(this),t=/[?&]t=([\d.]+)/.exec(location.search),this.seek(t?+t[1]:!this.renderMode&&this.opt.start||0),this.opt.autoplay&&!this.renderMode&&document.fonts.ready.then(function(){return i.play(!0)}),this},t.bind=function(){var i,e,n=this;return this.el.play.addEventListener("click",function(t){return t.stopPropagation(),n.toggle()}),this.el.fs.addEventListener("click",function(t){return t.stopPropagation(),n.fullscreen()}),this.viewport.addEventListener("click",function(){return n.root.focus(),n.toggle()}),i=!1,e=function(t){var i=n.el.track.getBoundingClientRect();return n.seek(n.duration*u((t.clientX-i.left)/i.width))},this.el.track.addEventListener("pointerdown",function(t){return i=!0,n.el.track.setPointerCapture(t.pointerId),e(t)}),this.el.track.addEventListener("pointermove",function(t){if(i)return e(t)}),this.el.track.addEventListener("pointerup",function(){return i=!1}),this.root.addEventListener("keydown",function(t){var i,e=n.chapters.map(function(t){return t[0]});return"Space"===t.code?(t.preventDefault(),n.toggle()):"f"===t.key?n.fullscreen():"ArrowRight"===t.key?n.seek(null!=(i=e.find(function(t){return t>n.t+.05}))?i:n.duration):"ArrowLeft"===t.key?n.seek(null!=(i=e.filter(function(t){return t<n.t-.5}).pop())?i:0):void 0})},t.fit=function(){var t,i=this.viewport.clientWidth,e=this.viewport.clientHeight;if(i&&e)return t=Math.min(i/this.width,e/this.height),this.stage.style.transform="translate("+(i-this.width*t)/2+"px,"+(e-this.height*t)/2+"px) scale("+t+")"},t.fmt=function(t){return Math.floor(t/60)+":"+("0"+Math.floor(t%60)).slice(-2)},t.seek=function(i){var t;if(this.started)return this.t=i=u(i,0,this.duration),this.opt.seek(i),t=100*i/this.duration+"%",this.el.fill.style.width=t,this.el.knob.style.left=t,t=this.chapters.filter(function(t){return t[0]<=i}).pop(),this.el.time.textContent=(t?t[1]+" · ":"")+this.fmt(i)+" / "+this.fmt(this.duration),this.root.classList.toggle("lotion-playing",this.playing)},t.tick=function(t){var i=this;if(this.playing){if(null!=this.last&&this.seek(this.t+(t-this.last)/1e3),this.last=t,!(this.t>=this.duration))return requestAnimationFrame(function(t){return i.tick(t)});this.playing=!1,this.seek(this.t)}},t.play=function(t){var i=this;if(null==t&&(t=!0),this.started)return t&&this.t>=this.duration&&(this.t=0),this.playing=t,this.last=null,this.seek(this.t),t?requestAnimationFrame(function(t){return i.tick(t)}):void 0},t.pause=function(){return this.play(!1)},t.toggle=function(){return this.play(!this.playing)},t.fullscreen=function(){var t=document;return t.fullscreenElement||t.webkitFullscreenElement?(t.exitFullscreen||t.webkitExitFullscreen).call(t):(this.root.requestFullscreen||this.root.webkitRequestFullscreen).call(this.root),this.root.focus()},t),t={clamp:u,lerp:a,ss:h,spring:c,presets:i,track:d,vis:f,bump:p,typing:v,hex:g,mixc:w,rgb:y,mk:m,put:k,txt:b,svg:x,player:M},"undefined"!=typeof module&&null!==module?module.exports=t:"undefined"!=typeof window&&null!==window&&(window.lotion=t)}.call(this);
|
|
1
|
+
!function(){var i,r,o,s,l,n,w,a,u,h,e,t,d,c,f,p,v,g,m,y,b,k,x,M,E,L,C,F;i=Math.PI,r=Math.sin,o=Math.cos,s=Math.exp,l=Math.sqrt,n=Math.round,w=function(t,e,n){return null==n&&(n=1),t<(e=null==e?0:e)?e:n<t?n:t},a=function(t,e,n){return t+(e-t)*n},u=function(t){return(t=w(t))*t*(3-2*t)},h=function(t,e,n){var i;return null==e&&(e=14),t<=0?0:1<=(n=null==n?.8:n)?1-s(-e*t)*(1+e*t):(i=e*l(1-n*n),1-s(-n*e*t)*(o(i*t)+n*e/i*r(i*t)))},e={fast:{w:22,z:.86},std:{w:14,z:.8},soft:{w:9,z:.9},move:{w:11,z:.85},ease:{w:10,z:1}},t=function(l,t){var a,u=(a=function(t){return"string"==typeof t?e[t]:t})(t=null==t?"std":t);return function(t){for(var e,n,i,r=l[0][1],o=1,s=l.length;o<s;++o)i=(n=l[e=o])[2]?a(n[2]):u,r+=(n[1]-l[e-1][1])*h(t-n[0],i.w,i.z);return r}},d=function(t,e,n,i,r){return u((t-e)/(i=null==i?.4:i))*(1-u((t-(n=null==n?1e9:n))/(r=null==r?.35:r)))},c=function(t,e,n){return null==n&&(n=.6),t<e||e+n<t?0:Math.pow(r(i*(t-e)/n),2)},f=function(t,e,n,i){return null==i&&(i=.06),(n=Array.from(n)).slice(0,w(Math.floor((t-e)/i)+1,0,n.length)).join("")},p=function(e){return[1,3,5].map(function(t){return parseInt(e.slice(t,t+2),16)})},v=function(e,n,i){return[0,1,2].map(function(t){return a(e[t],n[t],i)})},g=function(t,e){return null!=e?"rgba("+t.map(function(t){return n(t)}).join(",")+","+e+")":"rgb("+t.map(function(t){return n(t)}).join(",")+")"},m=function(t,e,n,i){var r;return null==t&&(t=""),null==e&&(e=""),null==i&&(i=""),(r=document.createElement("div")).className=t.split(" ").filter(function(t){return t}).map(function(t){return i+t}).join(" "),r.innerHTML=e,n&&n.appendChild(r),r},y=function(t,e,n,i,r,o){var s;if(null==i&&(i=1),null==r&&(r=1),!((s=null==(s=(o=null==o?{}:o).hide)||s)&&r<=.002))return s&&!0!==t._lv&&(t.style.visibility="visible",t._lv=!0),t.style.transform="translate("+e.toFixed(2)+"px,"+n.toFixed(2)+"px) scale("+i.toFixed(4)+")",t.style.opacity=r.toFixed(3),null!=o.blur?t.style.filter=.05<o.blur?"blur("+o.blur.toFixed(2)+"px)":"":void 0;!1!==t._lv&&(t.style.visibility="hidden",t._lv=!1)},b=function(t,e){if(t._lt!==e)return t.innerHTML=e,t._lt=e},k=function(t,e,n){var i,r,o;for(r in null==e&&(e={}),i=document.createElementNS("http://www.w3.org/2000/svg",t),e)o=e[r],i.setAttribute(r,o);return n&&n.appendChild(i),i},(x=function(t){var e=this;return this.opt=t=null==t?{}:t,this.root="string"==typeof t.root?document.querySelector(t.root):t.root,this.width=t.width||1920,this.height=t.height||1080,this.duration=t.duration||0,this.chapters=t.chapters||[],this.t=0,this.playing=!1,this.last=null,this.started=!1,this.ready=new Promise(function(t){return e.readyRes=t}),this.renderMode=/[?&]render\b/.test(location.search),this.init(),this}).prototype=((F=Object.create(Object.prototype)).constructor=x,F.init=function(){var t,i=this,e=this.root;return e.classList.add("lotion"),e.hasAttribute("tabindex")||e.setAttribute("tabindex",0),this.viewport=m("lotion-viewport","",e),this.stage=m("lotion-stage","",this.viewport),(t=this.stage.style).width=this.width+"px",t.height=this.height+"px",this.viewport.style.aspectRatio=this.width+" / "+this.height,e.classList.add("lotion-loading"),!1!==this.opt.loading&&(t="string"==typeof this.opt.loading?this.opt.loading:'<div class="lotion-spinner"></div>',this.loading=m("lotion-loading-screen",t,this.viewport)),this.bar=m("lotion-bar",'<div class="lotion-btn lotion-play"></div>\n<div class="lotion-track"><div class="lotion-rail"></div><div class="lotion-fill"></div>\n<div class="lotion-chapters"></div><div class="lotion-knob"></div></div>\n<div class="lotion-time"></div>\n<div class="lotion-btn lotion-fs" title="全螢幕"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"\nstroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M2 6V2h4M10 2h4v4M14 10v4h-4M6 14H2v-4"/></svg></div>',e),this.el={play:(t=function(t){return i.bar.querySelector(".lotion-"+t)})("play"),fill:t("fill"),knob:t("knob"),time:t("time"),track:t("track"),chapters:t("chapters"),fs:t("fs")},this.chapters.map(function(t){var e=t[0],t=t[1],n=m("lotion-ch","",i.el.chapters);return n.title=t,n.style.left=100*e/i.duration+"%"}),this.bind(),this.renderMode&&e.classList.add("lotion-render"),this.fit(),"undefined"!=typeof ResizeObserver?new ResizeObserver(function(){return i.fit()}).observe(this.viewport):window.addEventListener("resize",function(){return i.fit()})},F.start=function(){var t,e=this;return this.started=!0,this.root.classList.remove("lotion-loading"),this.loading&&this.loading.remove(),this.renderMode&&(window.seek=function(t){return e.opt.seek(t)},window.DURATION=this.duration,this.opt.cues)&&(window.cues=this.opt.cues),this.readyRes(this),t=/[?&]t=([\d.]+)/.exec(location.search),this.seek(t?+t[1]:!this.renderMode&&this.opt.start||0),this.opt.autoplay&&!this.renderMode&&document.fonts.ready.then(function(){return e.play(!0)}),this},F.bind=function(){var e,n,i=this;return this.el.play.addEventListener("click",function(t){return t.stopPropagation(),i.toggle()}),this.el.fs.addEventListener("click",function(t){return t.stopPropagation(),i.fullscreen()}),this.viewport.addEventListener("click",function(){return i.root.focus(),i.toggle()}),e=!1,n=function(t){var e=i.el.track.getBoundingClientRect();return i.seek(i.duration*w((t.clientX-e.left)/e.width))},this.el.track.addEventListener("pointerdown",function(t){return e=!0,i.el.track.setPointerCapture(t.pointerId),n(t)}),this.el.track.addEventListener("pointermove",function(t){if(e)return n(t)}),this.el.track.addEventListener("pointerup",function(){return e=!1}),this.root.addEventListener("keydown",function(t){var e,n=i.chapters.map(function(t){return t[0]});return"Space"===t.code?(t.preventDefault(),i.toggle()):"f"===t.key?i.fullscreen():"ArrowRight"===t.key?i.seek(null!=(e=n.find(function(t){return t>i.t+.05}))?e:i.duration):"ArrowLeft"===t.key?i.seek(null!=(e=n.filter(function(t){return t<i.t-.5}).pop())?e:0):void 0})},F.fit=function(){var t,e,n;if(!this.encoding&&(t=this.viewport.clientWidth,e=this.viewport.clientHeight,t)&&e)return n=Math.min(t/this.width,e/this.height),this.stage.style.transform="translate("+(t-this.width*n)/2+"px,"+(e-this.height*n)/2+"px) scale("+n+")"},F.fmt=function(t){return Math.floor(t/60)+":"+("0"+Math.floor(t%60)).slice(-2)},F.seek=function(e){var t;if(this.started&&!this.encoding)return this.t=e=w(e,0,this.duration),this.opt.seek(e),t=100*e/this.duration+"%",this.el.fill.style.width=t,this.el.knob.style.left=t,t=this.chapters.filter(function(t){return t[0]<=e}).pop(),this.el.time.textContent=(t?t[1]+" · ":"")+this.fmt(e)+" / "+this.fmt(this.duration),this.root.classList.toggle("lotion-playing",this.playing)},F.tick=function(t){var e=this;if(this.playing){if(null!=this.last&&this.seek(this.t+(t-this.last)/1e3),this.last=t,!(this.t>=this.duration))return requestAnimationFrame(function(t){return e.tick(t)});this.playing=!1,this.seek(this.t)}},F.play=function(t){var e=this;if(null==t&&(t=!0),this.started&&!this.encoding)return t&&this.t>=this.duration&&(this.t=0),this.playing=t,this.last=null,this.seek(this.t),t?requestAnimationFrame(function(t){return e.tick(t)}):void 0},F.pause=function(){return this.play(!1)},F.toggle=function(){return this.play(!this.playing)},F.fullscreen=function(){var t=document;return t.fullscreenElement||t.webkitFullscreenElement?(t.exitFullscreen||t.webkitExitFullscreen).call(t):(this.root.requestFullscreen||this.root.webkitRequestFullscreen).call(this.root),this.root.focus()},F.encode=function(e){var t,n,i,r,o,s,l,a=this;return null==e&&(e={}),this.started?this.encoding?Promise.reject(new Error("[lotion] already encoding")):(this.pause(),t=this.t,this.encoding=!0,this.root.classList.add("lotion-encoding"),n=this.stage.style.transform,this.stage.style.transform="none",i=m("lotion-encoding-screen",'<div class="lotion-spinner"></div><div class="lotion-progress"></div>',this.viewport),r=i.querySelector(".lotion-progress"),o=function(){return i.remove(),a.stage.style.transform=n,a.encoding=!1,a.root.classList.remove("lotion-encoding"),a.fit(),a.seek(t)},s=function(t){if(r.textContent=Math.round(100*t)+"%",e.progress)return e.progress(t)},C(((l=((t,e)=>{var n,i={}.hasOwnProperty;for(n in e)i.call(e,n)&&(t[n]=e[n]);return t})({el:this.stage,seek:this.opt.seek,duration:this.duration,width:this.width,height:this.height},e)).progress=s,l)).then(function(t){return o(),t}).catch(function(t){throw o(),t})):Promise.reject(new Error("[lotion] encode() before start()"))},F),E={},F={clamp:w,lerp:a,ss:u,spring:h,presets:e,track:t,vis:d,bump:c,typing:f,hex:p,mixc:v,rgb:g,mk:m,put:y,txt:b,svg:k,player:x,encode:C=function(a){var u,h,d,c,f,p,v,g,m,r,t;return null==a&&(a={}),"undefined"==typeof VideoEncoder?Promise.reject(new Error("[lotion] WebCodecs ( VideoEncoder ) is not supported in this browser")):(u=(t=function(t){return 2*Math.round(t/2)})(a.width||1920),h=t(a.height||1080),d=a.fps||30,c=Math.max(1,Math.round(a.sub||1)),f=(null!=(t=a.shutter)?t:.5)/d/c,p=a.from||0,t=null!=(t=a.to)?t:a.duration,v=Math.max(1,Math.round((t-p)*d)),(g=a.background||getComputedStyle(a.el).backgroundColor)&&"rgba(0, 0, 0, 0)"!==g&&"transparent"!==g||(g="#000"),m=function(){if(a.signal&&a.signal.aborted)throw new DOMException("export aborted","AbortError")},r=null,Promise.all([(t=a.snapdom)?Promise.resolve(t):L("snapdom"),(t=a.mediabunny)?Promise.resolve(t):L("mediabunny")]).then(function(t){var s,e=t[0],n=t[1],l=e.snapdom||e,i=document.createElement("canvas");return i.width=u,i.height=h,s=i.getContext("2d"),n.getFirstEncodableVideoCodec(["avc","vp9","av1"],{width:u,height:h}).then(function(t){var e;if(t)return r=new n.Output({format:new n.Mp4OutputFormat({fastStart:"in-memory"}),target:new n.BufferTarget}),e=new n.CanvasSource(i,{codec:t,bitrate:a.bitrate||n.QUALITY_HIGH}),r.addVideoTrack(e,{frameRate:d}),r.start().then(function(){return e});throw new Error("[lotion] no encodable video codec for "+u+"x"+h)}).then(function(i){var r=Promise.resolve(),o=function(e,n){var t;return m(),v<=e?r:c<=n?r.then(function(){return m(),r=i.add(e/d,1/d),a.progress&&a.progress((e+1)/v),o(e+1,0)}):(t=w(p+e/d+(1<c?(n-(c-1)/2)*f:0),0,a.duration),a.seek(t),l.toCanvas(a.el,{width:u,height:h,dpr:1,backgroundColor:g}).then(function(t){return s.globalAlpha=1/(n+1),s.drawImage(t,0,0,u,h),o(e,n+1)}))};return o(0,0)})}).then(function(){return r.finalize()}).then(function(){return new Blob([r.target.buffer],{type:"video/mp4"})}).catch(function(t){var e;throw!r||"started"!==(e=r.state)&&"pending"!==e||r.cancel(),t}))},libs:M={snapdom:"https://cdn.jsdelivr.net/npm/@zumer/snapdom@3.3.0/dist/snapdom.mjs",mediabunny:"https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/dist/bundles/mediabunny.min.mjs"},lib:L=function(t){var e;return null!=(e=E[t])?e:E[t]=import(M[t])}},"undefined"!=typeof module&&null!==module?module.exports=F:"undefined"!=typeof window&&null!==window&&(window.lotion=F)}.call(this);
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"author":"zbryikt","name":"@plotdb/lotion","version":"0.
|
|
1
|
+
{"author":"zbryikt","name":"@plotdb/lotion","version":"0.3.0","description":"deterministic motion toolkit: animation as a pure function of time, with player, frame renderer and block packaging","license":"MIT","main":"index.min.js","browser":"index.min.js","bin":{"lotion":"./cli.js"},"homepage":"https://github.com/plotdb/lotion","repository":{"type":"git","url":"git+https://github.com/plotdb/lotion.git"},"scripts":{"start":"npx server -r web -o true"},"engines":{"node":">=14"},"keywords":["motion","animation","video","playwright","block"],"dependencies":{"@plotdb/block":"^5.7.3","@plotdb/csscope":"^5.0.4","@plotdb/rescope":"^5.1.0","@plotdb/semver":"^0.0.4","proxise":"^1.0.1"},"peerDependencies":{"playwright":">=1.40"},"peerDependenciesMeta":{"playwright":{"optional":true}},"devDependencies":{"@plotdb/guides":"github:plotdb/guides","@zbryikt/template":"^2.5.0","@zumer/snapdom":"^3.3.0","fedep":"^1.9.0","livescript":"^1.6.0","mediabunny":"^1.61.3","playwright":"^1.63.0","stylus":"^0.64.0","uglify-js":"^3.19.3"},"frontendDependencies":{"root":"web/static/assets/lib","modules":["@zumer/snapdom","mediabunny"]}}
|
|
@@ -1,31 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 寫 lotion 動畫
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## 流程
|
|
9
|
-
|
|
10
|
-
1. 先寫故事線:一句話一個段落 ( chapter ),每段只推進一個概念。
|
|
11
|
-
概念之間若有缺口 ( 例如要證明「一定有贏家」之前,得先介紹「路徑強度」),主動補段並告知。
|
|
12
|
-
2. 準備資料:用真正的函式庫計算,畫面上的數字一律取自計算結果,旁白再依資料撰寫。
|
|
13
|
-
需要特定性質的資料 ( 例如出現循環、但最後能排出完整順序 ) 時,用程式搜尋,不要手湊。
|
|
14
|
-
3. 排時間軸:以段落起點加相對時間描述所有事件,方便整段平移或調整長度。
|
|
15
|
-
4. 實作 `seek(t)`,再用 `lotion sheet` 輸出各段代表畫面檢查版面,修正後再輸出影片。
|
|
3
|
+
所有 lotion 動畫都適用的寫法與陷阱,與動畫的形式、風格無關。
|
|
4
|
+
寫 lotion 動畫只需要這份文件。特定形式 ( 解說片、UI loop ) 的做法、風格庫、素材與音訊生成,
|
|
5
|
+
另有製作工具 `@plotdb/lotitor` ( 目前未公開 );沒有它不影響這裡的任何內容。
|
|
16
6
|
|
|
17
7
|
|
|
18
8
|
## 原則
|
|
19
9
|
|
|
20
10
|
- 畫面是時間的純函數:`seek(t)` 只依 `t` 計算,不保留跨影格狀態。
|
|
21
11
|
播放、拖曳、跳段與逐格輸出都只是用不同的 `t` 呼叫它。
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
-
|
|
25
|
-
旁白進出場用淡入淡出加輕微模糊,舊的出場與新的進場不可重疊在同一位置。
|
|
26
|
-
- 強調只用一種語彙:凸顯 = 加粗變深,其它變淡;錯誤 / 矛盾 = 紅色。避免同時有多種強調方式。
|
|
27
|
-
- 圖的配置要先避開交錯:例如四個節點的完全圖,把「輸給所有人」的節點放在三角形中央即可平面化。
|
|
28
|
-
- 標籤不要壓在連線上:名字預設放右側,遇到連線時個別調整,移動時以彈簧插值切換位置。
|
|
12
|
+
- `seek` 必須對任何 `t` 都成立,包含 0 與總長之後。
|
|
13
|
+
- 寫完先用 `lotion sheet` 輸出各段頭、尾、交界與轉場中間點的畫面檢查,再輸出影片。
|
|
14
|
+
- 音效時間點由頁面的 `cues()` 提供,與畫面共用同一組時間函數,`lotion cues` 可匯出。
|
|
29
15
|
|
|
30
16
|
|
|
31
17
|
## 嵌入網頁時
|
|
@@ -42,14 +28,6 @@
|
|
|
42
28
|
以 block 封裝時,interface 要帶上 `ready: player.ready`,render 協定才會等它就緒。
|
|
43
29
|
|
|
44
30
|
|
|
45
|
-
## 配樂與音效
|
|
46
|
-
|
|
47
|
-
- 先決定節奏再排時間軸:`lotion bgm --bpm 120 --grid grid.json` 產生配樂與節拍格線,段落起點與重要轉場對齊拍點。
|
|
48
|
-
使用現成音樂時以 `lotion beats` 分析,並確認第一拍是否偏了半拍。
|
|
49
|
-
- 音效以 cue 描述 ( `[{t, sfx, gain}]` ),由頁面的 `cues()` 提供,與畫面共用同一份時間軸;
|
|
50
|
-
混音時對齊的是音效量測到的 transient peak,而不是檔案開頭。
|
|
51
|
-
|
|
52
|
-
|
|
53
31
|
## 可攜的封裝
|
|
54
32
|
|
|
55
33
|
- 要放進其它網站時,把動畫包成 `@plotdb/block`:依賴 ( 包含 lotion ) 以 `pkg.dependencies` 宣告,
|