@editframe/elements 0.46.4 → 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/elements/EFMedia/BufferedSeekingInput.d.ts +50 -0
- package/dist/elements/EFMedia/BufferedSeekingInput.js +6 -5
- package/dist/elements/EFMedia/BufferedSeekingInput.js.map +1 -1
- package/dist/elements/EFMedia/CachedFetcher.js +23 -33
- package/dist/elements/EFMedia/CachedFetcher.js.map +1 -1
- package/dist/elements/EFMedia/SegmentTransport.d.ts +2 -2
- package/dist/elements/EFMedia/SegmentTransport.js.map +1 -1
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js +53 -0
- package/dist/elements/EFMedia/shared/ThumbnailExtractor.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js +20 -5
- package/dist/elements/EFMedia/videoTasks/MainVideoInputCache.js.map +1 -1
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.d.ts +48 -0
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js +36 -7
- package/dist/elements/EFMedia/videoTasks/ScrubInputCache.js.map +1 -1
- package/dist/elements/EFMotionBlur.d.ts +134 -0
- package/dist/elements/EFMotionBlur.js +809 -0
- package/dist/elements/EFMotionBlur.js.map +1 -0
- package/dist/elements/EFTemporal.js +1 -2
- package/dist/elements/EFTemporal.js.map +1 -1
- package/dist/elements/EFText.d.ts +20 -0
- package/dist/elements/EFText.js +66 -9
- package/dist/elements/EFText.js.map +1 -1
- package/dist/elements/EFTimegroup.d.ts +12 -0
- package/dist/elements/EFTimegroup.js +43 -4
- package/dist/elements/EFTimegroup.js.map +1 -1
- package/dist/elements/EFVideo.d.ts +26 -0
- package/dist/elements/EFVideo.js +114 -36
- package/dist/elements/EFVideo.js.map +1 -1
- package/dist/elements/SampleBuffer.d.ts +19 -0
- package/dist/elements/updateAnimations.js +49 -3
- package/dist/elements/updateAnimations.js.map +1 -1
- package/dist/gui/EFWorkbench.d.ts +1 -0
- package/dist/gui/EFWorkbench.js +15 -0
- package/dist/gui/EFWorkbench.js.map +1 -1
- package/dist/gui/EFWorkbench.spacebar.js +26 -0
- package/dist/gui/EFWorkbench.spacebar.js.map +1 -0
- package/dist/gui/TWMixin.js +1 -1
- package/dist/gui/TWMixin.js.map +1 -1
- package/dist/gui/timeline/EFTimeline.d.ts +18 -1
- package/dist/gui/timeline/EFTimeline.js +119 -25
- package/dist/gui/timeline/EFTimeline.js.map +1 -1
- package/dist/gui/timeline/timelineStateContext.d.ts +2 -0
- package/dist/gui/timeline/timelineStateContext.js.map +1 -1
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js +14 -8
- package/dist/gui/timeline/tracks/EFThumbnailStrip.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/preview/FrameController.d.ts +22 -1
- package/dist/preview/FrameController.js +26 -5
- package/dist/preview/FrameController.js.map +1 -1
- package/dist/preview/QualityUpgradeScheduler.d.ts +11 -2
- package/dist/preview/QualityUpgradeScheduler.js +31 -21
- package/dist/preview/QualityUpgradeScheduler.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.js +4 -0
- package/dist/preview/renderTimegroupToCanvas.js.map +1 -1
- package/dist/preview/renderTimegroupToCanvas.types.d.ts +2 -0
- package/dist/preview/renderTimegroupToVideo.js +3 -0
- package/dist/preview/renderTimegroupToVideo.js.map +1 -1
- package/dist/preview/rendering/serializeTimelineDirect.js +30 -35
- package/dist/preview/rendering/serializeTimelineDirect.js.map +1 -1
- package/dist/style.css +4 -0
- package/dist/utils/LRUCache.js +17 -5
- package/dist/utils/LRUCache.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -14,7 +14,15 @@ var ScrubInputCache = class {
|
|
|
14
14
|
#urlCache = /* @__PURE__ */ new Map();
|
|
15
15
|
#pendingBySegment = /* @__PURE__ */ new Map();
|
|
16
16
|
#pendingByUrl = /* @__PURE__ */ new Map();
|
|
17
|
-
#maxCacheSize
|
|
17
|
+
#maxCacheSize;
|
|
18
|
+
#maxUrlCacheSize;
|
|
19
|
+
/** Incremented on clear(). Promises capture the generation at creation time and
|
|
20
|
+
* skip writing back if the generation has advanced (cache was cleared). */
|
|
21
|
+
#generation = 0;
|
|
22
|
+
constructor(maxCacheSize = 20, maxUrlCacheSize = 5) {
|
|
23
|
+
this.#maxCacheSize = maxCacheSize;
|
|
24
|
+
this.#maxUrlCacheSize = maxUrlCacheSize;
|
|
25
|
+
}
|
|
18
26
|
/**
|
|
19
27
|
* Create a cache key that uniquely identifies a segment for a specific video
|
|
20
28
|
*/
|
|
@@ -35,12 +43,24 @@ var ScrubInputCache = class {
|
|
|
35
43
|
async getOrCreateInput(src, segmentId, createInputFn, scrubUrl) {
|
|
36
44
|
if (scrubUrl) {
|
|
37
45
|
const cached$1 = this.#urlCache.get(scrubUrl);
|
|
38
|
-
if (cached$1)
|
|
46
|
+
if (cached$1) {
|
|
47
|
+
this.#urlCache.delete(scrubUrl);
|
|
48
|
+
this.#urlCache.set(scrubUrl, cached$1);
|
|
49
|
+
return cached$1;
|
|
50
|
+
}
|
|
39
51
|
const pending$1 = this.#pendingByUrl.get(scrubUrl);
|
|
40
52
|
if (pending$1) return pending$1;
|
|
53
|
+
const capturedGeneration$1 = this.#generation;
|
|
41
54
|
const promise$1 = createInputFn().then((input) => {
|
|
42
55
|
this.#pendingByUrl.delete(scrubUrl);
|
|
43
|
-
if (input
|
|
56
|
+
if (input && this.#generation === capturedGeneration$1) {
|
|
57
|
+
this.#urlCache.set(scrubUrl, input);
|
|
58
|
+
while (this.#urlCache.size > this.#maxUrlCacheSize) {
|
|
59
|
+
const oldestKey = this.#urlCache.keys().next().value;
|
|
60
|
+
if (oldestKey !== void 0) this.#urlCache.delete(oldestKey);
|
|
61
|
+
else break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
44
64
|
return input;
|
|
45
65
|
}).catch((error) => {
|
|
46
66
|
this.#pendingByUrl.delete(scrubUrl);
|
|
@@ -51,16 +71,22 @@ var ScrubInputCache = class {
|
|
|
51
71
|
}
|
|
52
72
|
const cacheKey = this.#getCacheKey(src, segmentId);
|
|
53
73
|
const cached = this.#cache.get(cacheKey);
|
|
54
|
-
if (cached)
|
|
74
|
+
if (cached) {
|
|
75
|
+
this.#cache.delete(cacheKey);
|
|
76
|
+
this.#cache.set(cacheKey, cached);
|
|
77
|
+
return cached;
|
|
78
|
+
}
|
|
55
79
|
const pending = this.#pendingBySegment.get(cacheKey);
|
|
56
80
|
if (pending) return pending;
|
|
81
|
+
const capturedGeneration = this.#generation;
|
|
57
82
|
const promise = createInputFn().then((input) => {
|
|
58
83
|
this.#pendingBySegment.delete(cacheKey);
|
|
59
|
-
if (input) {
|
|
84
|
+
if (input && this.#generation === capturedGeneration) {
|
|
60
85
|
this.#cache.set(cacheKey, input);
|
|
61
|
-
|
|
86
|
+
while (this.#cache.size > this.#maxCacheSize) {
|
|
62
87
|
const oldestKey = this.#cache.keys().next().value;
|
|
63
88
|
if (oldestKey !== void 0) this.#cache.delete(oldestKey);
|
|
89
|
+
else break;
|
|
64
90
|
}
|
|
65
91
|
}
|
|
66
92
|
return input;
|
|
@@ -72,9 +98,12 @@ var ScrubInputCache = class {
|
|
|
72
98
|
return promise;
|
|
73
99
|
}
|
|
74
100
|
/**
|
|
75
|
-
* Clear the entire cache (called when video changes)
|
|
101
|
+
* Clear the entire cache (called when video changes).
|
|
102
|
+
* Increments the generation counter so any in-flight promises resolved after
|
|
103
|
+
* this call do not repopulate the cache with stale data from the old source.
|
|
76
104
|
*/
|
|
77
105
|
clear() {
|
|
106
|
+
this.#generation++;
|
|
78
107
|
this.#cache.clear();
|
|
79
108
|
this.#urlCache.clear();
|
|
80
109
|
this.#pendingBySegment.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScrubInputCache.js","names":["cached","#urlCache","pending","#pendingByUrl","promise","#getCacheKey","#cache","#pendingBySegment"
|
|
1
|
+
{"version":3,"file":"ScrubInputCache.js","names":["#maxCacheSize","#maxUrlCacheSize","cached","#urlCache","pending","#pendingByUrl","capturedGeneration","#generation","promise","#getCacheKey","#cache","#pendingBySegment"],"sources":["../../../../src/elements/EFMedia/videoTasks/ScrubInputCache.ts"],"sourcesContent":["import type { BufferedSeekingInput } from \"../BufferedSeekingInput\";\n\n/**\n * Cache for scrub BufferedSeekingInput instances.\n *\n * For JIT media (segmented scrub tracks), caches by src + segment ID.\n * For Asset media (single-file scrub tracks), caches by URL so all segments\n * share the same BufferedSeekingInput instance.\n *\n * Uses promise deduplication to prevent race conditions when multiple\n * concurrent requests arrive for the same segment.\n */\nexport class ScrubInputCache {\n #cache = new Map<string, BufferedSeekingInput>();\n #urlCache = new Map<string, BufferedSeekingInput>();\n #pendingBySegment = new Map<string, Promise<BufferedSeekingInput | undefined>>();\n #pendingByUrl = new Map<string, Promise<BufferedSeekingInput | undefined>>();\n #maxCacheSize: number;\n #maxUrlCacheSize: number;\n /** Incremented on clear(). Promises capture the generation at creation time and\n * skip writing back if the generation has advanced (cache was cleared). */\n #generation = 0;\n\n constructor(maxCacheSize = 20, maxUrlCacheSize = 5) {\n this.#maxCacheSize = maxCacheSize;\n this.#maxUrlCacheSize = maxUrlCacheSize;\n }\n\n /**\n * Create a cache key that uniquely identifies a segment for a specific video\n */\n #getCacheKey(src: string, segmentId: number): string {\n return `${src}:${segmentId}`;\n }\n\n /**\n * Get or create BufferedSeekingInput for a scrub segment.\n *\n * Uses promise deduplication to prevent race conditions when multiple\n * concurrent requests arrive for the same segment.\n *\n * @param src - The source URL of the video (required to distinguish between videos)\n * @param segmentId - The segment ID\n * @param createInputFn - Factory function to create the input\n * @param scrubUrl - Optional URL for single-file scrub tracks (all segments share same input)\n */\n async getOrCreateInput(\n src: string,\n segmentId: number,\n createInputFn: () => Promise<BufferedSeekingInput | undefined>,\n scrubUrl?: string,\n ): Promise<BufferedSeekingInput | undefined> {\n // For single-file scrub tracks (AssetMediaEngine), use URL-based caching\n // This ensures all segments share the same BufferedSeekingInput\n if (scrubUrl) {\n // Check completed cache\n const cached = this.#urlCache.get(scrubUrl);\n if (cached) {\n // Promote to MRU position so the eviction policy is true LRU\n this.#urlCache.delete(scrubUrl);\n this.#urlCache.set(scrubUrl, cached);\n return cached;\n }\n\n // Check pending requests (deduplication)\n const pending = this.#pendingByUrl.get(scrubUrl);\n if (pending) {\n return pending;\n }\n\n // Create promise and cache immediately. Capture the generation so that\n // if clear() fires while this promise is in-flight, the .then() callback\n // does not repopulate the cache with data from the old source.\n const capturedGeneration = this.#generation;\n const promise = createInputFn()\n .then((input) => {\n this.#pendingByUrl.delete(scrubUrl);\n if (input && this.#generation === capturedGeneration) {\n this.#urlCache.set(scrubUrl, input);\n // Evict oldest URL entries if cache is too large\n while (this.#urlCache.size > this.#maxUrlCacheSize) {\n const oldestKey = this.#urlCache.keys().next().value;\n if (oldestKey !== undefined) {\n this.#urlCache.delete(oldestKey);\n } else {\n break;\n }\n }\n }\n return input;\n })\n .catch((error) => {\n this.#pendingByUrl.delete(scrubUrl);\n throw error;\n });\n\n this.#pendingByUrl.set(scrubUrl, promise);\n return promise;\n }\n\n // For segmented scrub tracks (JIT), use src + segment-based caching\n const cacheKey = this.#getCacheKey(src, segmentId);\n const cached = this.#cache.get(cacheKey);\n if (cached) {\n // Promote to MRU position so the eviction policy is true LRU\n this.#cache.delete(cacheKey);\n this.#cache.set(cacheKey, cached);\n return cached;\n }\n\n // Check pending requests (deduplication)\n const pending = this.#pendingBySegment.get(cacheKey);\n if (pending) {\n return pending;\n }\n\n // Create promise and cache immediately. Capture the generation so that\n // if clear() fires while this promise is in-flight, the .then() callback\n // does not repopulate the cache with data from the old source.\n const capturedGeneration = this.#generation;\n const promise = createInputFn()\n .then((input) => {\n this.#pendingBySegment.delete(cacheKey);\n\n if (input && this.#generation === capturedGeneration) {\n this.#cache.set(cacheKey, input);\n\n // Evict oldest entries if cache is too large\n while (this.#cache.size > this.#maxCacheSize) {\n const oldestKey = this.#cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.#cache.delete(oldestKey);\n } else {\n break;\n }\n }\n }\n\n return input;\n })\n .catch((error) => {\n this.#pendingBySegment.delete(cacheKey);\n throw error;\n });\n\n this.#pendingBySegment.set(cacheKey, promise);\n return promise;\n }\n\n /**\n * Clear the entire cache (called when video changes).\n * Increments the generation counter so any in-flight promises resolved after\n * this call do not repopulate the cache with stale data from the old source.\n */\n clear() {\n this.#generation++;\n this.#cache.clear();\n this.#urlCache.clear();\n this.#pendingBySegment.clear();\n this.#pendingByUrl.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats() {\n return {\n size: this.#cache.size,\n urlCacheSize: this.#urlCache.size,\n pendingCount: this.#pendingBySegment.size + this.#pendingByUrl.size,\n cacheKeys: Array.from(this.#cache.keys()),\n };\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAa,kBAAb,MAA6B;CAC3B,yBAAS,IAAI,KAAmC;CAChD,4BAAY,IAAI,KAAmC;CACnD,oCAAoB,IAAI,KAAwD;CAChF,gCAAgB,IAAI,KAAwD;CAC5E;CACA;;;CAGA,cAAc;CAEd,YAAY,eAAe,IAAI,kBAAkB,GAAG;AAClD,QAAKA,eAAgB;AACrB,QAAKC,kBAAmB;;;;;CAM1B,aAAa,KAAa,WAA2B;AACnD,SAAO,GAAG,IAAI,GAAG;;;;;;;;;;;;;CAcnB,MAAM,iBACJ,KACA,WACA,eACA,UAC2C;AAG3C,MAAI,UAAU;GAEZ,MAAMC,WAAS,MAAKC,SAAU,IAAI,SAAS;AAC3C,OAAID,UAAQ;AAEV,UAAKC,SAAU,OAAO,SAAS;AAC/B,UAAKA,SAAU,IAAI,UAAUD,SAAO;AACpC,WAAOA;;GAIT,MAAME,YAAU,MAAKC,aAAc,IAAI,SAAS;AAChD,OAAID,UACF,QAAOA;GAMT,MAAME,uBAAqB,MAAKC;GAChC,MAAMC,YAAU,eAAe,CAC5B,MAAM,UAAU;AACf,UAAKH,aAAc,OAAO,SAAS;AACnC,QAAI,SAAS,MAAKE,eAAgBD,sBAAoB;AACpD,WAAKH,SAAU,IAAI,UAAU,MAAM;AAEnC,YAAO,MAAKA,SAAU,OAAO,MAAKF,iBAAkB;MAClD,MAAM,YAAY,MAAKE,SAAU,MAAM,CAAC,MAAM,CAAC;AAC/C,UAAI,cAAc,OAChB,OAAKA,SAAU,OAAO,UAAU;UAEhC;;;AAIN,WAAO;KACP,CACD,OAAO,UAAU;AAChB,UAAKE,aAAc,OAAO,SAAS;AACnC,UAAM;KACN;AAEJ,SAAKA,aAAc,IAAI,UAAUG,UAAQ;AACzC,UAAOA;;EAIT,MAAM,WAAW,MAAKC,YAAa,KAAK,UAAU;EAClD,MAAM,SAAS,MAAKC,MAAO,IAAI,SAAS;AACxC,MAAI,QAAQ;AAEV,SAAKA,MAAO,OAAO,SAAS;AAC5B,SAAKA,MAAO,IAAI,UAAU,OAAO;AACjC,UAAO;;EAIT,MAAM,UAAU,MAAKC,iBAAkB,IAAI,SAAS;AACpD,MAAI,QACF,QAAO;EAMT,MAAM,qBAAqB,MAAKJ;EAChC,MAAM,UAAU,eAAe,CAC5B,MAAM,UAAU;AACf,SAAKI,iBAAkB,OAAO,SAAS;AAEvC,OAAI,SAAS,MAAKJ,eAAgB,oBAAoB;AACpD,UAAKG,MAAO,IAAI,UAAU,MAAM;AAGhC,WAAO,MAAKA,MAAO,OAAO,MAAKV,cAAe;KAC5C,MAAM,YAAY,MAAKU,MAAO,MAAM,CAAC,MAAM,CAAC;AAC5C,SAAI,cAAc,OAChB,OAAKA,MAAO,OAAO,UAAU;SAE7B;;;AAKN,UAAO;IACP,CACD,OAAO,UAAU;AAChB,SAAKC,iBAAkB,OAAO,SAAS;AACvC,SAAM;IACN;AAEJ,QAAKA,iBAAkB,IAAI,UAAU,QAAQ;AAC7C,SAAO;;;;;;;CAQT,QAAQ;AACN,QAAKJ;AACL,QAAKG,MAAO,OAAO;AACnB,QAAKP,SAAU,OAAO;AACtB,QAAKQ,iBAAkB,OAAO;AAC9B,QAAKN,aAAc,OAAO;;;;;CAM5B,WAAW;AACT,SAAO;GACL,MAAM,MAAKK,MAAO;GAClB,cAAc,MAAKP,SAAU;GAC7B,cAAc,MAAKQ,iBAAkB,OAAO,MAAKN,aAAc;GAC/D,WAAW,MAAM,KAAK,MAAKK,MAAO,MAAM,CAAC;GAC1C"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
//#region src/elements/EFMotionBlur.d.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `<ef-motionblur>` applies motion blur to its child for two types of motion:
|
|
5
|
+
*
|
|
6
|
+
* **Translation** — N `feOffset` copies along the velocity vector, Gaussian-blended.
|
|
7
|
+
*
|
|
8
|
+
* **Spiral** (rotation + zoom combined) — N displaced copies using a single
|
|
9
|
+
* spiral vector map that encodes the weighted sum of tangential (rotation) and
|
|
10
|
+
* radial (zoom) components. Because tangent ⊥ radial with equal magnitude,
|
|
11
|
+
* the combined map always has magnitude r/maxR regardless of the rot/zoom ratio.
|
|
12
|
+
* One pass correctly captures simultaneous rotation and scaling as a spiral
|
|
13
|
+
* smear rather than the generic blur that sequential stages produce.
|
|
14
|
+
*/
|
|
15
|
+
declare const _EFMotionBlurBase: {
|
|
16
|
+
new (): HTMLElement;
|
|
17
|
+
prototype: HTMLElement;
|
|
18
|
+
};
|
|
19
|
+
declare class EFMotionBlur extends _EFMotionBlurBase {
|
|
20
|
+
static observedAttributes: string[];
|
|
21
|
+
/** Controls filter primitive density. "preview" for real-time playback, "render" for export. */
|
|
22
|
+
static renderQuality: "preview" | "render";
|
|
23
|
+
private _outer;
|
|
24
|
+
private _filterEl;
|
|
25
|
+
private _svg;
|
|
26
|
+
private _filterId;
|
|
27
|
+
private _spiralMapURI;
|
|
28
|
+
private _lastMapW;
|
|
29
|
+
private _lastMapH;
|
|
30
|
+
private _lastSpiralAngle;
|
|
31
|
+
private _filterNode;
|
|
32
|
+
private _transOffsets;
|
|
33
|
+
private _spiralDisplacements;
|
|
34
|
+
private _spiralFeImage;
|
|
35
|
+
private _spiralBlur;
|
|
36
|
+
private _lastStepConfig;
|
|
37
|
+
/** Translation blur: pixel displacement in X (screen space). */
|
|
38
|
+
_computedDx: number;
|
|
39
|
+
/** Translation blur: pixel displacement in Y (screen space). */
|
|
40
|
+
_computedDy: number;
|
|
41
|
+
_computedAngle: number;
|
|
42
|
+
_computedDirectionalAmount: number;
|
|
43
|
+
/** Signed rotation arc displacement (px at half-diagonal). Negative = CW, positive = CCW. */
|
|
44
|
+
_computedRotAmount: number;
|
|
45
|
+
/** Absolute rotation delta in degrees (for step count calculation). */
|
|
46
|
+
_computedRotDeg: number;
|
|
47
|
+
_computedIsotropicAmount: number;
|
|
48
|
+
_hasExplicitAngle: boolean;
|
|
49
|
+
_hasExplicitAmount: boolean;
|
|
50
|
+
get _computedAmount(): number;
|
|
51
|
+
set _computedAmount(v: number);
|
|
52
|
+
connectedCallback(): void;
|
|
53
|
+
disconnectedCallback(): void;
|
|
54
|
+
attributeChangedCallback(name: string, _old: string | null, newValue: string | null): void;
|
|
55
|
+
get angle(): number;
|
|
56
|
+
set angle(v: number);
|
|
57
|
+
get amount(): number;
|
|
58
|
+
set amount(v: number);
|
|
59
|
+
get shutterAngle(): number;
|
|
60
|
+
set shutterAngle(v: number);
|
|
61
|
+
get fps(): number;
|
|
62
|
+
set fps(v: number);
|
|
63
|
+
get sensitivity(): number;
|
|
64
|
+
set sensitivity(v: number);
|
|
65
|
+
get threshold(): number;
|
|
66
|
+
set threshold(v: number);
|
|
67
|
+
get shutterMs(): number;
|
|
68
|
+
/**
|
|
69
|
+
* One-time DOM setup. Creates the SVG filter host and two wrapper divs
|
|
70
|
+
* (_outer for the SVG definition, _filterEl for the filtered content).
|
|
71
|
+
* The SVG filter is rebuilt dynamically in _update().
|
|
72
|
+
*/
|
|
73
|
+
private _inject;
|
|
74
|
+
/**
|
|
75
|
+
* Rebuilds wrapper transforms and the SVG filter. Called on attribute changes
|
|
76
|
+
* and after each sample().
|
|
77
|
+
*
|
|
78
|
+
* The SVG contains up to two filter stages:
|
|
79
|
+
* 1. feOffset×N — directional (translation) blur
|
|
80
|
+
* 2. Spiral (feImage + feDisplacementMap×N + feComposite blend + feGaussianBlur)
|
|
81
|
+
* — combined rotation + zoom in one pass via a spiral vector map.
|
|
82
|
+
*
|
|
83
|
+
* On first call (or when step counts change), builds the full SVG filter DOM.
|
|
84
|
+
* On subsequent calls, updates only the attributes that change per frame.
|
|
85
|
+
*/
|
|
86
|
+
private _update;
|
|
87
|
+
/**
|
|
88
|
+
* Refreshes the serialization cache on _outer. The serializer reads this
|
|
89
|
+
* instead of calling XMLSerializer on the SVG defs, which is important
|
|
90
|
+
* when rotation/zoom displacement maps embed large base64 data URIs.
|
|
91
|
+
*/
|
|
92
|
+
private _refreshSerializationCache;
|
|
93
|
+
/**
|
|
94
|
+
* Builds the full SVG filter DOM tree. Called once when step configuration changes.
|
|
95
|
+
* Stores references to mutable elements for fast per-frame attribute updates.
|
|
96
|
+
*/
|
|
97
|
+
private _buildFilterDOM;
|
|
98
|
+
/**
|
|
99
|
+
* Generates or refreshes the spiral displacement map.
|
|
100
|
+
* Cached by element size and spiral angle (quantized to ~5°).
|
|
101
|
+
* The feImage href is updated in the fast path when the map URI changes.
|
|
102
|
+
*/
|
|
103
|
+
private _ensureMaps;
|
|
104
|
+
/**
|
|
105
|
+
* Samples the child's current position and transform, computing blur amounts.
|
|
106
|
+
* Called by `updateAnimations` after all animation times are settled.
|
|
107
|
+
*/
|
|
108
|
+
sample(_timestamp?: number): void;
|
|
109
|
+
/**
|
|
110
|
+
* Deterministic two-seek measurement. Seeks all animations back by shutterMs,
|
|
111
|
+
* reads positions and transforms, restores. Returns null when no seekable
|
|
112
|
+
* animations exist or currentTime < shutterMs.
|
|
113
|
+
*/
|
|
114
|
+
private _computeDeterministicTransform;
|
|
115
|
+
/**
|
|
116
|
+
* Converts measured deltas to blur amounts and calls _update() when changed.
|
|
117
|
+
*
|
|
118
|
+
* Translation → directional box blur amount + angle.
|
|
119
|
+
* Rotation → arc displacement amount (signed: + = CCW, − = CW).
|
|
120
|
+
* Arc length at half-diagonal radius: R × |Δθ_rad|.
|
|
121
|
+
* Stored signed so the displacement map is applied in the
|
|
122
|
+
* correct tangential direction.
|
|
123
|
+
* Scale → radial displacement amount: |Δscale| × half-diagonal.
|
|
124
|
+
*/
|
|
125
|
+
private _applyTransform;
|
|
126
|
+
}
|
|
127
|
+
declare global {
|
|
128
|
+
interface HTMLElementTagNameMap {
|
|
129
|
+
"ef-motionblur": EFMotionBlur;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//#endregion
|
|
133
|
+
export { EFMotionBlur };
|
|
134
|
+
//# sourceMappingURL=EFMotionBlur.d.ts.map
|