pixi-effects 0.33.0 → 0.35.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 +18 -0
- package/README.md +5 -5
- package/ai/chat-template.html +3 -3
- package/dist/Composition-NDXKQHB4.js +7 -0
- package/dist/{Composition-7XNPTM7L.js.map → Composition-NDXKQHB4.js.map} +1 -1
- package/dist/Composition-OLH7RC5Z.cjs +16 -0
- package/dist/{Composition-L6OALWHI.cjs.map → Composition-OLH7RC5Z.cjs.map} +1 -1
- package/dist/Controller.cjs +130 -1
- package/dist/Controller.cjs.map +1 -1
- package/dist/Controller.d.cts +23 -1
- package/dist/Controller.d.ts +23 -1
- package/dist/Controller.js +130 -1
- package/dist/Controller.js.map +1 -1
- package/dist/{Movie-CL6SsAWe.d.cts → Movie-Db4QnMIH.d.cts} +24 -1
- package/dist/{Movie-f0tQG9y5.d.ts → Movie-VYtUN4TS.d.ts} +24 -1
- package/dist/{Spans-KPZ2QDCN.js → Spans-QHHWC2PB.js} +4 -4
- package/dist/{Spans-KPZ2QDCN.js.map → Spans-QHHWC2PB.js.map} +1 -1
- package/dist/{Spans-VXNQXSI3.cjs → Spans-QRMN2EZO.cjs} +9 -9
- package/dist/{Spans-VXNQXSI3.cjs.map → Spans-QRMN2EZO.cjs.map} +1 -1
- package/dist/{chunk-WST432AP.js → chunk-2GNK6DPV.js} +88 -10
- package/dist/chunk-2GNK6DPV.js.map +1 -0
- package/dist/{chunk-SBBXKPBP.cjs → chunk-5IJENZOG.cjs} +62 -62
- package/dist/{chunk-SBBXKPBP.cjs.map → chunk-5IJENZOG.cjs.map} +1 -1
- package/dist/{chunk-LD4VKNPQ.cjs → chunk-7TTCPHAV.cjs} +120 -42
- package/dist/chunk-7TTCPHAV.cjs.map +1 -0
- package/dist/{chunk-BVMZUPDU.cjs → chunk-LEECUCTG.cjs} +22 -5
- package/dist/chunk-LEECUCTG.cjs.map +1 -0
- package/dist/{chunk-BMH4OCDX.js → chunk-XV4L72FW.js} +3 -3
- package/dist/{chunk-BMH4OCDX.js.map → chunk-XV4L72FW.js.map} +1 -1
- package/dist/{chunk-E53QWHBK.js → chunk-Y253JQOH.js} +22 -5
- package/dist/chunk-Y253JQOH.js.map +1 -0
- package/dist/index.cjs +31 -31
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/loader.css +1 -1
- package/dist/present.cjs +3 -3
- package/dist/present.d.cts +1 -1
- package/dist/present.d.ts +1 -1
- package/dist/present.js +3 -3
- package/dist/three.cjs +5 -5
- package/dist/three.js +1 -1
- package/docs/api.md +10 -0
- package/docs/dsl.md +7 -2
- package/llms-full.txt +20 -3
- package/package.json +1 -1
- package/skills/pixi-effects/SKILL.md +1 -1
- package/skills/pixi-effects/reference/pitfalls.md +2 -0
- package/skills/pixi-effects/template.html +6 -6
- package/dist/Composition-7XNPTM7L.js +0 -7
- package/dist/Composition-L6OALWHI.cjs +0 -16
- package/dist/chunk-BVMZUPDU.cjs.map +0 -1
- package/dist/chunk-E53QWHBK.js.map +0 -1
- package/dist/chunk-LD4VKNPQ.cjs.map +0 -1
- package/dist/chunk-WST432AP.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.35.0
|
|
4
|
+
|
|
5
|
+
**The player's seek-bar preview (`thumbnails: true`, off by default) and `movie.thumbnail()`**
|
|
6
|
+
|
|
7
|
+
- **`movie.thumbnail(frame, { width? | scale? })`** gives a small canvas of any frame **without moving the playhead or changing the picture**, also while the movie plays. It draws the frame on the side inside one task (nominal time set, 3D / shader layers awaited, the playhead put back and awaited), so the browser never shows the in-between; it equals `snapshot({ motionBlur: false })` at that size. `null` when a seek, render or another thumbnail is running. Seeks, renders and peeks wait for a running thumbnail, so they never interleave with it.
|
|
8
|
+
- **`movie.thumbnailsOff`** is `'variables'` for a movie that declares variables and `'video'` for one with a video layer, else `null`; `thumbnail()` gives `null` then (a video would have to seek twice, a variable movie can be re-laid out under it).
|
|
9
|
+
- **`new Controller(movie, { canvas, thumbnails: true })`**: hover (or move along) the seek bar and a 160 px picture with the time appears above it, kept inside the picture, hidden while dragging; pictures are cached per tenth of a second until a rebuild. Default off.
|
|
10
|
+
- Tests: `tests/tools/thumbnail.test.ts` (exact against `snapshot`, undisturbed canvas and playhead, playback with a thumbnail per frame, the off reasons, options) and `tests/tools/thumbnailPreview.test.ts` (the bar, with real mouse moves).
|
|
11
|
+
|
|
12
|
+
## 0.34.0
|
|
13
|
+
|
|
14
|
+
**From the capability probe of 2026-10-12 (15 kinds of video tried by fresh agents; nothing was impossible, 9 needed hand-written glue; these are the defects that were verified on the real build)**
|
|
15
|
+
|
|
16
|
+
- **Fixed (docs + a warning): `bulgePinch.center` is a fraction of the filtered layer (0 to 1), not canvas pixels.** The docs said pixels; a pixel value such as `{ x: 900, y: 500 }` moved the bulge out of the layer and nothing showed, silently (a trial lost three runs on it). The docs now say so (`docs/dsl.md`, the filters guide, pitfall 110), and a centre with a component beyond 3 warns: `bulgePinch.center is 0-1 of the layer ({ x: 0.5, y: 0.5 } is its middle), but {"x":900,"y":500} reads as pixels`. `twist.offset`, `zoomBlur.center` and `shockwave.center` were measured and are pixels, as documented.
|
|
17
|
+
- **Docs, facts only: right-to-left and complex scripts** (`docs/dsl.md` Text, pitfall 110). Arabic, Hebrew, Thai and Devanagari are shaped correctly with a font that covers the script; marks that hang below the font's box can be cut off (the dots of an Arabic ي: set `style.padding`; `check` cannot see it); `splitText` / `animateText` place words left to right, so an Arabic line comes out reversed; `visibleChars` shapes each prefix alone. Not changed in the code: nothing asked for it yet.
|
|
18
|
+
- **Fixed: `radialBlur` did nothing on WebGPU, the default backend.** pixi-filters 6.1.5 writes the filter's `kernelSize` as an i32 but its WGSL shader reads an f32, so the value was read as 0 and the blur was skipped, silently, for every user with WebGPU (WebGL was fine). Reproduced with pixi.js and pixi-filters alone (no pixi-effects); the shader is now rebuilt with the field as an i32, and the same blur comes out on both backends (`tests/tools/radialBlur.test.ts`, `examples/_checks/radial-blur.html`). Every other filter of the package was checked for the same mismatch: this is the only one. The workaround can be removed when pixi-filters fixes it.
|
|
19
|
+
- **A test that draws the recommended filters on WebGPU and on WebGL** (`tests/tools/filterSweep.test.ts`, `examples/_checks/filter-sweep.html`): `glow`, `outline`, `dropShadow`, `bevel`, `bloom`, `pixelate`, `crt`, `rgbSplit`, `dot`, `radialBlur`, `glitch` must show and look the same on both. All 38 `pixi-filters` filters were compared once: `multiColorReplace` (whole picture black), `kawaseBlur` (far weaker), `advancedBloom` (sharper) and `ascii` (stripes) differ on WebGPU; they are named in the docs with the filter to use instead (pitfall 111, `docs/dsl.md` Named filters, the filters guide) and the docs say which ones to reach for first. Not fixed in the code: they are not used by any of the repository's 100 example files, and `multiColorReplace` needs pixi-filters' uniform packing reworked.
|
|
20
|
+
|
|
3
21
|
## 0.33.0
|
|
4
22
|
|
|
5
23
|
**`check` tells the truth about what a trial found (design: the AI trial of 2026-10-12, 10 fresh agents on 0.32.0; a fresh review of this change found 2 gaps and 3 false alarms, fixed below)**
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pixi-effects
|
|
2
2
|
|
|
3
|
-
> **Status**: experimental — current release `0.
|
|
3
|
+
> **Status**: experimental — current release `0.35.0`. The API may still change between minor versions; see the [CHANGELOG](./CHANGELOG.md) for what each release changed.
|
|
4
4
|
|
|
5
5
|
**[Guide →](https://yjmtmtk.github.io/pixi-effects/guide/)** · **[Gallery →](https://yjmtmtk.github.io/pixi-effects/examples/gallery/)** · 52 portfolio pieces written as plain data by AI models · **[Examples →](https://yjmtmtk.github.io/pixi-effects/examples/)** · 15 numbered examples, a music lab + an in-browser playground.
|
|
6
6
|
|
|
@@ -49,8 +49,8 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
49
49
|
"imports": {
|
|
50
50
|
"pixi.js": "https://esm.sh/pixi.js@8.22.0?bundle-deps",
|
|
51
51
|
"mediabunny": "https://esm.sh/mediabunny",
|
|
52
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
53
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
52
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/index.js",
|
|
53
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/Controller.js"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
</script>
|
|
@@ -61,7 +61,7 @@ Drop the imports into an [importmap](https://developer.mozilla.org/docs/Web/HTML
|
|
|
61
61
|
</script>
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.
|
|
64
|
+
Load the `dist/` files **as they are** (jsDelivr's `/npm/…/dist/…`, or unpkg's `https://unpkg.com/pixi-effects@0.35.0/dist/index.js`) rather than a CDN-rebundled build such as `esm.sh/pixi-effects` or jsDelivr's `+esm`: the entries (`pixi-effects`, `…/controller`, `…/three`) share internal chunks, which only works when each file is served untouched.
|
|
65
65
|
|
|
66
66
|
> **Using three.js?** Add two more entries (`three` and `pixi-effects/three`) to this importmap — see [Adding three.js](#adding-threejs-optional) below.
|
|
67
67
|
>
|
|
@@ -77,7 +77,7 @@ Add two more entries to the importmap above: three.js itself, and the `pixi-effe
|
|
|
77
77
|
"imports": {
|
|
78
78
|
"...": "(everything from the importmap above)",
|
|
79
79
|
"three": "https://esm.sh/three@0.178.0",
|
|
80
|
-
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
80
|
+
"pixi-effects/three": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/three.js"
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
</script>
|
package/ai/chat-template.html
CHANGED
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"imports": {
|
|
60
60
|
"pixi.js": "https://cdn.jsdelivr.net/npm/pixi.js@8.22.0/dist/pixi.min.mjs",
|
|
61
61
|
"mediabunny": "https://cdn.jsdelivr.net/npm/mediabunny@1.61.3/+esm",
|
|
62
|
-
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
63
|
-
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
64
|
-
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.
|
|
62
|
+
"pixi-effects": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/index.js",
|
|
63
|
+
"pixi-effects/controller": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/Controller.js",
|
|
64
|
+
"pixi-effects/present": "https://cdn.jsdelivr.net/npm/pixi-effects@0.35.0/dist/present.js"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
</script>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CompositionSequence } from './chunk-XV4L72FW.js';
|
|
2
|
+
import './chunk-Y253JQOH.js';
|
|
3
|
+
import './chunk-RTJD3ZSW.js';
|
|
4
|
+
import './chunk-ITDYFBB6.js';
|
|
5
|
+
import './chunk-BXOCEZ4U.js';
|
|
6
|
+
//# sourceMappingURL=Composition-NDXKQHB4.js.map
|
|
7
|
+
//# sourceMappingURL=Composition-NDXKQHB4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-NDXKQHB4.js"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunk5IJENZOG_cjs = require('./chunk-5IJENZOG.cjs');
|
|
4
|
+
require('./chunk-LEECUCTG.cjs');
|
|
5
|
+
require('./chunk-IMQJVRMT.cjs');
|
|
6
|
+
require('./chunk-GL2AQFLQ.cjs');
|
|
7
|
+
require('./chunk-TWZNOUM7.cjs');
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(exports, "CompositionSequence", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: function () { return chunk5IJENZOG_cjs.CompositionSequence; }
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=Composition-OLH7RC5Z.cjs.map
|
|
16
|
+
//# sourceMappingURL=Composition-OLH7RC5Z.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"Composition-OLH7RC5Z.cjs"}
|
package/dist/Controller.cjs
CHANGED
|
@@ -63,6 +63,19 @@ var CONTROLLER_CSS = `
|
|
|
63
63
|
.mc-progress:hover .mc-progress-thumb,
|
|
64
64
|
.mc-progress.mc-scrubbing .mc-progress-thumb { transform: translate(-50%, -50%) scale(1); }
|
|
65
65
|
|
|
66
|
+
.movie-controller > .mc-preview {
|
|
67
|
+
position: absolute; bottom: 74px; left: 0;
|
|
68
|
+
transform: translateX(-50%);
|
|
69
|
+
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
|
70
|
+
padding: 3px; border-radius: 4px;
|
|
71
|
+
background: rgba(0,0,0,0.75);
|
|
72
|
+
pointer-events: none;
|
|
73
|
+
opacity: 0; transition: opacity 100ms ease;
|
|
74
|
+
}
|
|
75
|
+
.mc-preview[data-show="1"] { opacity: 1; }
|
|
76
|
+
.mc-preview canvas { display: block; width: 160px; height: auto; border-radius: 2px; }
|
|
77
|
+
.mc-preview-time { font-size: 12px; line-height: 1; color: var(--mc-fg, #fff); font-variant-numeric: tabular-nums; }
|
|
78
|
+
|
|
66
79
|
.mc-bar {
|
|
67
80
|
position: relative;
|
|
68
81
|
display: flex;
|
|
@@ -295,7 +308,7 @@ var THEME_PROPERTIES = {
|
|
|
295
308
|
trackHeight: "--mc-track-height",
|
|
296
309
|
font: "--mc-font"
|
|
297
310
|
};
|
|
298
|
-
var CONTROLLER_OPTION_KEYS = ["canvas", "theme", "exportButton", "keyboard", "clickToPlay", "presentButton", "pauseAtStops", "loopButton", "className"];
|
|
311
|
+
var CONTROLLER_OPTION_KEYS = ["canvas", "theme", "exportButton", "keyboard", "clickToPlay", "presentButton", "pauseAtStops", "loopButton", "thumbnails", "className"];
|
|
299
312
|
var Controller = class _Controller {
|
|
300
313
|
movie;
|
|
301
314
|
options;
|
|
@@ -341,6 +354,17 @@ var Controller = class _Controller {
|
|
|
341
354
|
onReady = null;
|
|
342
355
|
/** A rebuild (setVars, resize, rebuild) may have moved the stops: draw their marks again. */
|
|
343
356
|
onRebuilt = null;
|
|
357
|
+
previewEl = null;
|
|
358
|
+
previewCanvas = null;
|
|
359
|
+
previewTimeEl = null;
|
|
360
|
+
/** Pictures already made, by frame bucket (a frame is a pure function of time; cleared on a rebuild). */
|
|
361
|
+
previewCache = /* @__PURE__ */ new Map();
|
|
362
|
+
previewWanted = null;
|
|
363
|
+
previewRunning = false;
|
|
364
|
+
previewRetry = null;
|
|
365
|
+
previewRetries = 0;
|
|
366
|
+
previewCleanup = null;
|
|
367
|
+
static PREVIEW_WIDTH_PX = 160;
|
|
344
368
|
presenter = null;
|
|
345
369
|
/** `pixi-effects/present`, loaded as soon as the movie has stops (a press of play must not wait for it). */
|
|
346
370
|
present = null;
|
|
@@ -387,6 +411,7 @@ var Controller = class _Controller {
|
|
|
387
411
|
presentButton: options.presentButton ?? true,
|
|
388
412
|
pauseAtStops: options.pauseAtStops ?? true,
|
|
389
413
|
loopButton: options.loopButton ?? true,
|
|
414
|
+
thumbnails: options.thumbnails ?? false,
|
|
390
415
|
className: options.className ?? "movie-controller"
|
|
391
416
|
};
|
|
392
417
|
installStyles();
|
|
@@ -409,6 +434,7 @@ var Controller = class _Controller {
|
|
|
409
434
|
this.bindPlayButton();
|
|
410
435
|
if (this.options.clickToPlay) this.bindCanvasClick();
|
|
411
436
|
this.bindScrubbing();
|
|
437
|
+
if (this.options.thumbnails) this.bindPreview();
|
|
412
438
|
this.bindMuteButton();
|
|
413
439
|
this.bindLoopButton();
|
|
414
440
|
this.bindVolumeSlider();
|
|
@@ -588,6 +614,7 @@ var Controller = class _Controller {
|
|
|
588
614
|
};
|
|
589
615
|
this.onRebuilt = () => {
|
|
590
616
|
this.refreshStops();
|
|
617
|
+
this.previewCache.clear();
|
|
591
618
|
};
|
|
592
619
|
this.movie.on("ready", this.onReady);
|
|
593
620
|
this.movie.on("rebuilt", this.onRebuilt);
|
|
@@ -911,6 +938,104 @@ var Controller = class _Controller {
|
|
|
911
938
|
};
|
|
912
939
|
window.addEventListener("blur", this.windowBlurHandler);
|
|
913
940
|
}
|
|
941
|
+
/** The hover preview above the seek bar (`thumbnails: true`). */
|
|
942
|
+
bindPreview() {
|
|
943
|
+
const el = document.createElement("div");
|
|
944
|
+
el.className = "mc-preview";
|
|
945
|
+
el.setAttribute("aria-hidden", "true");
|
|
946
|
+
const canvas = document.createElement("canvas");
|
|
947
|
+
const time = document.createElement("span");
|
|
948
|
+
time.className = "mc-preview-time";
|
|
949
|
+
el.append(canvas, time);
|
|
950
|
+
this.root.appendChild(el);
|
|
951
|
+
this.previewEl = el;
|
|
952
|
+
this.previewCanvas = canvas;
|
|
953
|
+
this.previewTimeEl = time;
|
|
954
|
+
const hide = () => {
|
|
955
|
+
this.previewWanted = null;
|
|
956
|
+
el.removeAttribute("data-show");
|
|
957
|
+
};
|
|
958
|
+
const onMove = (e) => {
|
|
959
|
+
if (this.isScrubbing || this.movie.thumbnailsOff || e.pointerType === "touch") {
|
|
960
|
+
hide();
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
const rect = this.progressEl.getBoundingClientRect();
|
|
964
|
+
const inset = _Controller.PROGRESS_INSET_PX;
|
|
965
|
+
const frame = pxToFrame(e.clientX, { left: rect.left + inset, width: Math.max(0, rect.width - 2 * inset) }, this.movie.totalFrames);
|
|
966
|
+
const rootRect = this.root.getBoundingClientRect();
|
|
967
|
+
const half = el.getBoundingClientRect().width / 2 + 2;
|
|
968
|
+
const x = Math.min(Math.max(e.clientX - rootRect.left, half), Math.max(half, rootRect.width - half));
|
|
969
|
+
el.style.left = `${rootRect.width > 0 ? x / rootRect.width * 100 : 50}%`;
|
|
970
|
+
this.previewRetries = 0;
|
|
971
|
+
time.textContent = formatTime(frame / (this.movie.frameRate || 1));
|
|
972
|
+
this.previewWanted = frame;
|
|
973
|
+
void this.runPreview();
|
|
974
|
+
};
|
|
975
|
+
this.progressEl.addEventListener("pointermove", onMove);
|
|
976
|
+
this.progressEl.addEventListener("pointerleave", hide);
|
|
977
|
+
this.progressEl.addEventListener("pointerdown", hide);
|
|
978
|
+
this.previewCleanup = () => {
|
|
979
|
+
this.progressEl.removeEventListener("pointermove", onMove);
|
|
980
|
+
this.progressEl.removeEventListener("pointerleave", hide);
|
|
981
|
+
this.progressEl.removeEventListener("pointerdown", hide);
|
|
982
|
+
if (this.previewRetry) {
|
|
983
|
+
clearTimeout(this.previewRetry);
|
|
984
|
+
this.previewRetry = null;
|
|
985
|
+
}
|
|
986
|
+
el.remove();
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
/** Draws the picture of the wanted frame into the preview; one at a time, and the last wish wins. */
|
|
990
|
+
async runPreview() {
|
|
991
|
+
if (this.previewRunning) return;
|
|
992
|
+
this.previewRunning = true;
|
|
993
|
+
try {
|
|
994
|
+
while (this.previewWanted !== null && !this.destroyed) {
|
|
995
|
+
const wanted = this.previewWanted;
|
|
996
|
+
const fps = this.movie.frameRate || 30;
|
|
997
|
+
const step = Math.max(1, Math.round(fps / 10));
|
|
998
|
+
const key = Math.min(this.movie.totalFrames, Math.round(wanted / step) * step);
|
|
999
|
+
let pic = this.previewCache.get(key) ?? null;
|
|
1000
|
+
if (!pic) {
|
|
1001
|
+
try {
|
|
1002
|
+
pic = await this.movie.thumbnail(key, { width: _Controller.PREVIEW_WIDTH_PX });
|
|
1003
|
+
} catch {
|
|
1004
|
+
this.previewWanted = null;
|
|
1005
|
+
this.previewEl?.removeAttribute("data-show");
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
if (pic) {
|
|
1009
|
+
if (this.previewCache.size >= 120) this.previewCache.delete(this.previewCache.keys().next().value);
|
|
1010
|
+
this.previewCache.set(key, pic);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (this.previewWanted === null || this.destroyed) break;
|
|
1014
|
+
if (pic) {
|
|
1015
|
+
const c = this.previewCanvas;
|
|
1016
|
+
if (c.width !== pic.width || c.height !== pic.height) {
|
|
1017
|
+
c.width = pic.width;
|
|
1018
|
+
c.height = pic.height;
|
|
1019
|
+
}
|
|
1020
|
+
c.getContext("2d").drawImage(pic, 0, 0);
|
|
1021
|
+
this.previewEl.setAttribute("data-show", "1");
|
|
1022
|
+
if (this.previewWanted === wanted) this.previewWanted = null;
|
|
1023
|
+
} else if (this.previewWanted !== wanted) {
|
|
1024
|
+
} else if (!this.movie.thumbnailsOff && this.previewRetries++ < 10) {
|
|
1025
|
+
this.previewRetry = setTimeout(() => {
|
|
1026
|
+
this.previewRetry = null;
|
|
1027
|
+
void this.runPreview();
|
|
1028
|
+
}, 120);
|
|
1029
|
+
break;
|
|
1030
|
+
} else {
|
|
1031
|
+
this.previewWanted = null;
|
|
1032
|
+
this.previewEl?.removeAttribute("data-show");
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
} finally {
|
|
1036
|
+
this.previewRunning = false;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
914
1039
|
seekFromPointer(clientX) {
|
|
915
1040
|
const rect = this.progressEl.getBoundingClientRect();
|
|
916
1041
|
const inset = _Controller.PROGRESS_INSET_PX;
|
|
@@ -1187,6 +1312,10 @@ var Controller = class _Controller {
|
|
|
1187
1312
|
if (this.destroyed) return;
|
|
1188
1313
|
this.destroyed = true;
|
|
1189
1314
|
this.stopPresenting();
|
|
1315
|
+
if (this.previewCleanup) {
|
|
1316
|
+
this.previewCleanup();
|
|
1317
|
+
this.previewCleanup = null;
|
|
1318
|
+
}
|
|
1190
1319
|
if (this.onReady) {
|
|
1191
1320
|
this.movie.off("ready", this.onReady);
|
|
1192
1321
|
this.onReady = null;
|