8.1 KiB
Canvas proxy maintenance
Public entry: src/index.ts; public declarations remain in
types/artplayer-proxy-canvas.d.ts. The factory accepts the optional drawing callback,
then synchronously returns an actual HTMLCanvasElement when registered with ArtPlayer.
The root preserves the latest npm 1.1.0 pure factory, optional callback and exact Canvas
return extraction. Do not attach a required default member to this type: that breaks
assigning an ordinary historical replacement function back to typeof factory.
MediaCanvas is an explicit type view, not a narrowed factory result. CommonJS and browser
factory.default remain runtime self aliases. Accurate RuntimeFactory lives behind the
additive /runtime entry, whose runtime.d.mts/runtime.d.cts bridges distinguish ESM and
CommonJS and whose runtime.d.ts supports old Node10 export= resolution. Both entrypoints
load the same existing dist files. The old root declaration bridges remain packaged,
but root/legacy conditional types deliberately select the original .d.ts module shape.
Root NodeNext ESM therefore keeps the published namespace shape; it is not silently made
callable. Use /runtime for precise ESM default calls. The package README documents the
approved 1.0 export= migration; no JavaScript calls were removed by this type decision.
Historical JavaScript falsy callback arguments remain ignored at runtime; the typed public
callback remains optional and function-valued.
| Module | Ownership and responsibility |
|---|---|
| index.ts | Host event subscriptions, deferred media forwarding, initialization rollback and terminal disposal |
| adapter.ts | Preserve native canvas methods; forward only video properties absent from canvas; guard escaped media actions after disposal |
| subtitles.ts | Supply the initial native metadata track and route the core's actual HTML track insertion into the backing video |
| media.ts | Connect the backing video before playback and release its source, stream reference and DOM node |
| geometry.ts | Finite intrinsic dimensions and aspect-fit canvas/padding calculations |
| renderer.ts | Decoded-frame eligibility, bitmap acquisition/close, callback and draw/error notification order |
| scheduler.ts | One outstanding draw, coalesced requests, generations and nullable RAF ownership |
Only the entry receives ArtPlayer. The renderer has no host/DOM ownership, the scheduler has no media knowledge, and geometry is independent of scheduling. No package imports private core implementation modules.
State and event order
Canvas methods keep their receiver and win over same-named video methods for
ordinary Canvas content. appendChild has one intentional
subtitle-specific adaptation: active HTML track elements attach to the backing video,
where native loading and cuechange work. Other nodes still attach to the Canvas.
The initial empty metadata track makes the old core's synchronous textTracks[0]
capability check succeed; it is removed before the first actual track is appended.
The core owns actual track replacement/removal and subtitle URLs. After destruction,
escaped appendChild cannot attach more tracks to the unloaded private video.
This fixes the formerly empty subtitle path; a working track's parent is now VIDEO.
The initialization is inside the same rollback boundary as host registration.
Media properties remain live enumerable/configurable descriptors. Assigning src/srcObject or invoking load invalidates pending frames; the returned canvas and callback arguments keep their identities. The video is connected before play with absolute positioning, transparent opacity, no focus or pointer interaction, and intrinsic CSS sizing. Do not change it to a 1px square: tested WebKit then reports 1x1 media dimensions and loses the source geometry.
Media forwarding remains deferred until core initialization has installed its event registry.
Native events are forwarded as video:<type> with the original Event. Native removal is
owned before calling the extensible art.proxy registrar, so partial registration can roll back.
Host subscriptions are also owned before art.on, including reentrant destruction during setup.
play starts one draw chain; duplicate play does not start another. resize coalesces a new draw and invalidates an in-flight result. pause/source replacement invalidates pending work and cancels the current RAF, including ID zero. Paused seek requests one fresh frame. After bitmap acquisition, stale results are closed without drawing. Acquired bitmaps are closed in finally even if drawImage throws, and before invoking the public callback. An active callback failure emits the original error; a callback destroying the host cannot emit a subsequent draw event or restart work. Observer failures cannot strand the scheduler busy.
readyState alone does not prove a drawable frame. Seeking or insufficient data suspends drawing. The recorded Chromium first-frame case rejects bitmap acquisition with InvalidStateError at currentTime 0 and zero video frames even at readyState 4; that exact acquisition condition is deferred until another request. A zero frame counter is not a general capability check: tested WebKit draws successfully with that counter at zero. Errors from user callbacks or drawImage are not classified as bitmap acquisition failures.
destroy invalidates generations, clears the deferred timer, removes all host/native subscriptions, pauses and unloads backing video, releases srcObject without stopping consumer-owned tracks, removes the internal video and clears canvas buffers. Cleanup attempts every resource even when one disposer throws. Captured play/load methods and forwarded setters become inert. Native Canvas methods still operate on the same element.
Verification and remaining work
yarn test:canvas runs frozen historical and candidate lifecycle tests. Candidate tests can
be run against the frozen workspace with ARTPLAYER_CANVAS_BASELINE=1. Historical inputs
come from verified archives/Git via refactor/scripts/canvas-contract.mjs, not current dist.
test/browser/canvas-lifecycle.spec.js tests actual candidate source, native pixels and
dimensions, paused seek, source replacement, callback destruction and escaped methods on
actual npm core 5.4.0 and the candidate core in three engines. ambilight-proxy.spec.js
uses the candidate source loader after module splitting; its palette test proves sampling
geometry and is separate from native media pixel acceptance. npm core 5.1.7 has no proxy
option and is only a native-player capability control.
yarn test:canvas-types-package installs real archived and candidate packages outside the
workspace, verifies installed bytes and checks old/current compiler modes. It preserves
the actual old NodeNext direct-import errors and 1.0 export= differences, while testing
latest namespace replacements, plain factory bidirectional assignments, runtime self
identity and negative statements at their exact lines. No skipLibCheck or strictness
relaxation is used. The public declarations also generate the local editor declaration
through the semantic generator; the editor root stays plain, with an explicit
artplayerProxyCanvas.RuntimeFactory assertion for code needing the runtime self alias.
Broader installed artifact contents/deep paths and 8082 demos remain 06.
Real Safari/mobile/device scope remains 05; Windows Playwright WebKit is not a device claim.
canvas-subtitles.spec.js exercises native VTT load, paused seeking, replacement,
single-track ownership, ordinary Canvas child insertion/removal and escaped insertion
after destroy on old/new cores. It also belongs to the installed browser scope;
keep its bytes and native engine provenance with the lifecycle report.
See refactor/canvas-validation.md and the task evidence for actual outcomes and limitations.
canvas-dpip.spec.js follows the same Canvas, backing video and native subtitle
track into actual Document PiP windows. It covers playback/draw advancement,
paused subtitle seeking and close/native-close/destroy resource ownership. The
installed scope requires Document PiP's own verified package bytes as well; a
source helper may not fill a missing installed package. Unsupported native APIs
are recorded separately and never treated as native playback evidence.