@editframe/elements 0.47.1 → 0.47.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"EFMotionBlur.js","names":["result","u2","u3","EASING_KEYWORDS: Record<string, [number, number, number, number]>","seekable: Animation[]","currentTimes: number[]"],"sources":["../../src/elements/EFMotionBlur.ts"],"sourcesContent":["import { getTrackedAnimationsForSubtree } from \"./updateAnimations.js\";\n\n// ── Tuning constants ─────────────────────────────────────────────────────────\n// All magic numbers that control blur quality live here. Changing any value\n// is immediately visible as a single-location diff and can be reviewed\n// against the others.\n\n/** Multiplier on rotAmount → rotScale for centered displacement range. */\nexport const ROT_SCALE_FACTOR = 2;\n/** Multiplier on isotropicAmount → zoomScale. >2 amplifies zoom visibility;\n * higher values tilt the spiral angle toward radial. */\nexport const ZOOM_SCALE_FACTOR = 3;\n/** Maximum arc displacement in pixels (caps rotAmount). */\nconst MAX_ROT_AMOUNT_PX = 50;\n/** Maximum zoom displacement in pixels (caps isotropicAmount). */\nconst MAX_ZOOM_AMOUNT_PX = 50;\n/** Maximum translation displacement in pixels. */\nconst MAX_TRANS_AMOUNT_PX = 100;\n/** Gaussian sigma for temporal weights: sigma = max(n / SIGMA_DIVISOR, 2). */\nconst GAUSSIAN_SIGMA_DIVISOR = 5.5;\n/** Spiral output blur: coefficient on step spacing. Must stay small to\n * preserve zero-displacement center sharpness. */\nexport const SPIRAL_BLUR_COEFF = 0.15;\n/** Spiral output blur: hard cap in pixels. */\nexport const SPIRAL_BLUR_CAP = 0.8;\n/** Spiral output blur: floor in pixels. */\nconst SPIRAL_BLUR_FLOOR = 0.3;\n/** Translation output blur: coefficient on step spacing. Can be larger than\n * spiral because all pixels shift equally (no center-vs-edge gradient). */\nconst TRANS_BLUR_COEFF = 0.35;\n/** Translation output blur: hard cap in pixels. */\nconst TRANS_BLUR_CAP = 1.5;\n/** Translation output blur: floor in pixels. */\nconst TRANS_BLUR_FLOOR = 0.3;\n/** Spiral angle quantization: angles rounded to π/(12*2) ≈ 7.5° steps. */\nconst SPIRAL_ANGLE_QUANT = 12;\n\n/**\n * Extracts the 2D rotation angle (degrees) and uniform scale from a CSS\n * transform string. Handles `matrix(a,b,c,d,tx,ty)`, `rotate(Ndeg)`,\n * `scale(N)`, and `none`.\n */\nfunction parseMatrix(transform: string): { angle: number; scale: number } {\n if (!transform || transform === \"none\") return { angle: 0, scale: 1 };\n\n // matrix(a, b, c, d, tx, ty)\n const mm = transform.match(/^matrix\\(([^)]+)\\)/);\n if (mm) {\n const [a, b] = mm[1]!.split(\",\").map(Number) as [number, number, ...number[]];\n const scale = Math.sqrt(a! * a! + b! * b!);\n const angle = (Math.atan2(b!, a!) * 180) / Math.PI;\n return { angle, scale: scale || 1 };\n }\n\n // rotate(Ndeg) or rotate(Nrad)\n const rm = transform.match(/rotate\\(([^)]+)\\)/);\n if (rm) {\n const v = rm[1]!.trim();\n const angle = v.endsWith(\"rad\") ? (parseFloat(v) * 180) / Math.PI : parseFloat(v); // deg\n return { angle, scale: 1 };\n }\n\n // scale(N) or scale(N, N)\n const sm = transform.match(/^scale\\(([^)]+)\\)/);\n if (sm) {\n const parts = sm[1]!.split(\",\").map(Number);\n return { angle: 0, scale: parts[0] ?? 1 };\n }\n\n return { angle: 0, scale: 1 };\n}\n\n/**\n * Reads the rotation angle and scale from an element's current animation state.\n *\n * Prefers direct keyframe interpolation via `getComputedTiming().progress` +\n * `getKeyframes()` — this is synchronous and not subject to Chromium's deferred\n * WAAPI style application (where `getComputedStyle` may lag behind `currentTime`).\n *\n * Falls back to `commitStyles()` + `getComputedStyle` for animations whose keyframes\n * use complex transform lists that our simple interpolation can't handle.\n */\nfunction readTransformMatrix(\n child: HTMLElement,\n anims: Animation[],\n): { angle: number; scale: number } {\n // Preferred path: analytical interpolation from keyframes.\n // Works reliably for cancelled CSS animations whose getComputedStyle output\n // is deferred by Chromium until the next compositor frame.\n for (const anim of anims) {\n const effect = anim.effect;\n if (!(effect instanceof KeyframeEffect)) continue;\n const timing = effect.getComputedTiming();\n const progress = timing.progress;\n if (progress === null || progress === undefined) continue;\n const keyframes = effect.getKeyframes();\n const result = interpolateTransformKeyframes(keyframes, progress as number);\n if (result !== null) return result;\n }\n\n // Fallback: commitStyles() + getComputedStyle.\n let committed = false;\n for (const anim of anims) {\n try {\n (anim as any).commitStyles?.();\n committed = true;\n } catch (_) {\n /* fill:none or not replaceable */\n }\n }\n void child.getBoundingClientRect();\n const result = parseMatrix(getComputedStyle(child).transform);\n if (committed) child.style.removeProperty(\"transform\");\n return result;\n}\n\n/**\n * Decomposes a CSS transform string into cumulative rotation (degrees) and\n * uniform scale. Handles compound transforms like `scale(0.3) rotate(90deg)`,\n * pure rotations, pure scales, and matrix() values.\n *\n * Multiple rotate() functions are summed (so the orbit pattern\n * `rotate(360deg) translateX(90px) rotate(-360deg)` correctly yields 0°).\n *\n * Returns null only when no rotation or scale function is found and the\n * string is not a matrix.\n */\nfunction decomposeKeyframeTransform(transform: string): { angle: number; scale: number } | null {\n if (!transform || transform === \"none\") return { angle: 0, scale: 1 };\n\n const mm = transform.match(/^matrix\\(([^)]+)\\)/);\n if (mm) {\n const [a, b] = mm[1]!.split(\",\").map(Number) as [number, number, ...number[]];\n const scale = Math.sqrt(a! * a! + b! * b!);\n const angle = (Math.atan2(b!, a!) * 180) / Math.PI;\n return { angle, scale: scale || 1 };\n }\n\n let totalAngle = 0;\n let totalScale = 1;\n let found = false;\n\n for (const m of transform.matchAll(/rotate\\(([^)]+)\\)/g)) {\n const v = m[1]!.trim();\n totalAngle += v.endsWith(\"rad\") ? (parseFloat(v) * 180) / Math.PI : parseFloat(v);\n found = true;\n }\n\n const sm = transform.match(/scale\\(([^),]+)(?:,\\s*([^)]+))?\\)/);\n if (sm) {\n const sx = parseFloat(sm[1]!);\n const sy = sm[2] !== undefined ? parseFloat(sm[2]!) : sx;\n if (Math.abs(sx - sy) < 0.001) {\n totalScale = sx;\n found = true;\n }\n }\n\n return found ? { angle: totalAngle, scale: totalScale } : null;\n}\n\n/**\n * Solves a cubic-bezier curve: given control points (x1,y1,x2,y2) and input\n * parameter `t` (0–1), returns the output value y. Uses Newton-Raphson\n * iteration to invert the x(u) polynomial.\n */\nfunction solveCubicBezier(x1: number, y1: number, x2: number, y2: number, t: number): number {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n let u = t;\n for (let i = 0; i < 8; i++) {\n const u2 = u * u,\n u3 = u2 * u;\n const xu = 3 * (1 - u) * (1 - u) * u * x1 + 3 * (1 - u) * u2 * x2 + u3;\n const dxu = 3 * (1 - u) * (1 - u) * x1 + 6 * (1 - u) * u * (x2 - x1) + 3 * u2 * (1 - x2);\n if (Math.abs(dxu) < 1e-9) break;\n u -= (xu - t) / dxu;\n u = Math.max(0, Math.min(1, u));\n }\n const u2 = u * u,\n u3 = u2 * u;\n return 3 * (1 - u) * (1 - u) * u * y1 + 3 * (1 - u) * u2 * y2 + u3;\n}\n\nconst EASING_KEYWORDS: Record<string, [number, number, number, number]> = {\n ease: [0.25, 0.1, 0.25, 1.0],\n \"ease-in\": [0.42, 0, 1.0, 1.0],\n \"ease-out\": [0, 0, 0.58, 1.0],\n \"ease-in-out\": [0.42, 0, 0.58, 1.0],\n};\n\n/**\n * Applies a CSS easing function to a linear parameter t (0–1).\n * Handles keyword easings and `cubic-bezier(x1,y1,x2,y2)`.\n */\nfunction applyEasing(easing: string | undefined | null, t: number): number {\n if (!easing || easing === \"linear\") return t;\n const kw = EASING_KEYWORDS[easing];\n if (kw) return solveCubicBezier(kw[0], kw[1], kw[2], kw[3], t);\n const cbm = easing.match(/cubic-bezier\\(\\s*([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/);\n if (cbm)\n return solveCubicBezier(\n parseFloat(cbm[1]!),\n parseFloat(cbm[2]!),\n parseFloat(cbm[3]!),\n parseFloat(cbm[4]!),\n t,\n );\n return t;\n}\n\n/**\n * Interpolates angle (degrees) and scale from a WAAPI keyframe list at the\n * given progress (0–1).\n *\n * Uses the original CSS value strings from the keyframes (not computed matrices)\n * so `rotate(0deg) → rotate(360deg)` interpolates correctly as 0→360°.\n *\n * Handles compound transforms (e.g. `scale(0.3) rotate(90deg)`) by decomposing\n * each keyframe individually, and applies per-keyframe easing so that blur\n * intensity tracks the actual eased velocity rather than the linear one.\n */\nfunction interpolateTransformKeyframes(\n keyframes: ComputedKeyframe[],\n progress: number,\n): { angle: number; scale: number } | null {\n const withTransform = keyframes.filter((k) => k.transform != null);\n if (withTransform.length < 2) return null;\n\n let lo = withTransform[0]!;\n let hi = withTransform[withTransform.length - 1]!;\n for (let i = 0; i < withTransform.length - 1; i++) {\n const a = withTransform[i]!;\n const b = withTransform[i + 1]!;\n const aOff = typeof a.offset === \"number\" ? a.offset : i / (withTransform.length - 1);\n const bOff = typeof b.offset === \"number\" ? b.offset : (i + 1) / (withTransform.length - 1);\n if (progress >= aOff && progress <= bOff) {\n lo = a;\n hi = b;\n break;\n }\n }\n\n const loOff = typeof lo.offset === \"number\" ? lo.offset : 0;\n const hiOff = typeof hi.offset === \"number\" ? hi.offset : 1;\n const linearT = hiOff === loOff ? 0 : (progress - loOff) / (hiOff - loOff);\n const t = applyEasing(lo.easing as string | undefined, linearT);\n\n const loD = decomposeKeyframeTransform(String(lo.transform ?? \"none\"));\n const hiD = decomposeKeyframeTransform(String(hi.transform ?? \"none\"));\n if (!loD || !hiD) return null;\n if (loD.angle === 0 && hiD.angle === 0 && loD.scale === 1 && hiD.scale === 1) return null;\n\n return {\n angle: loD.angle + (hiD.angle - loD.angle) * t,\n scale: loD.scale + (hiD.scale - loD.scale) * t,\n };\n}\n\n/**\n * Gaussian temporal weights for N blur steps.\n * Soft window (vs equal weights) eliminates the \"stack of discrete frames\" look.\n */\nfunction gaussianWeights(n: number): number[] {\n if (n <= 1) return [1];\n const mid = (n - 1) / 2;\n const sigma = Math.max(n / GAUSSIAN_SIGMA_DIVISOR, 2);\n return Array.from({ length: n }, (_, i) => Math.exp(-0.5 * ((i - mid) / sigma) ** 2));\n}\n\n/**\n * Maximum distance from a point to any corner of a rectangle.\n */\nfunction maxDistToCorner(cx: number, cy: number, w: number, h: number): number {\n let m = 0;\n for (const px of [0, w])\n for (const py of [0, h]) {\n const d = Math.hypot(px - cx, py - cy);\n if (d > m) m = d;\n }\n return m || 1;\n}\n\n/**\n * Generates a canvas displacement map encoding a **spiral** vector field.\n *\n * Each pixel encodes a 2D displacement direction as (R, G) where:\n * R = 128 + vx*127 G = 128 + vy*127\n *\n * The vector at each pixel is:\n * v = sin(spiralAngle) × tangent + cos(spiralAngle) × radial\n *\n * Because tangent ⊥ radial and both have magnitude r/maxR, the combined\n * vector also has magnitude r/maxR regardless of spiralAngle. This means\n * one map + one set of N displaced copies correctly captures simultaneous\n * rotation and zoom as a single spiral smear — no sequential stages needed.\n *\n * spiralAngle = atan2(rotScale, zoomScale):\n * π/2 → pure tangential (rotation only)\n * 0 → pure radial (zoom only)\n * between → spiral\n */\nfunction buildSpiralMap(\n pw: number,\n ph: number,\n ox: number,\n oy: number,\n iw: number,\n ih: number,\n cx: number,\n cy: number,\n spiralAngle: number,\n): string {\n const canvas = new OffscreenCanvas(pw, ph);\n const ctx = canvas.getContext(\"2d\")!;\n const img = ctx.createImageData(pw, ph);\n const maxR = maxDistToCorner(cx, cy, iw, ih);\n const sinA = Math.sin(spiralAngle);\n const cosA = Math.cos(spiralAngle);\n\n for (let py = 0; py < ph; py++) {\n for (let px = 0; px < pw; px++) {\n const ux = px - ox;\n const uy = py - oy;\n const i = (py * pw + px) * 4;\n const dx = ux - cx;\n const dy = uy - cy;\n const vx = sinA * (-dy / maxR) + cosA * (dx / maxR);\n const vy = sinA * (dx / maxR) + cosA * (dy / maxR);\n img.data[i] = Math.round(128 + vx * 127);\n img.data[i + 1] = Math.round(128 + vy * 127);\n img.data[i + 2] = 128;\n img.data[i + 3] = 255;\n }\n }\n ctx.putImageData(img, 0, 0);\n const c = document.createElement(\"canvas\");\n c.width = pw;\n c.height = ph;\n c.getContext(\"2d\")!.putImageData(img, 0, 0);\n return c.toDataURL();\n}\n\n/**\n * Appends the SVG filter primitives for one temporal displacement blur stage\n * into an existing filter string. Takes `inResult` as input and writes\n * the final blended output to `outResult`.\n *\n * When `centered=true`, steps run from -totalScale/2 → +totalScale/2, placing\n * the un-displaced copy at the center of the range. This is used for rotation\n * blur so the current frame sits in the middle of the smear.\n * When `centered=false` (default), steps run from 0 → totalScale.\n *\n * Produces N copies each displaced by the map at increasing scale,\n * blended with Gaussian temporal weights. A small feGaussianBlur softens slice edges.\n */\nfunction appendTemporalBlurStage(\n xml: string,\n prefix: string,\n inResult: string,\n outResult: string,\n steps: number,\n totalScale: number,\n mapResult: string,\n centered = false,\n): string {\n const weights = gaussianWeights(steps);\n // Minimal smoothing to merge adjacent displaced copies at the element edges.\n // Must be conservative because feGaussianBlur is isotropic — it softens the\n // CENTER (where displacement is zero and the image should stay sharp) just as\n // much as the edges. Gaussian temporal weighting already handles most blending.\n const stepSpacing = steps > 1 ? Math.abs(totalScale) / (steps - 1) / 2 : 0;\n const outBlur = Math.min(\n SPIRAL_BLUR_CAP,\n Math.max(SPIRAL_BLUR_FLOOR, stepSpacing * SPIRAL_BLUR_COEFF),\n );\n\n for (let i = 0; i < steps; i++) {\n const t = steps > 1 ? i / (steps - 1) : 0;\n const scale = centered ? ((t - 0.5) * totalScale).toFixed(4) : (t * totalScale).toFixed(4);\n xml += ` <feDisplacementMap in=\"${inResult}\" in2=\"${mapResult}\"\n scale=\"${scale}\" xChannelSelector=\"R\" yChannelSelector=\"G\"\n result=\"${prefix}s${i}\"/>\\n`;\n }\n\n xml += ` <feComposite in=\"${prefix}s0\" in2=\"${prefix}s0\" operator=\"arithmetic\"\n k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\" result=\"${prefix}avg0\"/>\\n`;\n let sumW = weights[0]!;\n for (let i = 1; i < steps; i++) {\n const sumWNew = sumW + weights[i]!;\n const k2 = (sumW / sumWNew).toFixed(8);\n const k3 = (weights[i]! / sumWNew).toFixed(8);\n xml += ` <feComposite in=\"${prefix}avg${i - 1}\" in2=\"${prefix}s${i}\" operator=\"arithmetic\"\n k1=\"0\" k2=\"${k2}\" k3=\"${k3}\" k4=\"0\" result=\"${prefix}avg${i}\"/>\\n`;\n sumW = sumWNew;\n }\n\n xml += ` <feGaussianBlur in=\"${prefix}avg${steps - 1}\"\n stdDeviation=\"${outBlur.toFixed(3)}\" result=\"${outResult}\"/>\\n`;\n return xml;\n}\n\n/**\n * Appends SVG filter primitives for a directional translation blur using\n * feOffset. Produces N copies of `inResult` shifted along (vx, vy) from\n * 0 → (totalDx, totalDy), blended with Gaussian temporal weights.\n * No wrapper rotation needed — direction is encoded directly as dx/dy offsets.\n */\nfunction appendTranslationBlurStage(\n xml: string,\n prefix: string,\n inResult: string,\n outResult: string,\n steps: number,\n totalDx: number,\n totalDy: number,\n): string {\n const weights = gaussianWeights(steps);\n const sweep = Math.sqrt(totalDx ** 2 + totalDy ** 2);\n // Translation: all pixels shift equally, so isotropic blur doesn't selectively\n // soften any region. Can tolerate more smoothing than the spiral stage.\n const stepSpacing = steps > 1 ? sweep / (steps - 1) : 0;\n const outBlur = Math.min(\n TRANS_BLUR_CAP,\n Math.max(TRANS_BLUR_FLOOR, stepSpacing * TRANS_BLUR_COEFF),\n );\n\n for (let i = 0; i < steps; i++) {\n const t = steps > 1 ? i / (steps - 1) : 0;\n const dx = (t * totalDx).toFixed(3);\n const dy = (t * totalDy).toFixed(3);\n xml += ` <feOffset in=\"${inResult}\" dx=\"${dx}\" dy=\"${dy}\" result=\"${prefix}s${i}\"/>\\n`;\n }\n\n xml += ` <feComposite in=\"${prefix}s0\" in2=\"${prefix}s0\" operator=\"arithmetic\"\n k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\" result=\"${prefix}avg0\"/>\\n`;\n let sumW = weights[0]!;\n for (let i = 1; i < steps; i++) {\n const sumWNew = sumW + weights[i]!;\n const k2 = (sumW / sumWNew).toFixed(8);\n const k3 = (weights[i]! / sumWNew).toFixed(8);\n xml += ` <feComposite in=\"${prefix}avg${i - 1}\" in2=\"${prefix}s${i}\" operator=\"arithmetic\"\n k1=\"0\" k2=\"${k2}\" k3=\"${k3}\" k4=\"0\" result=\"${prefix}avg${i}\"/>\\n`;\n sumW = sumWNew;\n }\n\n xml += ` <feGaussianBlur in=\"${prefix}avg${steps - 1}\"\n stdDeviation=\"${outBlur.toFixed(3)}\" result=\"${outResult}\"/>\\n`;\n return xml;\n}\n\n/**\n * Step-count limits for each render quality tier.\n *\n * \"preview\" — real-time playback in the workbench. Caps steps aggressively\n * to keep the SVG filter primitive count under ~30 per element so\n * imgLoad stays under 2ms for combined blur.\n *\n * \"render\" — final MP4 export. Uses the full step budget for smooth,\n * artifact-free motion blur at the cost of heavier SVG rendering.\n */\nconst STEP_LIMITS = {\n preview: { trans: 16, spiral: 16 },\n render: { trans: 31, spiral: 50 },\n} as const;\n\nfunction transSteps(sweepPx: number): number {\n if (sweepPx <= 0) return 2;\n const cap = STEP_LIMITS[EFMotionBlur.renderQuality].trans;\n return Math.max(2, Math.min(cap, Math.ceil(sweepPx) + 1));\n}\n\nfunction spiralSteps(sweepPx: number, rotDeg: number): number {\n const cap = STEP_LIMITS[EFMotionBlur.renderQuality].spiral;\n const fromPx = sweepPx > 0 ? Math.ceil(sweepPx / 1) + 1 : 2;\n const fromAngle = rotDeg > 0 ? Math.ceil(rotDeg / 0.65) + 1 : 2;\n return Math.max(2, Math.min(cap, Math.max(fromPx, fromAngle)));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `<ef-motionblur>` applies motion blur to its child for two types of motion:\n *\n * **Translation** — N `feOffset` copies along the velocity vector, Gaussian-blended.\n *\n * **Spiral** (rotation + zoom combined) — N displaced copies using a single\n * spiral vector map that encodes the weighted sum of tangential (rotation) and\n * radial (zoom) components. Because tangent ⊥ radial with equal magnitude,\n * the combined map always has magnitude r/maxR regardless of the rot/zoom ratio.\n * One pass correctly captures simultaneous rotation and scaling as a spiral\n * smear rather than the generic blur that sequential stages produce.\n */\nconst _EFMotionBlurBase =\n typeof HTMLElement !== \"undefined\"\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\nexport class EFMotionBlur extends _EFMotionBlurBase {\n static observedAttributes = [\n \"angle\",\n \"amount\",\n \"shutter-angle\",\n \"fps\",\n \"sensitivity\",\n \"threshold\",\n ];\n\n /** Controls filter primitive density. \"preview\" for real-time playback, \"render\" for export. */\n static renderQuality: \"preview\" | \"render\" = \"preview\";\n\n private _outer: HTMLDivElement | null = null;\n private _filterEl: HTMLDivElement | null = null;\n private _svg: SVGSVGElement | null = null;\n private _filterId: string | null = null;\n\n // Cached spiral map data URI — rebuilt when element size or spiral angle changes\n private _spiralMapURI: string | null = null;\n private _lastMapW = 0;\n private _lastMapH = 0;\n private _lastSpiralAngle = NaN;\n\n // Cached SVG filter DOM nodes for attribute-only updates (avoids innerHTML rebuild)\n private _filterNode: SVGFilterElement | null = null;\n private _transOffsets: SVGFEOffsetElement[] = [];\n private _spiralDisplacements: SVGFEDisplacementMapElement[] = [];\n private _spiralFeImage: SVGFEImageElement | null = null;\n private _spiralBlur: SVGFEGaussianBlurElement | null = null;\n private _lastStepConfig = \"\"; // fingerprint of step counts for structural rebuild\n\n /** Translation blur: pixel displacement in X (screen space). */\n _computedDx: number = 0;\n /** Translation blur: pixel displacement in Y (screen space). */\n _computedDy: number = 0;\n _computedAngle: number = 0;\n _computedDirectionalAmount: number = 0;\n /** Signed rotation arc displacement (px at half-diagonal). Negative = CW, positive = CCW. */\n _computedRotAmount: number = 0;\n /** Absolute rotation delta in degrees (for step count calculation). */\n _computedRotDeg: number = 0;\n _computedIsotropicAmount: number = 0;\n\n _hasExplicitAngle: boolean = false;\n _hasExplicitAmount: boolean = false;\n\n get _computedAmount(): number {\n return this._computedDirectionalAmount;\n }\n set _computedAmount(v: number) {\n this._computedDirectionalAmount = v;\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n this._filterId = \"ef-mb-\" + Math.random().toString(36).slice(2);\n this._inject();\n this._update();\n }\n\n disconnectedCallback(): void {\n // Don't remove the SVG — it lives inside _outer which stays in our DOM tree.\n // If the element reconnects (e.g. after workbench auto-wrapping), _inject()\n // will re-acquire the reference. Removing it here caused filter:none because\n // _inject's querySelector(\"svg\") would find nothing on reconnect.\n }\n\n attributeChangedCallback(name: string, _old: string | null, newValue: string | null): void {\n if (name === \"angle\") this._hasExplicitAngle = newValue !== null;\n if (name === \"amount\") this._hasExplicitAmount = newValue !== null;\n if (this._outer) this._update();\n }\n\n // ── Public property API ─────────────────────────────────────────────────────\n\n get angle(): number {\n return parseFloat(this.getAttribute(\"angle\") ?? \"0\");\n }\n set angle(v: number) {\n this.setAttribute(\"angle\", String(v));\n }\n get amount(): number {\n return parseFloat(this.getAttribute(\"amount\") ?? \"0\");\n }\n set amount(v: number) {\n this.setAttribute(\"amount\", String(v));\n }\n get shutterAngle(): number {\n return parseFloat(this.getAttribute(\"shutter-angle\") ?? \"180\");\n }\n set shutterAngle(v: number) {\n this.setAttribute(\"shutter-angle\", String(v));\n }\n get fps(): number {\n return parseFloat(this.getAttribute(\"fps\") ?? \"30\");\n }\n set fps(v: number) {\n this.setAttribute(\"fps\", String(v));\n }\n get sensitivity(): number {\n return parseFloat(this.getAttribute(\"sensitivity\") ?? \"1\");\n }\n set sensitivity(v: number) {\n this.setAttribute(\"sensitivity\", String(v));\n }\n get threshold(): number {\n return parseFloat(this.getAttribute(\"threshold\") ?? \"0.5\");\n }\n set threshold(v: number) {\n this.setAttribute(\"threshold\", String(v));\n }\n\n get shutterMs(): number {\n return (this.shutterAngle / 360) * (1000 / this.fps);\n }\n\n // ── Private methods ─────────────────────────────────────────────────────────\n\n /**\n * One-time DOM setup. Creates the SVG filter host and two wrapper divs\n * (_outer for the SVG definition, _filterEl for the filtered content).\n * The SVG filter is rebuilt dynamically in _update().\n */\n private _inject(): void {\n const existingOuter = this.querySelector(\"[data-blur-outer]\") as HTMLDivElement | null;\n if (existingOuter) {\n this._outer = existingOuter;\n this._filterEl = existingOuter.querySelector(\"[data-blur-filter]\") as HTMLDivElement;\n this._svg = existingOuter.querySelector(\"svg\") as SVGSVGElement;\n if (!this._svg) {\n this._svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\") as SVGSVGElement;\n this._svg.style.cssText =\n \"position:absolute;width:0;height:0;overflow:hidden;pointer-events:none\";\n existingOuter.insertBefore(this._svg, this._filterEl);\n }\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n this.style.display = \"contents\";\n return;\n }\n\n this._svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\") as SVGSVGElement;\n this._svg.style.cssText =\n \"position:absolute;width:0;height:0;overflow:hidden;pointer-events:none\";\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n\n this._outer = document.createElement(\"div\");\n this._outer.dataset.blurOuter = \"\";\n this._outer.dataset.efStaticStyle =\n \"display:block;transform:none;transform-origin:center center\";\n this._outer.style.transformOrigin = \"center center\";\n\n this._filterEl = document.createElement(\"div\");\n this._filterEl.dataset.blurFilter = \"\";\n this._filterEl.style.transformOrigin = \"center center\";\n\n while (this.firstChild) this._filterEl.appendChild(this.firstChild);\n\n this._outer.appendChild(this._svg);\n this._outer.appendChild(this._filterEl);\n this.appendChild(this._outer);\n this.style.display = \"contents\";\n }\n\n /**\n * Rebuilds wrapper transforms and the SVG filter. Called on attribute changes\n * and after each sample().\n *\n * The SVG contains up to two filter stages:\n * 1. feOffset×N — directional (translation) blur\n * 2. Spiral (feImage + feDisplacementMap×N + feComposite blend + feGaussianBlur)\n * — combined rotation + zoom in one pass via a spiral vector map.\n *\n * On first call (or when step counts change), builds the full SVG filter DOM.\n * On subsequent calls, updates only the attributes that change per frame.\n */\n private _update(): void {\n if (!this._outer || !this._filterEl || !this._svg) return;\n\n this._outer.style.transform = \"none\";\n this._outer.style.display = \"block\";\n this._filterEl.style.transform = \"none\";\n this._filterEl.style.display = \"block\";\n\n const dx = this._hasExplicitAmount\n ? this.amount * Math.cos((this.angle * Math.PI) / 180)\n : this._computedDx;\n const dy = this._hasExplicitAmount\n ? this.amount * Math.sin((this.angle * Math.PI) / 180)\n : this._computedDy;\n const directionalMag = Math.sqrt(dx * dx + dy * dy);\n const rotAmount = this._hasExplicitAmount ? 0 : this._computedRotAmount;\n const isotropicAmount = this._hasExplicitAmount ? 0 : this._computedIsotropicAmount;\n\n const anyBlur =\n directionalMag > this.threshold ||\n Math.abs(rotAmount) > this.threshold ||\n isotropicAmount > this.threshold;\n this._filterEl.style.display = \"block\";\n\n if (!anyBlur) {\n this._filterEl.style.filter = \"none\";\n this._filterNode = null;\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n if (this._outer) (this._outer as any)._cachedFilterSVG = undefined;\n return;\n }\n\n // Combined spiral: rotation (tangential) and zoom (radial) in one pass.\n // rotScale: physical rotation displacement (signed, ×2 for centered range)\n // zoomScale: mildly amplified zoom (×3 vs physical ×2). Stronger amplification\n // distorts the spiral angle for rotation-dominant animations, making arc\n // smears look radial. ×3 keeps the spiral close to physical while giving\n // zoom-only animations 50% more visible displacement.\n const rotScale = rotAmount * ROT_SCALE_FACTOR;\n const zoomScale = isotropicAmount * ZOOM_SCALE_FACTOR;\n const combinedScale = Math.sqrt(rotScale ** 2 + zoomScale ** 2);\n const spiralAngle = Math.atan2(rotScale, zoomScale);\n const anySpiralBlur = combinedScale > this.threshold;\n\n const child = (this._filterEl.firstElementChild as HTMLElement | null) ?? this._filterEl;\n const childRect = child.getBoundingClientRect();\n const filterElRect = this._filterEl.getBoundingClientRect();\n\n const filterElLogicalW = this._filterEl.offsetWidth || 1;\n const ancestorScale = filterElRect.width / filterElLogicalW || 1;\n\n const iw = (child as HTMLElement).offsetWidth || childRect.width || 100;\n const ih = (child as HTMLElement).offsetHeight || childRect.height || 100;\n\n const bw = childRect.width / ancestorScale;\n const bh = childRect.height / ancestorScale;\n const cx = ((childRect.left + childRect.right) / 2 - filterElRect.left) / ancestorScale;\n const cy = ((childRect.top + childRect.bottom) / 2 - filterElRect.top) / ancestorScale;\n\n const maxBBox = Math.ceil(Math.hypot(iw, ih));\n const mapPad = 64;\n const mapSide = maxBBox + 2 * mapPad;\n const mapContentOx = mapPad + Math.round((maxBBox - iw) / 2);\n const mapContentOy = mapPad + Math.round((maxBBox - ih) / 2);\n this._ensureMaps(mapSide, mapSide, mapContentOx, mapContentOy, iw, ih, spiralAngle);\n\n const halfDiag = Math.ceil(Math.hypot(bw, bh) / 2);\n const margin =\n halfDiag +\n Math.ceil(combinedScale / 2) +\n Math.ceil(Math.abs(dx)) +\n Math.ceil(Math.abs(dy)) +\n 64;\n const filterX = Math.floor(cx - margin);\n const filterY = Math.floor(cy - margin);\n const filterW = Math.ceil(margin * 2);\n const filterH = Math.ceil(margin * 2);\n const mapImgX = Math.round(cx - mapSide / 2);\n const mapImgY = Math.round(cy - mapSide / 2);\n\n const transStepCount = directionalMag > this.threshold ? transSteps(directionalMag) : 0;\n const spiralStepCount = anySpiralBlur ? spiralSteps(combinedScale, this._computedRotDeg) : 0;\n const stepConfig = `${transStepCount},${spiralStepCount}`;\n\n if (stepConfig !== this._lastStepConfig || !this._filterNode) {\n this._lastStepConfig = stepConfig;\n this._buildFilterDOM(transStepCount, spiralStepCount, mapSide);\n }\n\n const f = this._filterNode!;\n f.setAttribute(\"x\", String(filterX));\n f.setAttribute(\"y\", String(filterY));\n f.setAttribute(\"width\", String(filterW));\n f.setAttribute(\"height\", String(filterH));\n\n if (transStepCount > 0) {\n for (let i = 0; i < this._transOffsets.length; i++) {\n const t = this._transOffsets.length > 1 ? i / (this._transOffsets.length - 1) : 0;\n this._transOffsets[i]!.setAttribute(\"dx\", String(t * dx));\n this._transOffsets[i]!.setAttribute(\"dy\", String(t * dy));\n }\n }\n\n if (spiralStepCount > 0 && this._spiralFeImage) {\n this._spiralFeImage.setAttribute(\"x\", String(mapImgX));\n this._spiralFeImage.setAttribute(\"y\", String(mapImgY));\n if (this._spiralMapURI) {\n this._spiralFeImage.setAttribute(\"href\", this._spiralMapURI);\n }\n for (let i = 0; i < this._spiralDisplacements.length; i++) {\n const t =\n this._spiralDisplacements.length > 1 ? i / (this._spiralDisplacements.length - 1) : 0;\n this._spiralDisplacements[i]!.setAttribute(\"scale\", String((t - 0.5) * combinedScale));\n }\n const nSpiral = this._spiralDisplacements.length;\n const spiralSpacing = nSpiral > 1 ? combinedScale / (nSpiral - 1) / 2 : 0;\n this._spiralBlur?.setAttribute(\n \"stdDeviation\",\n String(\n Math.min(SPIRAL_BLUR_CAP, Math.max(SPIRAL_BLUR_FLOOR, spiralSpacing * SPIRAL_BLUR_COEFF)),\n ),\n );\n }\n\n this._filterEl.style.filter = `url(#${this._filterId})`;\n this._refreshSerializationCache();\n }\n\n /**\n * Refreshes the serialization cache on _outer. The serializer reads this\n * instead of calling XMLSerializer on the SVG defs, which is important\n * when rotation/zoom displacement maps embed large base64 data URIs.\n */\n private _refreshSerializationCache(): void {\n if (!this._filterNode || !this._outer) return;\n (this._outer as any)._cachedFilterSVG = new XMLSerializer().serializeToString(this._filterNode);\n }\n\n /**\n * Builds the full SVG filter DOM tree. Called once when step configuration changes.\n * Stores references to mutable elements for fast per-frame attribute updates.\n */\n private _buildFilterDOM(transStepCount: number, spiralStepCount: number, mapSide: number): void {\n const filterId = this._filterId!;\n\n let xml = `<filter id=\"${filterId}\" filterUnits=\"userSpaceOnUse\" primitiveUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\\n`;\n let lastResult = \"SourceGraphic\";\n\n // ① Translation\n if (transStepCount > 0) {\n xml = appendTranslationBlurStage(xml, \"t\", lastResult, \"transblur\", transStepCount, 0, 0);\n lastResult = \"transblur\";\n }\n\n // ② Spiral (combined rotation + zoom)\n if (spiralStepCount > 0 && this._spiralMapURI) {\n xml += ` <feImage href=\"${this._spiralMapURI}\" preserveAspectRatio=\"none\" width=\"${mapSide}\" height=\"${mapSide}\" result=\"spiralmap\"/>\\n`;\n xml = appendTemporalBlurStage(\n xml,\n \"sp\",\n lastResult,\n \"spiralblur\",\n spiralStepCount,\n 0,\n \"spiralmap\",\n true,\n );\n lastResult = \"spiralblur\";\n }\n\n if (lastResult === \"SourceGraphic\") {\n xml += ` <feComposite in=\"SourceGraphic\" in2=\"SourceGraphic\" operator=\"arithmetic\" k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\"/>\\n`;\n }\n xml += `</filter>`;\n this._svg!.innerHTML = `<defs>${xml}</defs>`;\n\n this._filterNode = this._svg!.querySelector(\"filter\") as SVGFilterElement;\n this._transOffsets = Array.from(\n this._svg!.querySelectorAll(\"feOffset\"),\n ) as SVGFEOffsetElement[];\n this._spiralFeImage = this._svg!.querySelector(\n 'feImage[result=\"spiralmap\"]',\n ) as SVGFEImageElement | null;\n this._spiralDisplacements = Array.from(\n this._svg!.querySelectorAll('feDisplacementMap[result^=\"sps\"]'),\n ) as SVGFEDisplacementMapElement[];\n this._spiralBlur = this._svg!.querySelector(\n 'feGaussianBlur[result=\"spiralblur\"]',\n ) as SVGFEGaussianBlurElement | null;\n }\n\n /**\n * Generates or refreshes the spiral displacement map.\n * Cached by element size and spiral angle (quantized to ~5°).\n * The feImage href is updated in the fast path when the map URI changes.\n */\n private _ensureMaps(\n pw: number,\n ph: number,\n ox: number,\n oy: number,\n iw: number,\n ih: number,\n spiralAngle: number,\n ): void {\n const riw = Math.round(iw) || 100;\n const rih = Math.round(ih) || 100;\n const quantizedAngle = Math.round(spiralAngle * SPIRAL_ANGLE_QUANT) / SPIRAL_ANGLE_QUANT;\n\n const sizeChanged = Math.abs(riw - this._lastMapW) > 4 || Math.abs(rih - this._lastMapH) > 4;\n const angleChanged =\n Math.abs(quantizedAngle - this._lastSpiralAngle) > 0.001 || isNaN(this._lastSpiralAngle);\n\n if (!sizeChanged && !angleChanged) return;\n\n this._lastMapW = riw;\n this._lastMapH = rih;\n this._lastSpiralAngle = quantizedAngle;\n this._spiralMapURI = buildSpiralMap(pw, ph, ox, oy, riw, rih, riw / 2, rih / 2, quantizedAngle);\n }\n\n // ── Public API ──────────────────────────────────────────────────────────────\n\n /**\n * Samples the child's current position and transform, computing blur amounts.\n * Called by `updateAnimations` after all animation times are settled.\n */\n sample(_timestamp: number = performance.now()): void {\n if (this._hasExplicitAngle && this._hasExplicitAmount) return;\n if (!this._filterEl) return;\n\n const child = (this._filterEl.firstElementChild as HTMLElement | null) ?? this._filterEl;\n const det = this._computeDeterministicTransform(child);\n if (det !== null) {\n this._applyTransform(det.dx, det.dy, det.dAngle, det.dScale, det.halfDiag);\n }\n }\n\n /**\n * Deterministic two-seek measurement. Seeks all animations back by shutterMs,\n * reads positions and transforms, restores. Returns null when no seekable\n * animations exist or currentTime < shutterMs.\n */\n private _computeDeterministicTransform(child: HTMLElement): {\n dx: number;\n dy: number;\n dAngle: number;\n dScale: number;\n halfDiag: number;\n } | null {\n const tracked = getTrackedAnimationsForSubtree(child);\n const animations = (\n tracked.length > 0 ? tracked : child.getAnimations({ subtree: true })\n ) as Animation[];\n if (animations.length === 0) return null;\n\n const seekable: Animation[] = [];\n const currentTimes: number[] = [];\n for (const anim of animations) {\n const t = anim.currentTime;\n if (t !== null && typeof t === \"number\") {\n seekable.push(anim);\n currentTimes.push(t);\n }\n }\n if (seekable.length === 0) return null;\n\n const shutterMs = this.shutterMs;\n if (currentTimes.every((t) => t < shutterMs)) return null;\n\n // If all animations are in their fill phase (progress === null, activeDuration finite),\n // the element is stationary — no blur.\n const allInFill = seekable.every((anim) => {\n const timing = (anim.effect as KeyframeEffect)?.getComputedTiming();\n if (!timing) return false;\n const { activeDuration, progress } = timing;\n return progress === null && typeof activeDuration === \"number\" && isFinite(activeDuration);\n });\n if (allInFill) return null;\n\n const outerWas = this._outer!.style.transform;\n const filterWas = this._filterEl!.style.transform;\n this._outer!.style.transform = \"none\";\n this._filterEl!.style.transform = \"none\";\n\n // Ancestor-only scale from _filterEl (transform:none), not from the child\n // whose CSS animation rotation inflates its bbox.\n const filterElLogicalW = this._filterEl!.offsetWidth || 1;\n const filterElRect = this._filterEl!.getBoundingClientRect();\n const ancestorScale = filterElRect.width / filterElLogicalW || 1;\n\n // Read position and transform at t_now.\n const r1 = child.getBoundingClientRect();\n const cx1 = r1.left + r1.width / 2;\n const cy1 = r1.top + r1.height / 2;\n const halfDiag = Math.sqrt((child.offsetWidth || 1) ** 2 + (child.offsetHeight || 1) ** 2) / 2;\n const { angle: angle1, scale: scale1 } = readTransformMatrix(child, seekable);\n\n // Seek back to t_now - shutterMs, but never past the animation's active range.\n for (let i = 0; i < seekable.length; i++) {\n const timing = (seekable[i]!.effect as KeyframeEffect)?.getComputedTiming();\n const activeDuration = timing?.activeDuration;\n const endTime =\n typeof activeDuration === \"number\" && isFinite(activeDuration) ? activeDuration : Infinity;\n seekable[i]!.currentTime = Math.min(Math.max(0, currentTimes[i]! - shutterMs), endTime);\n }\n\n // Read position and transform at t_past.\n const r0 = child.getBoundingClientRect();\n const cx0 = r0.left + r0.width / 2;\n const cy0 = r0.top + r0.height / 2;\n const { angle: angle0, scale: scale0 } = readTransformMatrix(child, seekable);\n\n // Restore animation times and wrapper transforms.\n for (let i = 0; i < seekable.length; i++) {\n seekable[i]!.currentTime = currentTimes[i]!;\n }\n this._outer!.style.transform = outerWas;\n this._filterEl!.style.transform = filterWas;\n\n return {\n dx: (cx1 - cx0) / ancestorScale,\n dy: (cy1 - cy0) / ancestorScale,\n dAngle: angle1 - angle0,\n dScale: scale1 - scale0,\n halfDiag,\n };\n }\n\n /**\n * Converts measured deltas to blur amounts and calls _update() when changed.\n *\n * Translation → directional box blur amount + angle.\n * Rotation → arc displacement amount (signed: + = CCW, − = CW).\n * Arc length at half-diagonal radius: R × |Δθ_rad|.\n * Stored signed so the displacement map is applied in the\n * correct tangential direction.\n * Scale → radial displacement amount: |Δscale| × half-diagonal.\n */\n private _applyTransform(\n dx: number,\n dy: number,\n dAngle: number,\n dScale: number,\n halfDiag: number,\n ): void {\n const s = this.sensitivity;\n\n // Translation: store raw dx/dy scaled by sensitivity (capped at 100px)\n const mag = Math.sqrt(dx ** 2 + dy ** 2);\n const scaledMag = Math.min(mag * s, MAX_TRANS_AMOUNT_PX);\n const newDx = mag < this.threshold ? 0 : (dx / mag) * scaledMag;\n const newDy = mag < this.threshold ? 0 : (dy / mag) * scaledMag;\n const newDir = mag < this.threshold ? 0 : scaledMag;\n const newAngle =\n mag < this.threshold ? this._computedAngle : (Math.atan2(dy, dx) * 180) / Math.PI;\n\n // Rotation arc (signed)\n const dAngleRad = (dAngle * Math.PI) / 180;\n const arcLen = halfDiag * Math.abs(dAngleRad) * s;\n const newRot = Math.min(arcLen, MAX_ROT_AMOUNT_PX) * (dAngle > 0 ? -1 : 1);\n const newRotDeg = Math.abs(dAngle);\n\n // Scale zoom (same threshold as translation)\n const rawZoom = Math.abs(dScale) * halfDiag * s;\n const newZoom = rawZoom < this.threshold ? 0 : Math.min(rawZoom, MAX_ZOOM_AMOUNT_PX);\n\n const changed =\n newDx !== this._computedDx ||\n newDy !== this._computedDy ||\n newDir !== this._computedDirectionalAmount ||\n newAngle !== this._computedAngle ||\n newRot !== this._computedRotAmount ||\n newRotDeg !== this._computedRotDeg ||\n newZoom !== this._computedIsotropicAmount;\n\n if (!changed) return;\n\n this._computedDx = newDx;\n this._computedDy = newDy;\n this._computedDirectionalAmount = newDir;\n this._computedAngle = newAngle;\n this._computedRotAmount = newRot;\n this._computedRotDeg = newRotDeg;\n this._computedIsotropicAmount = newZoom;\n this._update();\n }\n}\n\nif (typeof customElements !== \"undefined\") {\n customElements.define(\"ef-motionblur\", EFMotionBlur);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-motionblur\": EFMotionBlur;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EFMotionBlur.js","names":["result","u2","u3","EASING_KEYWORDS: Record<string, [number, number, number, number]>","seekable: Animation[]","currentTimes: number[]"],"sources":["../../src/elements/EFMotionBlur.ts"],"sourcesContent":["import { getTrackedAnimationsForSubtree } from \"./updateAnimations.js\";\n\n// ── Tuning constants ─────────────────────────────────────────────────────────\n// All magic numbers that control blur quality live here. Changing any value\n// is immediately visible as a single-location diff and can be reviewed\n// against the others.\n\n/** Multiplier on rotAmount → rotScale for centered displacement range. */\nexport const ROT_SCALE_FACTOR = 2;\n/** Multiplier on isotropicAmount → zoomScale. >2 amplifies zoom visibility;\n * higher values tilt the spiral angle toward radial. */\nexport const ZOOM_SCALE_FACTOR = 3;\n/** Maximum arc displacement in pixels (caps rotAmount). */\nconst MAX_ROT_AMOUNT_PX = 50;\n/** Maximum zoom displacement in pixels (caps isotropicAmount). */\nconst MAX_ZOOM_AMOUNT_PX = 50;\n/** Maximum translation displacement in pixels. */\nconst MAX_TRANS_AMOUNT_PX = 100;\n/** Gaussian sigma for temporal weights: sigma = max(n / SIGMA_DIVISOR, 2). */\nconst GAUSSIAN_SIGMA_DIVISOR = 5.5;\n/** Spiral output blur: coefficient on step spacing. Must stay small to\n * preserve zero-displacement center sharpness. */\nexport const SPIRAL_BLUR_COEFF = 0.15;\n/** Spiral output blur: hard cap in pixels. */\nexport const SPIRAL_BLUR_CAP = 0.8;\n/** Spiral output blur: floor in pixels. */\nconst SPIRAL_BLUR_FLOOR = 0.3;\n/** Translation output blur: coefficient on step spacing. Can be larger than\n * spiral because all pixels shift equally (no center-vs-edge gradient). */\nconst TRANS_BLUR_COEFF = 0.35;\n/** Translation output blur: hard cap in pixels. */\nconst TRANS_BLUR_CAP = 1.5;\n/** Translation output blur: floor in pixels. */\nconst TRANS_BLUR_FLOOR = 0.3;\n/** Spiral angle quantization: angles rounded to π/(12*2) ≈ 7.5° steps. */\nconst SPIRAL_ANGLE_QUANT = 12;\n\n/**\n * Extracts the 2D rotation angle (degrees) and uniform scale from a CSS\n * transform string. Handles `matrix(a,b,c,d,tx,ty)`, `rotate(Ndeg)`,\n * `scale(N)`, and `none`.\n */\nfunction parseMatrix(transform: string): { angle: number; scale: number } {\n if (!transform || transform === \"none\") return { angle: 0, scale: 1 };\n\n // matrix(a, b, c, d, tx, ty)\n const mm = transform.match(/^matrix\\(([^)]+)\\)/);\n if (mm) {\n const [a, b] = mm[1]!.split(\",\").map(Number) as [number, number, ...number[]];\n const scale = Math.sqrt(a! * a! + b! * b!);\n const angle = (Math.atan2(b!, a!) * 180) / Math.PI;\n return { angle, scale: scale || 1 };\n }\n\n // rotate(Ndeg) or rotate(Nrad)\n const rm = transform.match(/rotate\\(([^)]+)\\)/);\n if (rm) {\n const v = rm[1]!.trim();\n const angle = v.endsWith(\"rad\") ? (parseFloat(v) * 180) / Math.PI : parseFloat(v); // deg\n return { angle, scale: 1 };\n }\n\n // scale(N) or scale(N, N)\n const sm = transform.match(/^scale\\(([^)]+)\\)/);\n if (sm) {\n const parts = sm[1]!.split(\",\").map(Number);\n return { angle: 0, scale: parts[0] ?? 1 };\n }\n\n return { angle: 0, scale: 1 };\n}\n\n/**\n * Reads the rotation angle and scale from an element's current animation state.\n *\n * Prefers direct keyframe interpolation via `getComputedTiming().progress` +\n * `getKeyframes()` — this is synchronous and not subject to Chromium's deferred\n * WAAPI style application (where `getComputedStyle` may lag behind `currentTime`).\n *\n * Falls back to `commitStyles()` + `getComputedStyle` for animations whose keyframes\n * use complex transform lists that our simple interpolation can't handle.\n */\nfunction readTransformMatrix(\n child: HTMLElement,\n anims: Animation[],\n): { angle: number; scale: number } {\n // Preferred path: analytical interpolation from keyframes.\n // Works reliably for cancelled CSS animations whose getComputedStyle output\n // is deferred by Chromium until the next compositor frame.\n for (const anim of anims) {\n const effect = anim.effect;\n if (!(effect instanceof KeyframeEffect)) continue;\n const timing = effect.getComputedTiming();\n const progress = timing.progress;\n if (progress === null || progress === undefined) continue;\n const keyframes = effect.getKeyframes();\n const result = interpolateTransformKeyframes(keyframes, progress as number);\n if (result !== null) return result;\n }\n\n // Fallback: commitStyles() + getComputedStyle.\n let committed = false;\n for (const anim of anims) {\n try {\n (anim as any).commitStyles?.();\n committed = true;\n } catch (_) {\n /* fill:none or not replaceable */\n }\n }\n void child.getBoundingClientRect();\n const result = parseMatrix(getComputedStyle(child).transform);\n if (committed) child.style.removeProperty(\"transform\");\n return result;\n}\n\n/**\n * Decomposes a CSS transform string into cumulative rotation (degrees) and\n * uniform scale. Handles compound transforms like `scale(0.3) rotate(90deg)`,\n * pure rotations, pure scales, and matrix() values.\n *\n * Multiple rotate() functions are summed (so the orbit pattern\n * `rotate(360deg) translateX(90px) rotate(-360deg)` correctly yields 0°).\n *\n * Returns null only when no rotation or scale function is found and the\n * string is not a matrix.\n */\nfunction decomposeKeyframeTransform(transform: string): { angle: number; scale: number } | null {\n if (!transform || transform === \"none\") return { angle: 0, scale: 1 };\n\n const mm = transform.match(/^matrix\\(([^)]+)\\)/);\n if (mm) {\n const [a, b] = mm[1]!.split(\",\").map(Number) as [number, number, ...number[]];\n const scale = Math.sqrt(a! * a! + b! * b!);\n const angle = (Math.atan2(b!, a!) * 180) / Math.PI;\n return { angle, scale: scale || 1 };\n }\n\n let totalAngle = 0;\n let totalScale = 1;\n let found = false;\n\n for (const m of transform.matchAll(/rotate\\(([^)]+)\\)/g)) {\n const v = m[1]!.trim();\n totalAngle += v.endsWith(\"rad\") ? (parseFloat(v) * 180) / Math.PI : parseFloat(v);\n found = true;\n }\n\n const sm = transform.match(/scale\\(([^),]+)(?:,\\s*([^)]+))?\\)/);\n if (sm) {\n const sx = parseFloat(sm[1]!);\n const sy = sm[2] !== undefined ? parseFloat(sm[2]!) : sx;\n if (Math.abs(sx - sy) < 0.001) {\n totalScale = sx;\n found = true;\n }\n }\n\n return found ? { angle: totalAngle, scale: totalScale } : null;\n}\n\n/**\n * Solves a cubic-bezier curve: given control points (x1,y1,x2,y2) and input\n * parameter `t` (0–1), returns the output value y. Uses Newton-Raphson\n * iteration to invert the x(u) polynomial.\n */\nfunction solveCubicBezier(x1: number, y1: number, x2: number, y2: number, t: number): number {\n if (t <= 0) return 0;\n if (t >= 1) return 1;\n let u = t;\n for (let i = 0; i < 8; i++) {\n const u2 = u * u,\n u3 = u2 * u;\n const xu = 3 * (1 - u) * (1 - u) * u * x1 + 3 * (1 - u) * u2 * x2 + u3;\n const dxu = 3 * (1 - u) * (1 - u) * x1 + 6 * (1 - u) * u * (x2 - x1) + 3 * u2 * (1 - x2);\n if (Math.abs(dxu) < 1e-9) break;\n u -= (xu - t) / dxu;\n u = Math.max(0, Math.min(1, u));\n }\n const u2 = u * u,\n u3 = u2 * u;\n return 3 * (1 - u) * (1 - u) * u * y1 + 3 * (1 - u) * u2 * y2 + u3;\n}\n\nconst EASING_KEYWORDS: Record<string, [number, number, number, number]> = {\n ease: [0.25, 0.1, 0.25, 1.0],\n \"ease-in\": [0.42, 0, 1.0, 1.0],\n \"ease-out\": [0, 0, 0.58, 1.0],\n \"ease-in-out\": [0.42, 0, 0.58, 1.0],\n};\n\n/**\n * Applies a CSS easing function to a linear parameter t (0–1).\n * Handles keyword easings and `cubic-bezier(x1,y1,x2,y2)`.\n */\nfunction applyEasing(easing: string | undefined | null, t: number): number {\n if (!easing || easing === \"linear\") return t;\n const kw = EASING_KEYWORDS[easing];\n if (kw) return solveCubicBezier(kw[0], kw[1], kw[2], kw[3], t);\n const cbm = easing.match(/cubic-bezier\\(\\s*([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/);\n if (cbm)\n return solveCubicBezier(\n parseFloat(cbm[1]!),\n parseFloat(cbm[2]!),\n parseFloat(cbm[3]!),\n parseFloat(cbm[4]!),\n t,\n );\n return t;\n}\n\n/**\n * Interpolates angle (degrees) and scale from a WAAPI keyframe list at the\n * given progress (0–1).\n *\n * Uses the original CSS value strings from the keyframes (not computed matrices)\n * so `rotate(0deg) → rotate(360deg)` interpolates correctly as 0→360°.\n *\n * Handles compound transforms (e.g. `scale(0.3) rotate(90deg)`) by decomposing\n * each keyframe individually, and applies per-keyframe easing so that blur\n * intensity tracks the actual eased velocity rather than the linear one.\n */\nfunction interpolateTransformKeyframes(\n keyframes: ComputedKeyframe[],\n progress: number,\n): { angle: number; scale: number } | null {\n const withTransform = keyframes.filter((k) => k.transform != null);\n if (withTransform.length < 2) return null;\n\n let lo = withTransform[0]!;\n let hi = withTransform[withTransform.length - 1]!;\n for (let i = 0; i < withTransform.length - 1; i++) {\n const a = withTransform[i]!;\n const b = withTransform[i + 1]!;\n const aOff = typeof a.offset === \"number\" ? a.offset : i / (withTransform.length - 1);\n const bOff = typeof b.offset === \"number\" ? b.offset : (i + 1) / (withTransform.length - 1);\n if (progress >= aOff && progress <= bOff) {\n lo = a;\n hi = b;\n break;\n }\n }\n\n const loOff = typeof lo.offset === \"number\" ? lo.offset : 0;\n const hiOff = typeof hi.offset === \"number\" ? hi.offset : 1;\n const linearT = hiOff === loOff ? 0 : (progress - loOff) / (hiOff - loOff);\n const t = applyEasing(lo.easing as string | undefined, linearT);\n\n const loD = decomposeKeyframeTransform(String(lo.transform ?? \"none\"));\n const hiD = decomposeKeyframeTransform(String(hi.transform ?? \"none\"));\n if (!loD || !hiD) return null;\n if (loD.angle === 0 && hiD.angle === 0 && loD.scale === 1 && hiD.scale === 1) return null;\n\n return {\n angle: loD.angle + (hiD.angle - loD.angle) * t,\n scale: loD.scale + (hiD.scale - loD.scale) * t,\n };\n}\n\n/**\n * Gaussian temporal weights for N blur steps.\n * Soft window (vs equal weights) eliminates the \"stack of discrete frames\" look.\n */\nfunction gaussianWeights(n: number): number[] {\n if (n <= 1) return [1];\n const mid = (n - 1) / 2;\n const sigma = Math.max(n / GAUSSIAN_SIGMA_DIVISOR, 2);\n return Array.from({ length: n }, (_, i) => Math.exp(-0.5 * ((i - mid) / sigma) ** 2));\n}\n\n/**\n * Maximum distance from a point to any corner of a rectangle.\n */\nfunction maxDistToCorner(cx: number, cy: number, w: number, h: number): number {\n let m = 0;\n for (const px of [0, w])\n for (const py of [0, h]) {\n const d = Math.hypot(px - cx, py - cy);\n if (d > m) m = d;\n }\n return m || 1;\n}\n\n/**\n * Generates a canvas displacement map encoding a **spiral** vector field.\n *\n * Each pixel encodes a 2D displacement direction as (R, G) where:\n * R = 128 + vx*127 G = 128 + vy*127\n *\n * The vector at each pixel is:\n * v = sin(spiralAngle) × tangent + cos(spiralAngle) × radial\n *\n * Because tangent ⊥ radial and both have magnitude r/maxR, the combined\n * vector also has magnitude r/maxR regardless of spiralAngle. This means\n * one map + one set of N displaced copies correctly captures simultaneous\n * rotation and zoom as a single spiral smear — no sequential stages needed.\n *\n * spiralAngle = atan2(rotScale, zoomScale):\n * π/2 → pure tangential (rotation only)\n * 0 → pure radial (zoom only)\n * between → spiral\n */\nfunction buildSpiralMap(\n pw: number,\n ph: number,\n ox: number,\n oy: number,\n iw: number,\n ih: number,\n cx: number,\n cy: number,\n spiralAngle: number,\n): string {\n const canvas = new OffscreenCanvas(pw, ph);\n const ctx = canvas.getContext(\"2d\")!;\n const img = ctx.createImageData(pw, ph);\n const maxR = maxDistToCorner(cx, cy, iw, ih);\n const sinA = Math.sin(spiralAngle);\n const cosA = Math.cos(spiralAngle);\n\n for (let py = 0; py < ph; py++) {\n for (let px = 0; px < pw; px++) {\n const ux = px - ox;\n const uy = py - oy;\n const i = (py * pw + px) * 4;\n const dx = ux - cx;\n const dy = uy - cy;\n const vx = sinA * (-dy / maxR) + cosA * (dx / maxR);\n const vy = sinA * (dx / maxR) + cosA * (dy / maxR);\n img.data[i] = Math.round(128 + vx * 127);\n img.data[i + 1] = Math.round(128 + vy * 127);\n img.data[i + 2] = 128;\n img.data[i + 3] = 255;\n }\n }\n ctx.putImageData(img, 0, 0);\n const c = document.createElement(\"canvas\");\n c.width = pw;\n c.height = ph;\n c.getContext(\"2d\")!.putImageData(img, 0, 0);\n return c.toDataURL();\n}\n\n/**\n * Appends the SVG filter primitives for one temporal displacement blur stage\n * into an existing filter string. Takes `inResult` as input and writes\n * the final blended output to `outResult`.\n *\n * When `centered=true`, steps run from -totalScale/2 → +totalScale/2, placing\n * the un-displaced copy at the center of the range. This is used for rotation\n * blur so the current frame sits in the middle of the smear.\n * When `centered=false` (default), steps run from 0 → totalScale.\n *\n * Produces N copies each displaced by the map at increasing scale,\n * blended with Gaussian temporal weights. A small feGaussianBlur softens slice edges.\n */\nfunction appendTemporalBlurStage(\n xml: string,\n prefix: string,\n inResult: string,\n outResult: string,\n steps: number,\n totalScale: number,\n mapResult: string,\n centered = false,\n): string {\n const weights = gaussianWeights(steps);\n // Minimal smoothing to merge adjacent displaced copies at the element edges.\n // Must be conservative because feGaussianBlur is isotropic — it softens the\n // CENTER (where displacement is zero and the image should stay sharp) just as\n // much as the edges. Gaussian temporal weighting already handles most blending.\n const stepSpacing = steps > 1 ? Math.abs(totalScale) / (steps - 1) / 2 : 0;\n const outBlur = Math.min(\n SPIRAL_BLUR_CAP,\n Math.max(SPIRAL_BLUR_FLOOR, stepSpacing * SPIRAL_BLUR_COEFF),\n );\n\n for (let i = 0; i < steps; i++) {\n const t = steps > 1 ? i / (steps - 1) : 0;\n const scale = centered ? ((t - 0.5) * totalScale).toFixed(4) : (t * totalScale).toFixed(4);\n xml += ` <feDisplacementMap in=\"${inResult}\" in2=\"${mapResult}\"\n scale=\"${scale}\" xChannelSelector=\"R\" yChannelSelector=\"G\"\n result=\"${prefix}s${i}\"/>\\n`;\n }\n\n xml += ` <feComposite in=\"${prefix}s0\" in2=\"${prefix}s0\" operator=\"arithmetic\"\n k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\" result=\"${prefix}avg0\"/>\\n`;\n let sumW = weights[0]!;\n for (let i = 1; i < steps; i++) {\n const sumWNew = sumW + weights[i]!;\n const k2 = (sumW / sumWNew).toFixed(8);\n const k3 = (weights[i]! / sumWNew).toFixed(8);\n xml += ` <feComposite in=\"${prefix}avg${i - 1}\" in2=\"${prefix}s${i}\" operator=\"arithmetic\"\n k1=\"0\" k2=\"${k2}\" k3=\"${k3}\" k4=\"0\" result=\"${prefix}avg${i}\"/>\\n`;\n sumW = sumWNew;\n }\n\n xml += ` <feGaussianBlur in=\"${prefix}avg${steps - 1}\"\n stdDeviation=\"${outBlur.toFixed(3)}\" result=\"${outResult}\"/>\\n`;\n return xml;\n}\n\n/**\n * Appends SVG filter primitives for a directional translation blur using\n * feOffset. Produces N copies of `inResult` shifted along (vx, vy) from\n * 0 → (totalDx, totalDy), blended with Gaussian temporal weights.\n * No wrapper rotation needed — direction is encoded directly as dx/dy offsets.\n */\nfunction appendTranslationBlurStage(\n xml: string,\n prefix: string,\n inResult: string,\n outResult: string,\n steps: number,\n totalDx: number,\n totalDy: number,\n): string {\n const weights = gaussianWeights(steps);\n const sweep = Math.sqrt(totalDx ** 2 + totalDy ** 2);\n // Translation: all pixels shift equally, so isotropic blur doesn't selectively\n // soften any region. Can tolerate more smoothing than the spiral stage.\n const stepSpacing = steps > 1 ? sweep / (steps - 1) : 0;\n const outBlur = Math.min(\n TRANS_BLUR_CAP,\n Math.max(TRANS_BLUR_FLOOR, stepSpacing * TRANS_BLUR_COEFF),\n );\n\n for (let i = 0; i < steps; i++) {\n const t = steps > 1 ? i / (steps - 1) : 0;\n const dx = (t * totalDx).toFixed(3);\n const dy = (t * totalDy).toFixed(3);\n xml += ` <feOffset in=\"${inResult}\" dx=\"${dx}\" dy=\"${dy}\" result=\"${prefix}s${i}\"/>\\n`;\n }\n\n xml += ` <feComposite in=\"${prefix}s0\" in2=\"${prefix}s0\" operator=\"arithmetic\"\n k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\" result=\"${prefix}avg0\"/>\\n`;\n let sumW = weights[0]!;\n for (let i = 1; i < steps; i++) {\n const sumWNew = sumW + weights[i]!;\n const k2 = (sumW / sumWNew).toFixed(8);\n const k3 = (weights[i]! / sumWNew).toFixed(8);\n xml += ` <feComposite in=\"${prefix}avg${i - 1}\" in2=\"${prefix}s${i}\" operator=\"arithmetic\"\n k1=\"0\" k2=\"${k2}\" k3=\"${k3}\" k4=\"0\" result=\"${prefix}avg${i}\"/>\\n`;\n sumW = sumWNew;\n }\n\n xml += ` <feGaussianBlur in=\"${prefix}avg${steps - 1}\"\n stdDeviation=\"${outBlur.toFixed(3)}\" result=\"${outResult}\"/>\\n`;\n return xml;\n}\n\n/**\n * Step-count limits for each render quality tier.\n *\n * \"preview\" — real-time playback in the workbench. Caps steps aggressively\n * to keep the SVG filter primitive count under ~30 per element so\n * imgLoad stays under 2ms for combined blur.\n *\n * \"render\" — final MP4 export. Uses the full step budget for smooth,\n * artifact-free motion blur at the cost of heavier SVG rendering.\n */\nconst STEP_LIMITS = {\n preview: { trans: 16, spiral: 16 },\n render: { trans: 31, spiral: 50 },\n} as const;\n\nfunction transSteps(sweepPx: number): number {\n if (sweepPx <= 0) return 2;\n const cap = STEP_LIMITS[EFMotionBlur.renderQuality].trans;\n return Math.max(2, Math.min(cap, Math.ceil(sweepPx) + 1));\n}\n\nfunction spiralSteps(sweepPx: number, rotDeg: number): number {\n const cap = STEP_LIMITS[EFMotionBlur.renderQuality].spiral;\n const fromPx = sweepPx > 0 ? Math.ceil(sweepPx / 1) + 1 : 2;\n const fromAngle = rotDeg > 0 ? Math.ceil(rotDeg / 0.65) + 1 : 2;\n return Math.max(2, Math.min(cap, Math.max(fromPx, fromAngle)));\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * `<ef-motionblur>` applies motion blur to its child for two types of motion:\n *\n * **Translation** — N `feOffset` copies along the velocity vector, Gaussian-blended.\n *\n * **Spiral** (rotation + zoom combined) — N displaced copies using a single\n * spiral vector map that encodes the weighted sum of tangential (rotation) and\n * radial (zoom) components. Because tangent ⊥ radial with equal magnitude,\n * the combined map always has magnitude r/maxR regardless of the rot/zoom ratio.\n * One pass correctly captures simultaneous rotation and scaling as a spiral\n * smear rather than the generic blur that sequential stages produce.\n */\nconst _EFMotionBlurBase =\n typeof HTMLElement !== \"undefined\" ? HTMLElement : (class {} as unknown as typeof HTMLElement);\n\nexport class EFMotionBlur extends _EFMotionBlurBase {\n static observedAttributes = [\n \"angle\",\n \"amount\",\n \"shutter-angle\",\n \"fps\",\n \"sensitivity\",\n \"threshold\",\n ];\n\n /** Controls filter primitive density. \"preview\" for real-time playback, \"render\" for export. */\n static renderQuality: \"preview\" | \"render\" = \"preview\";\n\n private _outer: HTMLDivElement | null = null;\n private _filterEl: HTMLDivElement | null = null;\n private _svg: SVGSVGElement | null = null;\n private _filterId: string | null = null;\n\n // Cached spiral map data URI — rebuilt when element size or spiral angle changes\n private _spiralMapURI: string | null = null;\n private _lastMapW = 0;\n private _lastMapH = 0;\n private _lastSpiralAngle = NaN;\n\n // Cached SVG filter DOM nodes for attribute-only updates (avoids innerHTML rebuild)\n private _filterNode: SVGFilterElement | null = null;\n private _transOffsets: SVGFEOffsetElement[] = [];\n private _spiralDisplacements: SVGFEDisplacementMapElement[] = [];\n private _spiralFeImage: SVGFEImageElement | null = null;\n private _spiralBlur: SVGFEGaussianBlurElement | null = null;\n private _lastStepConfig = \"\"; // fingerprint of step counts for structural rebuild\n\n /** Translation blur: pixel displacement in X (screen space). */\n _computedDx: number = 0;\n /** Translation blur: pixel displacement in Y (screen space). */\n _computedDy: number = 0;\n _computedAngle: number = 0;\n _computedDirectionalAmount: number = 0;\n /** Signed rotation arc displacement (px at half-diagonal). Negative = CW, positive = CCW. */\n _computedRotAmount: number = 0;\n /** Absolute rotation delta in degrees (for step count calculation). */\n _computedRotDeg: number = 0;\n _computedIsotropicAmount: number = 0;\n\n _hasExplicitAngle: boolean = false;\n _hasExplicitAmount: boolean = false;\n\n get _computedAmount(): number {\n return this._computedDirectionalAmount;\n }\n set _computedAmount(v: number) {\n this._computedDirectionalAmount = v;\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────────────────\n\n connectedCallback(): void {\n this._filterId = \"ef-mb-\" + Math.random().toString(36).slice(2);\n this._inject();\n this._update();\n }\n\n disconnectedCallback(): void {\n // Don't remove the SVG — it lives inside _outer which stays in our DOM tree.\n // If the element reconnects (e.g. after workbench auto-wrapping), _inject()\n // will re-acquire the reference. Removing it here caused filter:none because\n // _inject's querySelector(\"svg\") would find nothing on reconnect.\n }\n\n attributeChangedCallback(name: string, _old: string | null, newValue: string | null): void {\n if (name === \"angle\") this._hasExplicitAngle = newValue !== null;\n if (name === \"amount\") this._hasExplicitAmount = newValue !== null;\n if (this._outer) this._update();\n }\n\n // ── Public property API ─────────────────────────────────────────────────────\n\n get angle(): number {\n return parseFloat(this.getAttribute(\"angle\") ?? \"0\");\n }\n set angle(v: number) {\n this.setAttribute(\"angle\", String(v));\n }\n get amount(): number {\n return parseFloat(this.getAttribute(\"amount\") ?? \"0\");\n }\n set amount(v: number) {\n this.setAttribute(\"amount\", String(v));\n }\n get shutterAngle(): number {\n return parseFloat(this.getAttribute(\"shutter-angle\") ?? \"180\");\n }\n set shutterAngle(v: number) {\n this.setAttribute(\"shutter-angle\", String(v));\n }\n get fps(): number {\n return parseFloat(this.getAttribute(\"fps\") ?? \"30\");\n }\n set fps(v: number) {\n this.setAttribute(\"fps\", String(v));\n }\n get sensitivity(): number {\n return parseFloat(this.getAttribute(\"sensitivity\") ?? \"1\");\n }\n set sensitivity(v: number) {\n this.setAttribute(\"sensitivity\", String(v));\n }\n get threshold(): number {\n return parseFloat(this.getAttribute(\"threshold\") ?? \"0.5\");\n }\n set threshold(v: number) {\n this.setAttribute(\"threshold\", String(v));\n }\n\n get shutterMs(): number {\n return (this.shutterAngle / 360) * (1000 / this.fps);\n }\n\n // ── Private methods ─────────────────────────────────────────────────────────\n\n /**\n * One-time DOM setup. Creates the SVG filter host and two wrapper divs\n * (_outer for the SVG definition, _filterEl for the filtered content).\n * The SVG filter is rebuilt dynamically in _update().\n */\n private _inject(): void {\n const existingOuter = this.querySelector(\"[data-blur-outer]\") as HTMLDivElement | null;\n if (existingOuter) {\n this._outer = existingOuter;\n this._filterEl = existingOuter.querySelector(\"[data-blur-filter]\") as HTMLDivElement;\n this._svg = existingOuter.querySelector(\"svg\") as SVGSVGElement;\n if (!this._svg) {\n this._svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\") as SVGSVGElement;\n this._svg.style.cssText =\n \"position:absolute;width:0;height:0;overflow:hidden;pointer-events:none\";\n existingOuter.insertBefore(this._svg, this._filterEl);\n }\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n this.style.display = \"contents\";\n return;\n }\n\n this._svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\") as SVGSVGElement;\n this._svg.style.cssText =\n \"position:absolute;width:0;height:0;overflow:hidden;pointer-events:none\";\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n\n this._outer = document.createElement(\"div\");\n this._outer.dataset.blurOuter = \"\";\n this._outer.dataset.efStaticStyle =\n \"display:block;transform:none;transform-origin:center center\";\n this._outer.style.transformOrigin = \"center center\";\n\n this._filterEl = document.createElement(\"div\");\n this._filterEl.dataset.blurFilter = \"\";\n this._filterEl.style.transformOrigin = \"center center\";\n\n while (this.firstChild) this._filterEl.appendChild(this.firstChild);\n\n this._outer.appendChild(this._svg);\n this._outer.appendChild(this._filterEl);\n this.appendChild(this._outer);\n this.style.display = \"contents\";\n }\n\n /**\n * Rebuilds wrapper transforms and the SVG filter. Called on attribute changes\n * and after each sample().\n *\n * The SVG contains up to two filter stages:\n * 1. feOffset×N — directional (translation) blur\n * 2. Spiral (feImage + feDisplacementMap×N + feComposite blend + feGaussianBlur)\n * — combined rotation + zoom in one pass via a spiral vector map.\n *\n * On first call (or when step counts change), builds the full SVG filter DOM.\n * On subsequent calls, updates only the attributes that change per frame.\n */\n private _update(): void {\n if (!this._outer || !this._filterEl || !this._svg) return;\n\n this._outer.style.transform = \"none\";\n this._outer.style.display = \"block\";\n this._filterEl.style.transform = \"none\";\n this._filterEl.style.display = \"block\";\n\n const dx = this._hasExplicitAmount\n ? this.amount * Math.cos((this.angle * Math.PI) / 180)\n : this._computedDx;\n const dy = this._hasExplicitAmount\n ? this.amount * Math.sin((this.angle * Math.PI) / 180)\n : this._computedDy;\n const directionalMag = Math.sqrt(dx * dx + dy * dy);\n const rotAmount = this._hasExplicitAmount ? 0 : this._computedRotAmount;\n const isotropicAmount = this._hasExplicitAmount ? 0 : this._computedIsotropicAmount;\n\n const anyBlur =\n directionalMag > this.threshold ||\n Math.abs(rotAmount) > this.threshold ||\n isotropicAmount > this.threshold;\n this._filterEl.style.display = \"block\";\n\n if (!anyBlur) {\n this._filterEl.style.filter = \"none\";\n this._filterNode = null;\n this._svg.innerHTML = `<defs><filter id=\"${this._filterId}\"></filter></defs>`;\n if (this._outer) (this._outer as any)._cachedFilterSVG = undefined;\n return;\n }\n\n // Combined spiral: rotation (tangential) and zoom (radial) in one pass.\n // rotScale: physical rotation displacement (signed, ×2 for centered range)\n // zoomScale: mildly amplified zoom (×3 vs physical ×2). Stronger amplification\n // distorts the spiral angle for rotation-dominant animations, making arc\n // smears look radial. ×3 keeps the spiral close to physical while giving\n // zoom-only animations 50% more visible displacement.\n const rotScale = rotAmount * ROT_SCALE_FACTOR;\n const zoomScale = isotropicAmount * ZOOM_SCALE_FACTOR;\n const combinedScale = Math.sqrt(rotScale ** 2 + zoomScale ** 2);\n const spiralAngle = Math.atan2(rotScale, zoomScale);\n const anySpiralBlur = combinedScale > this.threshold;\n\n const child = (this._filterEl.firstElementChild as HTMLElement | null) ?? this._filterEl;\n const childRect = child.getBoundingClientRect();\n const filterElRect = this._filterEl.getBoundingClientRect();\n\n const filterElLogicalW = this._filterEl.offsetWidth || 1;\n const ancestorScale = filterElRect.width / filterElLogicalW || 1;\n\n const iw = (child as HTMLElement).offsetWidth || childRect.width || 100;\n const ih = (child as HTMLElement).offsetHeight || childRect.height || 100;\n\n const bw = childRect.width / ancestorScale;\n const bh = childRect.height / ancestorScale;\n const cx = ((childRect.left + childRect.right) / 2 - filterElRect.left) / ancestorScale;\n const cy = ((childRect.top + childRect.bottom) / 2 - filterElRect.top) / ancestorScale;\n\n const maxBBox = Math.ceil(Math.hypot(iw, ih));\n const mapPad = 64;\n const mapSide = maxBBox + 2 * mapPad;\n const mapContentOx = mapPad + Math.round((maxBBox - iw) / 2);\n const mapContentOy = mapPad + Math.round((maxBBox - ih) / 2);\n this._ensureMaps(mapSide, mapSide, mapContentOx, mapContentOy, iw, ih, spiralAngle);\n\n const halfDiag = Math.ceil(Math.hypot(bw, bh) / 2);\n const margin =\n halfDiag +\n Math.ceil(combinedScale / 2) +\n Math.ceil(Math.abs(dx)) +\n Math.ceil(Math.abs(dy)) +\n 64;\n const filterX = Math.floor(cx - margin);\n const filterY = Math.floor(cy - margin);\n const filterW = Math.ceil(margin * 2);\n const filterH = Math.ceil(margin * 2);\n const mapImgX = Math.round(cx - mapSide / 2);\n const mapImgY = Math.round(cy - mapSide / 2);\n\n const transStepCount = directionalMag > this.threshold ? transSteps(directionalMag) : 0;\n const spiralStepCount = anySpiralBlur ? spiralSteps(combinedScale, this._computedRotDeg) : 0;\n const stepConfig = `${transStepCount},${spiralStepCount}`;\n\n if (stepConfig !== this._lastStepConfig || !this._filterNode) {\n this._lastStepConfig = stepConfig;\n this._buildFilterDOM(transStepCount, spiralStepCount, mapSide);\n }\n\n const f = this._filterNode!;\n f.setAttribute(\"x\", String(filterX));\n f.setAttribute(\"y\", String(filterY));\n f.setAttribute(\"width\", String(filterW));\n f.setAttribute(\"height\", String(filterH));\n\n if (transStepCount > 0) {\n for (let i = 0; i < this._transOffsets.length; i++) {\n const t = this._transOffsets.length > 1 ? i / (this._transOffsets.length - 1) : 0;\n this._transOffsets[i]!.setAttribute(\"dx\", String(t * dx));\n this._transOffsets[i]!.setAttribute(\"dy\", String(t * dy));\n }\n }\n\n if (spiralStepCount > 0 && this._spiralFeImage) {\n this._spiralFeImage.setAttribute(\"x\", String(mapImgX));\n this._spiralFeImage.setAttribute(\"y\", String(mapImgY));\n if (this._spiralMapURI) {\n this._spiralFeImage.setAttribute(\"href\", this._spiralMapURI);\n }\n for (let i = 0; i < this._spiralDisplacements.length; i++) {\n const t =\n this._spiralDisplacements.length > 1 ? i / (this._spiralDisplacements.length - 1) : 0;\n this._spiralDisplacements[i]!.setAttribute(\"scale\", String((t - 0.5) * combinedScale));\n }\n const nSpiral = this._spiralDisplacements.length;\n const spiralSpacing = nSpiral > 1 ? combinedScale / (nSpiral - 1) / 2 : 0;\n this._spiralBlur?.setAttribute(\n \"stdDeviation\",\n String(\n Math.min(SPIRAL_BLUR_CAP, Math.max(SPIRAL_BLUR_FLOOR, spiralSpacing * SPIRAL_BLUR_COEFF)),\n ),\n );\n }\n\n this._filterEl.style.filter = `url(#${this._filterId})`;\n this._refreshSerializationCache();\n }\n\n /**\n * Refreshes the serialization cache on _outer. The serializer reads this\n * instead of calling XMLSerializer on the SVG defs, which is important\n * when rotation/zoom displacement maps embed large base64 data URIs.\n */\n private _refreshSerializationCache(): void {\n if (!this._filterNode || !this._outer) return;\n (this._outer as any)._cachedFilterSVG = new XMLSerializer().serializeToString(this._filterNode);\n }\n\n /**\n * Builds the full SVG filter DOM tree. Called once when step configuration changes.\n * Stores references to mutable elements for fast per-frame attribute updates.\n */\n private _buildFilterDOM(transStepCount: number, spiralStepCount: number, mapSide: number): void {\n const filterId = this._filterId!;\n\n let xml = `<filter id=\"${filterId}\" filterUnits=\"userSpaceOnUse\" primitiveUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\\n`;\n let lastResult = \"SourceGraphic\";\n\n // ① Translation\n if (transStepCount > 0) {\n xml = appendTranslationBlurStage(xml, \"t\", lastResult, \"transblur\", transStepCount, 0, 0);\n lastResult = \"transblur\";\n }\n\n // ② Spiral (combined rotation + zoom)\n if (spiralStepCount > 0 && this._spiralMapURI) {\n xml += ` <feImage href=\"${this._spiralMapURI}\" preserveAspectRatio=\"none\" width=\"${mapSide}\" height=\"${mapSide}\" result=\"spiralmap\"/>\\n`;\n xml = appendTemporalBlurStage(\n xml,\n \"sp\",\n lastResult,\n \"spiralblur\",\n spiralStepCount,\n 0,\n \"spiralmap\",\n true,\n );\n lastResult = \"spiralblur\";\n }\n\n if (lastResult === \"SourceGraphic\") {\n xml += ` <feComposite in=\"SourceGraphic\" in2=\"SourceGraphic\" operator=\"arithmetic\" k1=\"0\" k2=\"1\" k3=\"0\" k4=\"0\"/>\\n`;\n }\n xml += `</filter>`;\n this._svg!.innerHTML = `<defs>${xml}</defs>`;\n\n this._filterNode = this._svg!.querySelector(\"filter\") as SVGFilterElement;\n this._transOffsets = Array.from(\n this._svg!.querySelectorAll(\"feOffset\"),\n ) as SVGFEOffsetElement[];\n this._spiralFeImage = this._svg!.querySelector(\n 'feImage[result=\"spiralmap\"]',\n ) as SVGFEImageElement | null;\n this._spiralDisplacements = Array.from(\n this._svg!.querySelectorAll('feDisplacementMap[result^=\"sps\"]'),\n ) as SVGFEDisplacementMapElement[];\n this._spiralBlur = this._svg!.querySelector(\n 'feGaussianBlur[result=\"spiralblur\"]',\n ) as SVGFEGaussianBlurElement | null;\n }\n\n /**\n * Generates or refreshes the spiral displacement map.\n * Cached by element size and spiral angle (quantized to ~5°).\n * The feImage href is updated in the fast path when the map URI changes.\n */\n private _ensureMaps(\n pw: number,\n ph: number,\n ox: number,\n oy: number,\n iw: number,\n ih: number,\n spiralAngle: number,\n ): void {\n const riw = Math.round(iw) || 100;\n const rih = Math.round(ih) || 100;\n const quantizedAngle = Math.round(spiralAngle * SPIRAL_ANGLE_QUANT) / SPIRAL_ANGLE_QUANT;\n\n const sizeChanged = Math.abs(riw - this._lastMapW) > 4 || Math.abs(rih - this._lastMapH) > 4;\n const angleChanged =\n Math.abs(quantizedAngle - this._lastSpiralAngle) > 0.001 || isNaN(this._lastSpiralAngle);\n\n if (!sizeChanged && !angleChanged) return;\n\n this._lastMapW = riw;\n this._lastMapH = rih;\n this._lastSpiralAngle = quantizedAngle;\n this._spiralMapURI = buildSpiralMap(pw, ph, ox, oy, riw, rih, riw / 2, rih / 2, quantizedAngle);\n }\n\n // ── Public API ──────────────────────────────────────────────────────────────\n\n /**\n * Samples the child's current position and transform, computing blur amounts.\n * Called by `updateAnimations` after all animation times are settled.\n */\n sample(_timestamp: number = performance.now()): void {\n if (this._hasExplicitAngle && this._hasExplicitAmount) return;\n if (!this._filterEl) return;\n\n const child = (this._filterEl.firstElementChild as HTMLElement | null) ?? this._filterEl;\n const det = this._computeDeterministicTransform(child);\n if (det !== null) {\n this._applyTransform(det.dx, det.dy, det.dAngle, det.dScale, det.halfDiag);\n }\n }\n\n /**\n * Deterministic two-seek measurement. Seeks all animations back by shutterMs,\n * reads positions and transforms, restores. Returns null when no seekable\n * animations exist or currentTime < shutterMs.\n */\n private _computeDeterministicTransform(child: HTMLElement): {\n dx: number;\n dy: number;\n dAngle: number;\n dScale: number;\n halfDiag: number;\n } | null {\n const tracked = getTrackedAnimationsForSubtree(child);\n const animations = (\n tracked.length > 0 ? tracked : child.getAnimations({ subtree: true })\n ) as Animation[];\n if (animations.length === 0) return null;\n\n const seekable: Animation[] = [];\n const currentTimes: number[] = [];\n for (const anim of animations) {\n const t = anim.currentTime;\n if (t !== null && typeof t === \"number\") {\n seekable.push(anim);\n currentTimes.push(t);\n }\n }\n if (seekable.length === 0) return null;\n\n const shutterMs = this.shutterMs;\n if (currentTimes.every((t) => t < shutterMs)) return null;\n\n // If all animations are in their fill phase (progress === null, activeDuration finite),\n // the element is stationary — no blur.\n const allInFill = seekable.every((anim) => {\n const timing = (anim.effect as KeyframeEffect)?.getComputedTiming();\n if (!timing) return false;\n const { activeDuration, progress } = timing;\n return progress === null && typeof activeDuration === \"number\" && isFinite(activeDuration);\n });\n if (allInFill) return null;\n\n const outerWas = this._outer!.style.transform;\n const filterWas = this._filterEl!.style.transform;\n this._outer!.style.transform = \"none\";\n this._filterEl!.style.transform = \"none\";\n\n // Ancestor-only scale from _filterEl (transform:none), not from the child\n // whose CSS animation rotation inflates its bbox.\n const filterElLogicalW = this._filterEl!.offsetWidth || 1;\n const filterElRect = this._filterEl!.getBoundingClientRect();\n const ancestorScale = filterElRect.width / filterElLogicalW || 1;\n\n // Read position and transform at t_now.\n const r1 = child.getBoundingClientRect();\n const cx1 = r1.left + r1.width / 2;\n const cy1 = r1.top + r1.height / 2;\n const halfDiag = Math.sqrt((child.offsetWidth || 1) ** 2 + (child.offsetHeight || 1) ** 2) / 2;\n const { angle: angle1, scale: scale1 } = readTransformMatrix(child, seekable);\n\n // Seek back to t_now - shutterMs, but never past the animation's active range.\n for (let i = 0; i < seekable.length; i++) {\n const timing = (seekable[i]!.effect as KeyframeEffect)?.getComputedTiming();\n const activeDuration = timing?.activeDuration;\n const endTime =\n typeof activeDuration === \"number\" && isFinite(activeDuration) ? activeDuration : Infinity;\n seekable[i]!.currentTime = Math.min(Math.max(0, currentTimes[i]! - shutterMs), endTime);\n }\n\n // Read position and transform at t_past.\n const r0 = child.getBoundingClientRect();\n const cx0 = r0.left + r0.width / 2;\n const cy0 = r0.top + r0.height / 2;\n const { angle: angle0, scale: scale0 } = readTransformMatrix(child, seekable);\n\n // Restore animation times and wrapper transforms.\n for (let i = 0; i < seekable.length; i++) {\n seekable[i]!.currentTime = currentTimes[i]!;\n }\n this._outer!.style.transform = outerWas;\n this._filterEl!.style.transform = filterWas;\n\n return {\n dx: (cx1 - cx0) / ancestorScale,\n dy: (cy1 - cy0) / ancestorScale,\n dAngle: angle1 - angle0,\n dScale: scale1 - scale0,\n halfDiag,\n };\n }\n\n /**\n * Converts measured deltas to blur amounts and calls _update() when changed.\n *\n * Translation → directional box blur amount + angle.\n * Rotation → arc displacement amount (signed: + = CCW, − = CW).\n * Arc length at half-diagonal radius: R × |Δθ_rad|.\n * Stored signed so the displacement map is applied in the\n * correct tangential direction.\n * Scale → radial displacement amount: |Δscale| × half-diagonal.\n */\n private _applyTransform(\n dx: number,\n dy: number,\n dAngle: number,\n dScale: number,\n halfDiag: number,\n ): void {\n const s = this.sensitivity;\n\n // Translation: store raw dx/dy scaled by sensitivity (capped at 100px)\n const mag = Math.sqrt(dx ** 2 + dy ** 2);\n const scaledMag = Math.min(mag * s, MAX_TRANS_AMOUNT_PX);\n const newDx = mag < this.threshold ? 0 : (dx / mag) * scaledMag;\n const newDy = mag < this.threshold ? 0 : (dy / mag) * scaledMag;\n const newDir = mag < this.threshold ? 0 : scaledMag;\n const newAngle =\n mag < this.threshold ? this._computedAngle : (Math.atan2(dy, dx) * 180) / Math.PI;\n\n // Rotation arc (signed)\n const dAngleRad = (dAngle * Math.PI) / 180;\n const arcLen = halfDiag * Math.abs(dAngleRad) * s;\n const newRot = Math.min(arcLen, MAX_ROT_AMOUNT_PX) * (dAngle > 0 ? -1 : 1);\n const newRotDeg = Math.abs(dAngle);\n\n // Scale zoom (same threshold as translation)\n const rawZoom = Math.abs(dScale) * halfDiag * s;\n const newZoom = rawZoom < this.threshold ? 0 : Math.min(rawZoom, MAX_ZOOM_AMOUNT_PX);\n\n const changed =\n newDx !== this._computedDx ||\n newDy !== this._computedDy ||\n newDir !== this._computedDirectionalAmount ||\n newAngle !== this._computedAngle ||\n newRot !== this._computedRotAmount ||\n newRotDeg !== this._computedRotDeg ||\n newZoom !== this._computedIsotropicAmount;\n\n if (!changed) return;\n\n this._computedDx = newDx;\n this._computedDy = newDy;\n this._computedDirectionalAmount = newDir;\n this._computedAngle = newAngle;\n this._computedRotAmount = newRot;\n this._computedRotDeg = newRotDeg;\n this._computedIsotropicAmount = newZoom;\n this._update();\n }\n}\n\nif (typeof customElements !== \"undefined\") {\n customElements.define(\"ef-motionblur\", EFMotionBlur);\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ef-motionblur\": EFMotionBlur;\n }\n}\n"],"mappings":"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"}
@@ -38,6 +38,32 @@ declare class EFVideo extends EFVideo_base implements FrameRenderable {
38
38
  static styles: lit3.CSSResult[];
39
39
  canvasRef: lit_html_directives_ref0.Ref<HTMLCanvasElement>;
40
40
  unifiedVideoSeekTask: VideoSeekTask;
41
+ /**
42
+ * Pin the video to a specific source time, making it a freeze frame.
43
+ *
44
+ * When set, `currentSourceTimeMs` ignores the composition timeline and
45
+ * always returns this value — the same frame is shown for the entire
46
+ * duration of the element. Pair with `duration` to control how long the
47
+ * freeze frame occupies in the timeline:
48
+ *
49
+ * `<ef-video src="clip.webm" current-time="4.367s" duration="2s" alpha>`
50
+ *
51
+ * Accepts any duration string: `"4.367s"`, `"4367ms"`, etc.
52
+ *
53
+ * @domAttribute "current-time"
54
+ */
55
+ pinnedSourceTimeMs: number | undefined;
56
+ /**
57
+ * When `current-time` is set, return the pinned source time regardless of
58
+ * where the composition playhead is.
59
+ */
60
+ get currentSourceTimeMs(): number;
61
+ /**
62
+ * When `current-time` is set, allow the `duration` attribute to override
63
+ * the video's intrinsic duration so the timeline footprint is controlled
64
+ * explicitly instead of being derived from the full video length.
65
+ */
66
+ get intrinsicDurationMs(): number | undefined;
41
67
  /**
42
68
  * Get the current rendition being displayed.
43
69
  * @public
@@ -104,7 +130,9 @@ declare class EFVideo extends EFVideo_base implements FrameRenderable {
104
130
  */
105
131
  clearCanvas(): void;
106
132
  /**
107
- * Display a video frame on the canvas
133
+ * Display a video frame on the canvas.
134
+ * VP9-alpha VideoFrames carry alpha natively (I420A / RGBA) — ctx.drawImage
135
+ * composites the alpha automatically when the canvas has alpha: true (default).
108
136
  */
109
137
  displayFrame(frame: VideoFrame, seekToMs: number, parentSpan?: any): void;
110
138
  /**
@@ -2,6 +2,8 @@ import { TWMixin } from "../gui/TWMixin2.js";
2
2
  import { withSpanSync } from "../otel/tracingHelpers.js";
3
3
  import { PRIORITY_VIDEO } from "../preview/FrameController.js";
4
4
  import { updateAnimations } from "./updateAnimations.js";
5
+ import { parseTimeToMs } from "./parseTimeToMs.js";
6
+ import { durationConverter } from "./durationConverter.js";
5
7
  import { __decorate } from "../_virtual/_@oxc-project_runtime@0.95.0/helpers/decorate.js";
6
8
  import { EFMedia } from "./EFMedia.js";
7
9
  import { DelayedLoadingState } from "../DelayedLoadingState.js";
@@ -9,7 +11,7 @@ import { MainVideoInputCache } from "./EFMedia/videoTasks/MainVideoInputCache.js
9
11
  import { ScrubInputCache } from "./EFMedia/videoTasks/ScrubInputCache.js";
10
12
  import debug from "debug";
11
13
  import { css, html } from "lit";
12
- import { customElement, state } from "lit/decorators.js";
14
+ import { customElement, property, state } from "lit/decorators.js";
13
15
  import { context, trace } from "@opentelemetry/api";
14
16
  import { createRef, ref } from "lit/directives/ref.js";
15
17
 
@@ -83,6 +85,38 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
83
85
  `];
84
86
  }
85
87
  /**
88
+ * Read the pinned source time, checking both the Lit-managed property
89
+ * (set asynchronously via attributeChangedCallback) and the raw DOM attribute
90
+ * (set synchronously at element creation). The raw attribute read is a fallback
91
+ * for the window between element construction and Lit's first reactive update,
92
+ * which otherwise causes the first prepareFrame call to see ownCurrentTimeMs=0.
93
+ */
94
+ #readPinnedSourceTimeMs() {
95
+ if (this.pinnedSourceTimeMs !== void 0) return this.pinnedSourceTimeMs;
96
+ const raw = this.getAttribute("current-time");
97
+ if (raw !== null) try {
98
+ return parseTimeToMs(raw);
99
+ } catch {}
100
+ }
101
+ /**
102
+ * When `current-time` is set, return the pinned source time regardless of
103
+ * where the composition playhead is.
104
+ */
105
+ get currentSourceTimeMs() {
106
+ const pinned = this.#readPinnedSourceTimeMs();
107
+ if (pinned !== void 0) return pinned;
108
+ return super.currentSourceTimeMs;
109
+ }
110
+ /**
111
+ * When `current-time` is set, allow the `duration` attribute to override
112
+ * the video's intrinsic duration so the timeline footprint is controlled
113
+ * explicitly instead of being derived from the full video length.
114
+ */
115
+ get intrinsicDurationMs() {
116
+ if (this.#readPinnedSourceTimeMs() !== void 0 && this.explicitDurationMs !== void 0) return this.explicitDurationMs;
117
+ return super.intrinsicDurationMs;
118
+ }
119
+ /**
86
120
  * Cached video sample for the current frame.
87
121
  * Set by prepareFrame(), consumed by renderFrame().
88
122
  */
@@ -316,6 +350,10 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
316
350
  return await mainInput.seek(videoTrackInfo.id, desiredSeekTimeMs);
317
351
  }
318
352
  /**
353
+ * Fetch the paired alpha matte sample at the given time.
354
+ * Returns undefined if no alpha track is configured for this source.
355
+ */
356
+ /**
319
357
  * Delayed loading state manager for user feedback
320
358
  */
321
359
  #delayedLoadingState;
@@ -323,12 +361,13 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
323
361
  super();
324
362
  this.canvasRef = createRef();
325
363
  this.unifiedVideoSeekTask = new VideoSeekTask();
364
+ this.pinnedSourceTimeMs = void 0;
326
365
  this.loadingState = {
327
366
  isLoading: false,
328
367
  operation: null,
329
368
  message: ""
330
369
  };
331
- this.#delayedLoadingState = new DelayedLoadingState(100, (isLoading, message) => {
370
+ this.#delayedLoadingState = new DelayedLoadingState(400, (isLoading, message) => {
332
371
  this.setLoadingState(isLoading, null, message);
333
372
  });
334
373
  }
@@ -463,7 +502,9 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
463
502
  if (ctx) ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
464
503
  }
465
504
  /**
466
- * Display a video frame on the canvas
505
+ * Display a video frame on the canvas.
506
+ * VP9-alpha VideoFrames carry alpha natively (I420A / RGBA) — ctx.drawImage
507
+ * composites the alpha automatically when the canvas has alpha: true (default).
467
508
  */
468
509
  displayFrame(frame, seekToMs, parentSpan) {
469
510
  const parentContext = parentSpan ? trace.setSpan(context.active(), parentSpan) : void 0;
@@ -516,6 +557,7 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
516
557
  throw new Error(`Frame display failed: Video frame has null format at time ${seekToMs}ms. This indicates corrupted or incompatible video data.`);
517
558
  }
518
559
  const tDrawStart = performance.now();
560
+ ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
519
561
  ctx.drawImage(frame, 0, 0, this.canvasElement.width, this.canvasElement.height);
520
562
  const tDrawEnd = performance.now();
521
563
  span.setAttribute("drawImageMs", Math.round((tDrawEnd - tDrawStart) * 100) / 100);
@@ -961,6 +1003,11 @@ let EFVideo = class EFVideo$1 extends TWMixin(EFMedia) {
961
1003
  }
962
1004
  }
963
1005
  };
1006
+ __decorate([property({
1007
+ type: Number,
1008
+ attribute: "current-time",
1009
+ converter: durationConverter
1010
+ })], EFVideo.prototype, "pinnedSourceTimeMs", void 0);
964
1011
  __decorate([state()], EFVideo.prototype, "loadingState", void 0);
965
1012
  EFVideo = __decorate([customElement("ef-video")], EFVideo);
966
1013