fix(canvas): [PKG-CANVAS-04] preserve public type and export compatibility

This commit is contained in:
Harvey Zhao committed 2026-09-12 13:29:10 +08:00
1 parent 2b319109cd
commit 224572032a
29 files changed
+682 -41

No files matched your search

+19 -8
View File
@@ -1,10 +1,21 @@
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
type Result = HTMLCanvasElement
declare const artplayerProxyCanvas: (option?: Option) => (art: Artplayer) => Result
export default artplayerProxyCanvas
// Generated from the package public declaration by yarn build:ts. Do not edit.
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
declare namespace artplayerProxyCanvas {
/** Runs after drawing and bitmap release, before the draw event. */
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
/** Preserve the exact published return type and its assignability. */
type Result = HTMLCanvasElement
/** Opt-in view of supported enumerable media members; native Canvas members win. */
type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
interface Callable {
/** Keep Parameters extraction optional, as published in 1.1.0. */
(option?: Option): (art: Artplayer) => Result
}
interface Factory extends Callable {
/** Same factory, for historical require(package).default calls. */
readonly default: Factory
}
}
declare const artplayerProxyCanvas: artplayerProxyCanvas.Factory
export = artplayerProxyCanvas
export as namespace artplayerProxyCanvas;
+1 -1
View File
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,s=null;function l(){null!==s&&cancelAnimationFrame(s),s=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,s=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(s,s)}else n&&null===s&&(s=requestAnimationFrame(()=>{s=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,l(),c())}function f(){n=!1,r=!1,a++,l()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:f,destroy(){i=!0,f()}}}return function(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let s=i,l=i.isDestroy,c=null;const u=[],f=()=>!l&&!s?.isDestroy,d=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>s?.template?.$player,f),p=function(t,n,i,o,r){const a=t.getContext("2d");let s=!1;return{available:()=>!!a||(s||(s=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(s){const l=()=>s()&&n.readyState>=2&&!n.seeking;if(!a||!l()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{l()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!l())return;i&&i(a,n),l()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;l()&&!e&&r(u)}}}}(r,a,n,(e,t)=>s?.emit("artplayerProxyCanvas:draw",e,t),e=>s?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,s]of r)Reflect.set(e,a,s)}(r,a,f,h.stop,d.mount),l)return s=null,r;function y(){if(l)return;l=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),d.destroy])try{n()}catch(t){e.push(t)}if(s=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),l&&o()}function v(){p.available()&&h.request()}try{m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{d.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const s=n.videoWidth/n.videoHeight,l=r/a>s?a*s:r,c=r/a>s?a:r/s;t.width=l,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-l)/2}px`}(r,a,s?.template?.$player,s?.option.autoSize),v()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{d.mount();for(const e of o.config.events){if(!f())break;const t=e=>{f()&&s?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);f()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,s=null;function l(){null!==s&&cancelAnimationFrame(s),s=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,s=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(s,s)}else n&&null===s&&(s=requestAnimationFrame(()=>{s=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,l(),c())}function f(){n=!1,r=!1,a++,l()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:f,destroy(){i=!0,f()}}}function n(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let s=i,l=i.isDestroy,c=null;const u=[],f=()=>!l&&!s?.isDestroy,d=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>s?.template?.$player,f),p=function(t,n,i,o,r){const a=t.getContext("2d");let s=!1;return{available:()=>!!a||(s||(s=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(s){const l=()=>s()&&n.readyState>=2&&!n.seeking;if(!a||!l()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{l()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!l())return;i&&i(a,n),l()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;l()&&!e&&r(u)}}}}(r,a,n,(e,t)=>s?.emit("artplayerProxyCanvas:draw",e,t),e=>s?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,s]of r)Reflect.set(e,a,s)}(r,a,f,h.stop,d.mount),l)return s=null,r;function y(){if(l)return;l=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),d.destroy])try{n()}catch(t){e.push(t)}if(s=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),l&&o()}function v(){p.available()&&h.request()}try{m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{d.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const s=n.videoWidth/n.videoHeight,l=r/a>s?a*s:r,c=r/a>s?a:r/s;t.width=l,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-l)/2}px`}(r,a,s?.template?.$player,s?.option.autoSize),v()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{d.mount();for(const e of o.config.events){if(!f())break;const t=e=>{f()&&s?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);f()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}return Object.assign(n,{default:n})});
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const l=t.getContext("2d");let a=!1;return{available:()=>!!l||(a||(a=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(a){return s=this,u=null,c=function*(){const s=()=>a()&&n.readyState>=2&&!n.seeking;if(!l||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&l.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else l.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(l,n),s()&&o(l,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,l=0,a=null;function s(){null!==a&&cancelAnimationFrame(a),a=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=l,a=()=>{o=!1,u()};e(()=>!i&&t()&&l===n).then(a,a)}else n&&null===a&&(a=requestAnimationFrame(()=>{a=null,r=!0,u()}))}function c(){!i&&t()&&(l++,r=!0,s(),u())}function d(){n=!1,r=!1,l++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}return function(i){return o=>{const r=o.constructor,l=r.utils.createElement("canvas"),a=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),h=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),p=t(l,a,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const l in e){const t=Reflect.get(e,l);"function"===typeof t&&r.set(l,(...n)=>Reflect.apply(t,e,n))}for(const l in t)l in e||Object.defineProperty(e,l,{get(){const e=Reflect.get(t,l);return"function"!==typeof e?e:(...r)=>n()?("load"===l&&i(),"play"===l&&o(),Reflect.apply(e,t,r)):"play"===l?Promise.resolve():void 0},set(e){n()&&("src"!==l&&"srcObject"!==l||i(),Reflect.set(t,l,e))},configurable:!0,enumerable:!0});for(const[l,a]of r)Reflect.set(e,l,a)}(l,a,f,y.stop,h.mount),u)return s=null,l;function v(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),h.destroy])try{n()}catch(t){e.push(t)}if(s=null,l.width=0,l.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){p.available()&&y.request()}try{m("destroy",v),m("video:loadedmetadata",()=>{e(a)&&(l.width=a.videoWidth,l.height=a.videoHeight)}),m("video:play",()=>{h.mount(),p.available()&&y.start()}),m("video:pause",y.stop),m("video:emptied",y.stop),m("video:seeked",g),m("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,l=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(l)||r<=0||l<=0)return;const a=n.videoWidth/n.videoHeight,s=r/l>a?l*a:r,u=r/l>a?l:r/a;t.width=s,t.height=u,t.style.padding=`${(l-u)/2}px ${(r-s)/2}px`}(l,a,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{h.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>a.removeEventListener(e,t);d.push(n);const i=o.proxy(a,e,t);f()?d.push(i):i()}}catch(e){try{v()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{v()}catch(w){}throw b}return l}}});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const l=t.getContext("2d");let a=!1;return{available:()=>!!l||(a||(a=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(a){return s=this,u=null,c=function*(){const s=()=>a()&&n.readyState>=2&&!n.seeking;if(!l||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&l.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else l.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(l,n),s()&&o(l,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,l=0,a=null;function s(){null!==a&&cancelAnimationFrame(a),a=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=l,a=()=>{o=!1,u()};e(()=>!i&&t()&&l===n).then(a,a)}else n&&null===a&&(a=requestAnimationFrame(()=>{a=null,r=!0,u()}))}function c(){!i&&t()&&(l++,r=!0,s(),u())}function d(){n=!1,r=!1,l++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}function i(i){return o=>{const r=o.constructor,l=r.utils.createElement("canvas"),a=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),h=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),p=t(l,a,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const l in e){const t=Reflect.get(e,l);"function"===typeof t&&r.set(l,(...n)=>Reflect.apply(t,e,n))}for(const l in t)l in e||Object.defineProperty(e,l,{get(){const e=Reflect.get(t,l);return"function"!==typeof e?e:(...r)=>n()?("load"===l&&i(),"play"===l&&o(),Reflect.apply(e,t,r)):"play"===l?Promise.resolve():void 0},set(e){n()&&("src"!==l&&"srcObject"!==l||i(),Reflect.set(t,l,e))},configurable:!0,enumerable:!0});for(const[l,a]of r)Reflect.set(e,l,a)}(l,a,f,y.stop,h.mount),u)return s=null,l;function v(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),h.destroy])try{n()}catch(t){e.push(t)}if(s=null,l.width=0,l.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){p.available()&&y.request()}try{m("destroy",v),m("video:loadedmetadata",()=>{e(a)&&(l.width=a.videoWidth,l.height=a.videoHeight)}),m("video:play",()=>{h.mount(),p.available()&&y.start()}),m("video:pause",y.stop),m("video:emptied",y.stop),m("video:seeked",g),m("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,l=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(l)||r<=0||l<=0)return;const a=n.videoWidth/n.videoHeight,s=r/l>a?l*a:r,u=r/l>a?l:r/a;t.width=s,t.height=u,t.style.padding=`${(l-u)/2}px ${(r-s)/2}px`}(l,a,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{h.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>a.removeEventListener(e,t);d.push(n);const i=o.proxy(a,e,t);f()?d.push(i):i()}}catch(e){try{v()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{v()}catch(w){}throw b}return l}}return Object.assign(i,{default:i})});
+2 -1
View File
@@ -322,6 +322,7 @@ function artplayerProxyCanvas(callback) {
return canvas;
};
}
const index = Object.assign(artplayerProxyCanvas, { default: artplayerProxyCanvas });
export {
artplayerProxyCanvas as default
index as default
};
+2 -1
View File
@@ -72,7 +72,8 @@
"test:contracts": "node refactor/scripts/run-contracts.mjs",
"test:ambilight": "node --test test/ambilight.test.js test/ambilight-lifecycle.test.js test/ambilight-proxy.test.js",
"test:ambilight-types-package": "node refactor/scripts/ambilight-package-types.mjs",
"test:canvas": "node --test test/canvas.test.js test/canvas-lifecycle.test.js"
"test:canvas": "node --test test/canvas.test.js test/canvas-lifecycle.test.js",
"test:canvas-types-package": "node refactor/scripts/canvas-package-types.mjs"
},
"browserslist": "last 1 Chrome version",
"devDependencies": {
@@ -3,7 +3,10 @@
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.
Public declaration/export consumer work continues in PKG-CANVAS-04.
The public namespace preserves the optional callback and exact Canvas return extraction.
MediaCanvas is an explicit type view, not a narrowed factory result. CommonJS and browser
factory.default are self aliases; .d.mts/.d.cts wrappers distinguish ESM and CommonJS
resolution, and typesVersions keeps the legacy path available to older TypeScript.
Historical JavaScript falsy callback arguments remain ignored at runtime; the typed public
callback remains optional and function-valued.
@@ -68,6 +71,9 @@ uses the candidate source loader after module splitting; its palette test proves
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.
Public type/import compatibility, full installed artifacts and 8082 demos remain 04/06.
`yarn test:canvas-types-package` installs real archived and candidate packages outside the
workspace, verifies installed bytes and checks old/current compiler modes. The public
namespace also generates the local editor declaration through the semantic generator.
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.
See `refactor/canvas-validation.md` and the task evidence for actual outcomes and limitations.
+46
View File
@@ -2,6 +2,52 @@
canvas proxy for ArtPlayer
## Usage
```js
import Artplayer from 'artplayer'
import canvas from 'artplayer-proxy-canvas'
const art = new Artplayer({
container: '#player',
url: 'video.mp4',
proxy: canvas((context, video) => {
// Post-process the frame with the native 2D context.
}),
})
```
The callback is optional. Registration returns a native Canvas synchronously. Its own
methods and properties keep Canvas behavior; video-only enumerable properties are
forwarded to the backing video. Use ArtPlayer's `video:*` events for media notifications.
The callback receives the actual video, and a successful callback is followed by
`artplayerProxyCanvas:draw`. Drawing failures emit `artplayerProxyCanvas:error`.
ES modules use the default import. Both `require('artplayer-proxy-canvas')` and the
historical `require('artplayer-proxy-canvas').default` refer to the same factory. The
`artplayer-proxy-canvas/legacy` entry preserves the legacy browser build.
## TypeScript
The optional callback and exact `HTMLCanvasElement` return type retain published 1.1.0
inference. `Option`, `Result`, `Factory` and `MediaCanvas` are exported types. The last is
an explicit view for code using supported forwarded media members:
```ts
import type { MediaCanvas, Option } from 'artplayer-proxy-canvas'
const callback: Option = (context, video) => context.drawImage(video, 0, 0)
const media = art.template.$video as MediaCanvas
await media.play()
```
`MediaCanvas` keeps Canvas members when names overlap, including dimensions and DOM
listeners. It does not add unsupported browser APIs. Factory return inference remains
`HTMLCanvasElement`, so existing assignments and `ReturnType` consumers keep compiling.
Runtime falsy callback arguments retain historical behavior; declarations accept an
optional function, matching the published types. Implementation and test guidance is in
[ARCHITECTURE.md](ARCHITECTURE.md).
## Demo
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-proxy-canvas/index.js&example=canvas)
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,s=null;function l(){null!==s&&cancelAnimationFrame(s),s=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,s=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(s,s)}else n&&null===s&&(s=requestAnimationFrame(()=>{s=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,l(),c())}function f(){n=!1,r=!1,a++,l()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:f,destroy(){i=!0,f()}}}return function(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let s=i,l=i.isDestroy,c=null;const u=[],f=()=>!l&&!s?.isDestroy,d=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>s?.template?.$player,f),p=function(t,n,i,o,r){const a=t.getContext("2d");let s=!1;return{available:()=>!!a||(s||(s=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(s){const l=()=>s()&&n.readyState>=2&&!n.seeking;if(!a||!l()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{l()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!l())return;i&&i(a,n),l()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;l()&&!e&&r(u)}}}}(r,a,n,(e,t)=>s?.emit("artplayerProxyCanvas:draw",e,t),e=>s?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,s]of r)Reflect.set(e,a,s)}(r,a,f,h.stop,d.mount),l)return s=null,r;function y(){if(l)return;l=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),d.destroy])try{n()}catch(t){e.push(t)}if(s=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),l&&o()}function v(){p.available()&&h.request()}try{m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{d.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const s=n.videoWidth/n.videoHeight,l=r/a>s?a*s:r,c=r/a>s?a:r/s;t.width=l,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-l)/2}px`}(r,a,s?.template?.$player,s?.option.autoSize),v()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{d.mount();for(const e of o.config.events){if(!f())break;const t=e=>{f()&&s?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);f()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(e,t){let n=!1,i=!1,o=!1,r=!1,a=0,s=null;function l(){null!==s&&cancelAnimationFrame(s),s=null}function c(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=a,s=()=>{o=!1,c()};e(()=>!i&&t()&&a===n).then(s,s)}else n&&null===s&&(s=requestAnimationFrame(()=>{s=null,r=!0,c()}))}function u(){!i&&t()&&(a++,r=!0,l(),c())}function f(){n=!1,r=!1,a++,l()}return{request:u,start(){n||i||!t()||(n=!0,u())},stop:f,destroy(){i=!0,f()}}}function n(n){return i=>{const o=i.constructor,r=o.utils.createElement("canvas"),a=o.utils.createElement("video");let s=i,l=i.isDestroy,c=null;const u=[],f=()=>!l&&!s?.isDestroy,d=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>s?.template?.$player,f),p=function(t,n,i,o,r){const a=t.getContext("2d");let s=!1;return{available:()=>!!a||(s||(s=!0,r(new Error("Canvas 2D context is unavailable"))),!1),async draw(s){const l=()=>s()&&n.readyState>=2&&!n.seeking;if(!a||!l()||!e(n)||!t.width||!t.height)return;let c=!1;try{if("undefined"!==typeof createImageBitmap){c=!0;const e=await createImageBitmap(n);c=!1;try{l()&&a.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else a.drawImage(n,0,0,t.width,t.height);if(!l())return;i&&i(a,n),l()&&o(a,n)}catch(u){const e=c&&"object"===typeof u&&null!==u&&"name"in u&&"InvalidStateError"===u.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;l()&&!e&&r(u)}}}}(r,a,n,(e,t)=>s?.emit("artplayerProxyCanvas:draw",e,t),e=>s?.emit("artplayerProxyCanvas:error",e)),h=t(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const a in e){const t=Reflect.get(e,a);"function"===typeof t&&r.set(a,(...n)=>Reflect.apply(t,e,n))}for(const a in t)a in e||Object.defineProperty(e,a,{get(){const e=Reflect.get(t,a);return"function"!==typeof e?e:(...r)=>n()?("load"===a&&i(),"play"===a&&o(),Reflect.apply(e,t,r)):"play"===a?Promise.resolve():void 0},set(e){n()&&("src"!==a&&"srcObject"!==a||i(),Reflect.set(t,a,e))},configurable:!0,enumerable:!0});for(const[a,s]of r)Reflect.set(e,a,s)}(r,a,f,h.stop,d.mount),l)return s=null,r;function y(){if(l)return;l=!0,h.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...u.splice(0).reverse(),d.destroy])try{n()}catch(t){e.push(t)}if(s=null,r.width=0,r.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},o=()=>i.off(e,n);u.push(o),i.on(e,n),l&&o()}function v(){p.available()&&h.request()}try{m("destroy",y),m("video:loadedmetadata",()=>{e(a)&&(r.width=a.videoWidth,r.height=a.videoHeight)}),m("video:play",()=>{d.mount(),p.available()&&h.start()}),m("video:pause",h.stop),m("video:emptied",h.stop),m("video:seeked",v),m("resize",()=>{!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,a=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(a)||r<=0||a<=0)return;const s=n.videoWidth/n.videoHeight,l=r/a>s?a*s:r,c=r/a>s?a:r/s;t.width=l,t.height=c,t.style.padding=`${(a-c)/2}px ${(r-l)/2}px`}(r,a,s?.template?.$player,s?.option.autoSize),v()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{d.mount();for(const e of o.config.events){if(!f())break;const t=e=>{f()&&s?.emit(`video:${e.type}`,e)},n=()=>a.removeEventListener(e,t);u.push(n);const o=i.proxy(a,e,t);f()?u.push(o):o()}}catch(e){try{y()}catch{}i.emit("artplayerProxyCanvas:error",e)}}))}catch(g){try{y()}catch{}throw g}return r}}return Object.assign(n,{default:n})});
@@ -4,4 +4,4 @@
* (c) 2017-2026 Harvey Zhao
* Released under the MIT License.
*/
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const l=t.getContext("2d");let a=!1;return{available:()=>!!l||(a||(a=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(a){return s=this,u=null,c=function*(){const s=()=>a()&&n.readyState>=2&&!n.seeking;if(!l||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&l.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else l.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(l,n),s()&&o(l,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,l=0,a=null;function s(){null!==a&&cancelAnimationFrame(a),a=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=l,a=()=>{o=!1,u()};e(()=>!i&&t()&&l===n).then(a,a)}else n&&null===a&&(a=requestAnimationFrame(()=>{a=null,r=!0,u()}))}function c(){!i&&t()&&(l++,r=!0,s(),u())}function d(){n=!1,r=!1,l++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}return function(i){return o=>{const r=o.constructor,l=r.utils.createElement("canvas"),a=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),h=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),p=t(l,a,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const l in e){const t=Reflect.get(e,l);"function"===typeof t&&r.set(l,(...n)=>Reflect.apply(t,e,n))}for(const l in t)l in e||Object.defineProperty(e,l,{get(){const e=Reflect.get(t,l);return"function"!==typeof e?e:(...r)=>n()?("load"===l&&i(),"play"===l&&o(),Reflect.apply(e,t,r)):"play"===l?Promise.resolve():void 0},set(e){n()&&("src"!==l&&"srcObject"!==l||i(),Reflect.set(t,l,e))},configurable:!0,enumerable:!0});for(const[l,a]of r)Reflect.set(e,l,a)}(l,a,f,y.stop,h.mount),u)return s=null,l;function v(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),h.destroy])try{n()}catch(t){e.push(t)}if(s=null,l.width=0,l.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){p.available()&&y.request()}try{m("destroy",v),m("video:loadedmetadata",()=>{e(a)&&(l.width=a.videoWidth,l.height=a.videoHeight)}),m("video:play",()=>{h.mount(),p.available()&&y.start()}),m("video:pause",y.stop),m("video:emptied",y.stop),m("video:seeked",g),m("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,l=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(l)||r<=0||l<=0)return;const a=n.videoWidth/n.videoHeight,s=r/l>a?l*a:r,u=r/l>a?l:r/a;t.width=s,t.height=u,t.style.padding=`${(l-u)/2}px ${(r-s)/2}px`}(l,a,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{h.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>a.removeEventListener(e,t);d.push(n);const i=o.proxy(a,e,t);f()?d.push(i):i()}}catch(e){try{v()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{v()}catch(w){}throw b}return l}}});
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.artplayerProxyCanvas=t(),define(function(){return e.artplayerProxyCanvas})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerProxyCanvas=t()}(this,function(){"use strict";function e(e){return Number.isFinite(e.videoWidth)&&Number.isFinite(e.videoHeight)&&e.videoWidth>0&&e.videoHeight>0}function t(t,n,i,o,r){const l=t.getContext("2d");let a=!1;return{available:()=>!!l||(a||(a=!0,r(new Error("Canvas 2D context is unavailable"))),!1),draw(a){return s=this,u=null,c=function*(){const s=()=>a()&&n.readyState>=2&&!n.seeking;if(!l||!s()||!e(n)||!t.width||!t.height)return;let u=!1;try{if("undefined"!==typeof createImageBitmap){u=!0;const e=yield createImageBitmap(n);u=!1;try{s()&&l.drawImage(e,0,0,t.width,t.height)}finally{e.close()}}else l.drawImage(n,0,0,t.width,t.height);if(!s())return;i&&i(l,n),s()&&o(l,n)}catch(c){const e=u&&"object"===typeof c&&null!==c&&"name"in c&&"InvalidStateError"===c.name&&0===n.currentTime&&"function"===typeof n.getVideoPlaybackQuality&&0===n.getVideoPlaybackQuality().totalVideoFrames;s()&&!e&&r(c)}},new Promise((e,t)=>{var n=e=>{try{o(c.next(e))}catch(n){t(n)}},i=e=>{try{o(c.throw(e))}catch(n){t(n)}},o=t=>t.done?e(t.value):Promise.resolve(t.value).then(n,i);o((c=c.apply(s,u)).next())});var s,u,c}}}function n(e,t){let n=!1,i=!1,o=!1,r=!1,l=0,a=null;function s(){null!==a&&cancelAnimationFrame(a),a=null}function u(){if(!o&&!i&&t())if(r){r=!1,o=!0;const n=l,a=()=>{o=!1,u()};e(()=>!i&&t()&&l===n).then(a,a)}else n&&null===a&&(a=requestAnimationFrame(()=>{a=null,r=!0,u()}))}function c(){!i&&t()&&(l++,r=!0,s(),u())}function d(){n=!1,r=!1,l++,s()}return{request:c,start(){n||i||!t()||(n=!0,c())},stop:d,destroy(){i=!0,d()}}}function i(i){return o=>{const r=o.constructor,l=r.utils.createElement("canvas"),a=r.utils.createElement("video");let s=o,u=o.isDestroy,c=null;const d=[],f=()=>!u&&!(null==s?void 0:s.isDestroy),h=function(e,t,n){let i=!1;return{mount(){if(i||!n())return;const o=t();o&&(e.setAttribute("aria-hidden","true"),e.tabIndex=-1,e.playsInline=!0,Object.assign(e.style,{position:"absolute",width:"auto",height:"auto",opacity:"0",pointerEvents:"none",left:"0",top:"0"}),o.appendChild(e),i=!0,n()||e.remove())},destroy(){const t=[],n=()=>{e.srcObject=null};for(const i of[()=>e.pause(),()=>e.removeAttribute("src"),n,()=>e.load(),()=>e.remove()])try{i()}catch(o){t.push(o)}if(i=!1,t.length)throw t[0]}}}(a,()=>{var e;return null==(e=null==s?void 0:s.template)?void 0:e.$player},f),p=t(l,a,i,(e,t)=>null==s?void 0:s.emit("artplayerProxyCanvas:draw",e,t),e=>null==s?void 0:s.emit("artplayerProxyCanvas:error",e)),y=n(p.draw,f);if(function(e,t,n,i,o){const r=new Map;for(const l in e){const t=Reflect.get(e,l);"function"===typeof t&&r.set(l,(...n)=>Reflect.apply(t,e,n))}for(const l in t)l in e||Object.defineProperty(e,l,{get(){const e=Reflect.get(t,l);return"function"!==typeof e?e:(...r)=>n()?("load"===l&&i(),"play"===l&&o(),Reflect.apply(e,t,r)):"play"===l?Promise.resolve():void 0},set(e){n()&&("src"!==l&&"srcObject"!==l||i(),Reflect.set(t,l,e))},configurable:!0,enumerable:!0});for(const[l,a]of r)Reflect.set(e,l,a)}(l,a,f,y.stop,h.mount),u)return s=null,l;function v(){if(u)return;u=!0,y.destroy(),null!==c&&clearTimeout(c),c=null;const e=[];for(const n of[...d.splice(0).reverse(),h.destroy])try{n()}catch(t){e.push(t)}if(s=null,l.width=0,l.height=0,e.length)throw e[0]}function m(e,t){if(!f())return;const n=()=>{("destroy"===e||f())&&t()},i=()=>o.off(e,n);d.push(i),o.on(e,n),u&&i()}function g(){p.available()&&y.request()}try{m("destroy",v),m("video:loadedmetadata",()=>{e(a)&&(l.width=a.videoWidth,l.height=a.videoHeight)}),m("video:play",()=>{h.mount(),p.available()&&y.start()}),m("video:pause",y.stop),m("video:emptied",y.stop),m("video:seeked",g),m("resize",()=>{var t;!function(t,n,i,o){if(!i||o||!e(n))return;const r=i.clientWidth,l=i.clientHeight;if(!Number.isFinite(r)||!Number.isFinite(l)||r<=0||l<=0)return;const a=n.videoWidth/n.videoHeight,s=r/l>a?l*a:r,u=r/l>a?l:r/a;t.width=s,t.height=u,t.style.padding=`${(l-u)/2}px ${(r-s)/2}px`}(l,a,null==(t=null==s?void 0:s.template)?void 0:t.$player,null==s?void 0:s.option.autoSize),g()}),f()&&(c=setTimeout(()=>{if(c=null,f())try{h.mount();for(const e of r.config.events){if(!f())break;const t=e=>{f()&&(null==s||s.emit(`video:${e.type}`,e))},n=()=>a.removeEventListener(e,t);d.push(n);const i=o.proxy(a,e,t);f()?d.push(i):i()}}catch(e){try{v()}catch(t){}o.emit("artplayerProxyCanvas:error",e)}}))}catch(b){try{v()}catch(w){}throw b}return l}}return Object.assign(i,{default:i})});
@@ -322,6 +322,7 @@ function artplayerProxyCanvas(callback) {
return canvas;
};
}
const index = Object.assign(artplayerProxyCanvas, { default: artplayerProxyCanvas });
export {
artplayerProxyCanvas as default
index as default
};
+24 -7
View File
@@ -19,19 +19,36 @@
],
"exports": {
".": {
"types": "./types/artplayer-proxy-canvas.d.ts",
"import": "./dist/artplayer-proxy-canvas.mjs",
"require": "./dist/artplayer-proxy-canvas.js"
"import": {
"types": "./types/artplayer-proxy-canvas.d.mts",
"default": "./dist/artplayer-proxy-canvas.mjs"
},
"require": {
"types": "./types/artplayer-proxy-canvas.d.cts",
"default": "./dist/artplayer-proxy-canvas.js"
}
},
"./legacy": {
"types": "./types/artplayer-proxy-canvas.d.ts",
"import": "./dist/artplayer-proxy-canvas.legacy.js",
"require": "./dist/artplayer-proxy-canvas.legacy.js"
"import": {
"types": "./types/artplayer-proxy-canvas.d.cts",
"default": "./dist/artplayer-proxy-canvas.legacy.js"
},
"require": {
"types": "./types/artplayer-proxy-canvas.d.cts",
"default": "./dist/artplayer-proxy-canvas.legacy.js"
}
}
},
"main": "./dist/artplayer-proxy-canvas.js",
"module": "./dist/artplayer-proxy-canvas.mjs",
"types": "./types/artplayer-proxy-canvas.d.ts",
"legacy": "./dist/artplayer-proxy-canvas.legacy.js",
"browserslist": "last 1 Chrome version"
"browserslist": "last 1 Chrome version",
"typesVersions": {
"*": {
"legacy": [
"types/artplayer-proxy-canvas.d.ts"
]
}
}
}
+3 -1
View File
@@ -6,7 +6,7 @@ import { ownMedia } from './media'
import { createRenderer } from './renderer'
import { createFrameScheduler } from './scheduler'
export default function artplayerProxyCanvas(callback?: DrawCallback) {
function artplayerProxyCanvas(callback?: DrawCallback) {
return (art: Artplayer): HTMLCanvasElement => {
const constructor = art.constructor as typeof Artplayer
const canvas = constructor.utils.createElement('canvas')
@@ -130,3 +130,5 @@ export default function artplayerProxyCanvas(callback?: DrawCallback) {
return canvas
}
}
export default Object.assign(artplayerProxyCanvas, { default: artplayerProxyCanvas })
@@ -0,0 +1,4 @@
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
import artplayerProxyCanvas = require('./artplayer-proxy-canvas.js')
export = artplayerProxyCanvas
@@ -0,0 +1,2 @@
export { default } from './artplayer-proxy-canvas.js'
export type { Callable, Factory, MediaCanvas, Option, Result } from './artplayer-proxy-canvas.js'
@@ -1,9 +1,27 @@
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
import type Artplayer from 'artplayer'
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
declare namespace artplayerProxyCanvas {
/** Runs after drawing and bitmap release, before the draw event. */
type Option = (ctx: CanvasRenderingContext2D, video: HTMLVideoElement) => void
type Result = HTMLCanvasElement
/** Preserve the exact published return type and its assignability. */
type Result = HTMLCanvasElement
declare const artplayerProxyCanvas: (option?: Option) => (art: Artplayer) => Result
/** Opt-in view of supported enumerable media members; native Canvas members win. */
type MediaCanvas = HTMLCanvasElement & Pick<HTMLVideoElement, Exclude<keyof HTMLVideoElement, keyof HTMLCanvasElement>>
export default artplayerProxyCanvas
interface Callable {
/** Keep Parameters extraction optional, as published in 1.1.0. */
(option?: Option): (art: Artplayer) => Result
}
interface Factory extends Callable {
/** Same factory, for historical require(package).default calls. */
readonly default: Factory
}
}
declare const artplayerProxyCanvas: artplayerProxyCanvas.Factory
export = artplayerProxyCanvas
export as namespace artplayerProxyCanvas
@@ -0,0 +1,198 @@
{
"schemaVersion": 1,
"task": "PKG-CANVAS-04",
"status": "done",
"checks": [
{
"command": "node --test refactor/scripts/canvas-types.test.mjs",
"result": "pass",
"tests": 4,
"scope": "Exact published declarations, optional Parameters and exact Canvas result, five compiler modes with eleven negative checks, semantic editor globals and actual production exports"
},
{
"command": "yarn test:canvas-types-package",
"result": "pass with explicit historical diagnostics",
"cases": 17,
"candidateZeroDiagnosticCases": 7,
"historicalZeroDiagnosticCases": 9,
"historicalKnownFailureCases": 1,
"expectedHistoricalCodes": [
2349,
2349,
2322,
2344,
2344
],
"candidateInvalidUseRejectionsPerMode": 11,
"report": "refactor/.cache/canvas-package-types-NZAnqz/report.json",
"sha256": "05c8f0a0367e1478b59e839520e5c7572b3d5519df7323af21ead9e967524da8"
},
{
"command": "yarn build:ts && yarn build artplayer-proxy-canvas",
"result": "pass",
"scope": "Semantic editor declarations and real three-format outputs"
},
{
"command": "focused eslint --no-fix",
"result": "pass"
},
{
"command": "playwright test test/browser/canvas-lifecycle.spec.js",
"result": "pass",
"tests": 18,
"skipped": 0,
"browsers": [
{
"project": "chromium",
"version": "153.0.8010.12",
"platform": "win32"
},
{
"project": "firefox",
"version": "155.0",
"platform": "win32"
},
{
"project": "webkit",
"version": "26.6",
"platform": "win32"
}
],
"scope": "Candidate source with default alias, actual npm core5.4.0/candidate, native pixels/dimensions/resize/source/paused seek and terminal media/frame ownership",
"report": "refactor/.cache/canvas04-final-results/report.json",
"sha256": "af893ebcd22fa6312db1e02b222849135ca89568ecf196c88ce2947436cda0ae",
"resultsDirectory": "refactor/.cache/canvas04-final-results/results"
},
{
"command": "yarn ci:check",
"result": "pass",
"tests": {
"unit": 938,
"engineering": 14,
"baseline": 100,
"mainTotal": 1052,
"repeatedContractExecutions": 44
},
"strictProductionTypeScriptFiles": 284
}
],
"limitations": [
"Published 1.1.0 NodeNext ESM has five recorded diagnostics; expected historical failure is not zero-error compilation.",
"Full distribution/deep paths/demo and device gates remain tasks 05/06."
],
"sourceCommit": "2b319109cd191439d49d2cb701632c95ed04cff4",
"inputs": [
{
"file": "packages/artplayer-proxy-canvas/src/index.ts",
"sha256LF": "2124bfe63da80f7d0b25a2d0e397e13ca002dd74a4293a0ae023c3c3702a69ec"
},
{
"file": "packages/artplayer-proxy-canvas/package.json",
"sha256LF": "6c84b3b089dd341edaca26655289afcf834c5b4b67af8d56e0703155b004ae3a"
},
{
"file": "packages/artplayer-proxy-canvas/types/artplayer-proxy-canvas.d.ts",
"sha256LF": "85cdcf86ed12b383785f5303b41458d5d39117b83e74d9f7e99545e59ab4687b"
},
{
"file": "packages/artplayer-proxy-canvas/types/artplayer-proxy-canvas.d.mts",
"sha256LF": "fdff66b63ff24a93370e7f4e87ed6b19a1bd4a2305aa6db37da2c38af3415458"
},
{
"file": "packages/artplayer-proxy-canvas/types/artplayer-proxy-canvas.d.cts",
"sha256LF": "812d9c09a027834e45619dceb04f8b49876623771ad7ce570ab5920383679fea"
},
{
"file": "docs/assets/ts/artplayer-proxy-canvas.d.ts",
"sha256LF": "4f4163b4250bf001d7807c26e8e34e784eba70581997d43142febbd524a2a8f7"
},
{
"file": "test/types/canvas.ts",
"sha256LF": "ae59e12c59316c387bb41a443b65eae9ad747959e61e76c791356e8d68014d33"
},
{
"file": "refactor/fixtures/consumers/canvas-published.ts",
"sha256LF": "590578a232bd9b6716dc82e27d5b4d9ffd90943115a26ae6550f338daf40258d"
},
{
"file": "refactor/scripts/canvas-types.test.mjs",
"sha256LF": "df08de1af269201669dc93d5feb533f3c3cab1063bf94c98d1a3665e174e8fb1"
},
{
"file": "refactor/scripts/canvas-package-types.mjs",
"sha256LF": "71c4de28bc9595441ae2265fb4b45682acd637835315817310748aa4708885d7"
},
{
"file": "scripts/build-ts.js",
"sha256LF": "c9ece2c799cb81f7c35d4b5714c184c59a3a741379d3dcb970d8afbaaae98ad0"
},
{
"file": "scripts/typecheck.mjs",
"sha256LF": "583e4c855cb1541c7c2a9f812f91f32f5b5893c35db45cb457223c218250e2aa"
}
],
"artifacts": [
{
"file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.js",
"sha256LF": "a901e5c5fc00a64a6c2ae9d4b2b846950412710ec9672d30640d40987d4f9e73"
},
{
"file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.legacy.js",
"sha256LF": "052263fdd7ab657a0183d874a706c2cf82527233c5e60b58158caa0677718b97"
},
{
"file": "packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.mjs",
"sha256LF": "66ca7271f52545a8796f563f2d5ca04581bed8d60d218463923325acf5b73ccf"
},
{
"file": "docs/compiled/artplayer-proxy-canvas.js",
"sha256LF": "a901e5c5fc00a64a6c2ae9d4b2b846950412710ec9672d30640d40987d4f9e73"
},
{
"file": "docs/compiled/artplayer-proxy-canvas.legacy.js",
"sha256LF": "052263fdd7ab657a0183d874a706c2cf82527233c5e60b58158caa0677718b97"
},
{
"file": "docs/compiled/artplayer-proxy-canvas.mjs",
"sha256LF": "66ca7271f52545a8796f563f2d5ca04581bed8d60d218463923325acf5b73ccf"
}
],
"logs": [
{
"file": "refactor/.cache/canvas04-editor-build.log",
"sha256": "d6014f1ae611921f7d0fd47e1a27b5d5488f7ef6056731948edff8864b6653e9"
},
{
"file": "refactor/.cache/canvas04-build.log",
"sha256": "c9da09787163706b1f90adf55ff901b70963d7acc45df38e689789e0cea410b5"
},
{
"file": "refactor/.cache/canvas04-targeted.log",
"sha256": "a1d89c6c052c003d23d22b0b26a749eb34522b96191e49af31531052805b89ca"
},
{
"file": "refactor/.cache/canvas04-lint-final.log",
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
{
"file": "refactor/.cache/canvas04-installed-first.log",
"sha256": "589a52e4720ea68d6aeed8145ea0af8b88f7220a61dfd9909fc9cf1fb9ab4663"
},
{
"file": "refactor/.cache/canvas04-installed-verified.log",
"sha256": "e96728c91e3afe9431ab80addb09b2de45554720547c41a75ef6bbe402266e07"
},
{
"file": "refactor/.cache/canvas04-browser.log",
"sha256": "d4450a918655a45add2589c62326ce4b4f80373e3600b8cee8c10e9379e3df94"
},
{
"file": "refactor/.cache/canvas04-ci.log",
"sha256": "efc36adbaf3d23a8e90d66829afa01bf87fe8cc31eac927bbc5dc90f11093465"
}
],
"firstHistoricalFailure": {
"file": "refactor/.cache/canvas-package-types-qQ0exQ/published-1.1.0-5.9.3-nodenext-esm-diagnostics.json",
"scope": "Preserved exact five diagnostics before updating the expected historical failure count for the Canvas ReturnType consumer"
}
}
+17
View File
@@ -56,3 +56,20 @@ WebKit须验证intrinsic尺寸,不能仅alpha255;1px挂载曾令videoWidth/v
失败诊断与针对性回归见[03记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md)和
[03证据](baselines/canvas-lifecycle-validation.json)。04公开类型/导出和05设备、06
完整分发/demo仍需完成;本步的strict内部类型不替代安装消费者验收。
## 04 公开类型和分发入口
`node --test refactor/scripts/canvas-types.test.mjs`运行四组基线:两代真实声明、五种
编译器模式、11项非法用法、生成编辑器全局及三格式导出身份。Parameters保持可选
callback,Result精确保持HTMLCanvasElement;MediaCanvas只作显式类型视图,不能
用于收窄旧返回值或假设所有浏览器具备全部媒体API。不要复制Ambilight的必填末重载。
`yarn test:canvas-types-package`在仓库外安装两份实际发布及候选,校验文件字节和
非workspace链接,离线安装与frozen reinstall均须通过。17个编译场景里,候选7个
及历史9个应零诊断;另1个npm1.1.0 NodeNext ESM保留2349/2349/2322/2344/2344
五个旧诊断。候选每种模式移除@ts-expect-error后须拒绝11项非法用法。
公开namespace是编辑器声明源,不手改docs/assets/ts生成文件。.default与工厂
自身同一引用;根ESM/CJS及legacy类型入口各自测试。完整路径/内容/demo仍留06,
真实Safari/移动设备仍留05。见[04实施记录](changes/2026-09-12-PKG-CANVAS-04-types.md)
和[04证据](baselines/canvas-types-validation.json)。
@@ -0,0 +1,24 @@
# PKG-CANVAS-04 公开类型与导出兼容
公开namespace定义Option、Result、Callable、Factory及显式MediaCanvas视图。Callable
只有可选callback签名,保持1.1.0的Parameters包含undefined;Result仍精确等于
HTMLCanvasElement,不把返回值收窄为必须具备全部媒体属性的交叉类型。MediaCanvas
供显式使用转发成员的消费者断言,Canvas自有成员优先,不声称新增浏览器能力。
入口default自别名兼容旧require.default与直接可调用工厂;ESM仅default运行时导出。
新增.d.mts/.d.cts入口包装和import/require条件类型,typesVersions保留旧TS legacy路径。
公开namespace接入语义编辑器声明生成,README和包内架构同步说明用法与边界。
四组专项覆盖五种旧/新编译器模式、11项非法用法、两份真实发布声明、生成全局声明
以及实际三格式产物。1.0.0回调必填到1.1.0可选是既有发布差异,候选不恢复必填限制。
新旧ReturnType消费者仍可赋值普通Canvas;没有借用Ads的类型推导修正授权。
仓库外真实安装先复现npm1.1.0在TS5.9.3 NodeNext ESM的五个精确诊断:
2349/2349/2322/2344/2344。默认导入被当作CommonJS模块namespace而不可调用,
额外ReturnType断言也受影响;该历史失败单独保留,不能把历史全部场景称为零诊断。
候选安装消费者必须无诊断,且移除@ts-expect-error后拒绝全部11项非法用法。
结果和输入哈希见[验证证据](../baselines/canvas-types-validation.json)。05真实设备和
06完整分发/demo验收仍需完成,本步不发布npm、不修改版本、不推送。
最终四组专项、编辑器/三格式构建、仓库外17场景和18项真实浏览器通过;17场景保留1项历史五诊断,不能等同17项零错误。完整CI1052项通过(938单元+14工程+100基线),另44项重复契约观察;284个生产TS文件严格检查。CANVAS-TYPE-01及CANVAS-DIST-01具体风险关闭,本任务done并独立本地提交,05/06及整体发布验收继续。
@@ -0,0 +1,11 @@
import Artplayer from 'artplayer'
import canvas from 'artplayer-proxy-canvas'
const callback = (context: CanvasRenderingContext2D, video: HTMLVideoElement) => context.drawImage(video, 0, 0)
const art = new Artplayer({ container: '#player', url: 'video.mp4', proxy: canvas(callback) })
const result = canvas(callback)(art)
const actual: HTMLCanvasElement = result
const context: CanvasRenderingContext2D | null = result.getContext('2d')
const accepted: Parameters<typeof canvas>[0] = callback
const originalResult: ReturnType<ReturnType<typeof canvas>> = document.createElement('canvas')
void [actual, context, accepted, originalResult]
+3 -2
View File
@@ -4,7 +4,7 @@
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 221 项,范围 22 个包及工作区/示例。
状态:todo 123 / doing 6 / blocked 0 / done 92 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
状态:todo 122 / doing 6 / blocked 0 / done 93 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
@@ -321,7 +321,7 @@
| PKG-CANVAS-01 | artplayer-proxy-canvas<br>核对包契约与历史用法 | BASE-05 | callback、canvas 原方法、转发 media 属性/方法/事件 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done |
| PKG-CANVAS-02 | artplayer-proxy-canvas<br>建立特有行为与错误测试 | PKG-CANVAS-01, ENG-03, ENG-05 | ready/loadeddata/canplay、play/seek、回调、resize 与销毁 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | done |
| PKG-CANVAS-03 | artplayer-proxy-canvas<br>整理内部职责与资源 | PKG-CANVAS-02, CORE-06, CORE-11, CORE-16 | video adapter/原 canvas 方法/RAF 绘制/事件订阅分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | done |
| PKG-CANVAS-04 | artplayer-proxy-canvas<br>迁移自有源码和公开类型 | PKG-CANVAS-03, ENG-04, ENG-06, CORE-07 | canvas 与媒体能力的精确组合类型,保持 Result | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo |
| PKG-CANVAS-04 | artplayer-proxy-canvas<br>迁移自有源码和公开类型 | PKG-CANVAS-03, ENG-04, ENG-06, CORE-07 | canvas 与媒体能力的精确组合类型,保持 Result | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | done |
| PKG-CANVAS-05 | artplayer-proxy-canvas<br>验证新旧核心和组合 | PKG-CANVAS-04, CORE-22 | 真实 video/canvas 绘制、字幕和 document PiP 恢复 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo |
| PKG-CANVAS-06 | artplayer-proxy-canvas<br>验证分发并同步文档 | PKG-CANVAS-05, ENG-07 | canvas.js、三种产物、调用兼容与资源证据 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo |
@@ -509,3 +509,4 @@
- PKG-CANVAS-01: [记录](changes/2026-09-12-PKG-CANVAS-01-contract.md) [记录](baselines/canvas-contract.md) [记录](baselines/canvas-release.json) [记录](canvas-validation.md) [记录](baselines/canvas-contract-validation.json)
- PKG-CANVAS-02: [记录](changes/2026-09-12-PKG-CANVAS-02-tests.md) [记录](baselines/canvas-behavior-validation.json) [记录](canvas-validation.md)
- PKG-CANVAS-03: [记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md) [记录](baselines/canvas-lifecycle-validation.json) [记录](canvas-validation.md)
- PKG-CANVAS-04: [记录](changes/2026-09-12-PKG-CANVAS-04-types.md) [记录](baselines/canvas-types-validation.json) [记录](canvas-validation.md)
+11
View File
@@ -1,5 +1,16 @@
# 进度与证据
## 最新完成:Canvas-04 公开类型与两代导出
公开namespace保留可选Parameters和精确HTMLCanvasElement返回,新增显式MediaCanvas
视图;工厂.default自别名、ESM/CJS声明包装、旧TS legacy路径及语义编辑器生成同步。
仓库外安装17场景:候选7个/历史9个零诊断,另1个历史NodeNext ESM保留五个精确
诊断;候选每种模式拒绝11项非法用法。四组专项、18项真实浏览器及完整CI1052项
通过,另44项重复契约观察,284个生产TS文件严格检查。类型/两代导出具体风险关闭,
设备、Document PiP组合及完整demo/分发仍待05/06。当前221项:93 done、6 doing、
122 todo。见 [本步记录](changes/2026-09-12-PKG-CANVAS-04-types.md) 和
[验证证据](baselines/canvas-types-validation.json)。独立本地提交,无推送/发布。
## 最新完成:Canvas-03 严格TS职责与生命周期
生产源码拆为入口/媒体转发/底层video所有权/尺寸/绘制/调度六个严格TS模块,修复
+2 -2
View File
@@ -156,6 +156,6 @@
| AMBILIGHT-DIST-01 | resolved / 已复现 | Ambilight 1.0.0 CommonJS default namespace differs from callable 1.1.0 | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
| AMBILIGHT-PROXY-01 | resolved / 已复现 | Canvas proxy output pixels differ from forwarded video dimensions | PKG-AMBILIGHT-PROXY-01, PKG-AMBILIGHT-05 |
| CANVAS-LIFE-01 | resolved / 已复现 | Canvas proxy pending draw, bitmap and delayed subscriptions lack terminal ownership | PKG-CANVAS-02, PKG-CANVAS-03, PKG-CANVAS-05 |
| CANVAS-TYPE-01 | open / 源码/产物事实 | Canvas proxy declarations change required callback across generations and omit forwarded media capabilities | PKG-CANVAS-04, PKG-CANVAS-06 |
| CANVAS-DIST-01 | open / 已复现 | Canvas proxy published CommonJS namespace differs from current callable export | PKG-CANVAS-04, PKG-CANVAS-06 |
| CANVAS-TYPE-01 | resolved / 源码/产物事实 | Canvas proxy declarations change required callback across generations and omit forwarded media capabilities | PKG-CANVAS-04, PKG-CANVAS-06 |
| CANVAS-DIST-01 | resolved / 已复现 | Canvas proxy published CommonJS namespace differs from current callable export | PKG-CANVAS-04, PKG-CANVAS-06 |
| CANVAS-PIXEL-01 | resolved / 已复现 | Historical Canvas proxy produces transparent frames with detached native video in tested WebKit | PKG-CANVAS-03, PKG-CANVAS-05 |
+16 -4
View File
@@ -3376,12 +3376,18 @@
],
"compatibleResolution": "Keep optional current callback inference and actual canvas return while validating historical consumers and any additive media surface types.",
"closureCriteria": "Strict old/current compiler consumers and installed callback/result/import variants verified without new inference narrowing.",
"status": "open",
"status": "resolved",
"evidence": [
"refactor/baselines/canvas-contract.md",
"refactor/baselines/canvas-release.json",
"refactor/changes/2026-09-12-PKG-CANVAS-01-contract.md"
]
],
"resolutionEvidence": [
"refactor/changes/2026-09-12-PKG-CANVAS-04-types.md",
"refactor/baselines/canvas-types-validation.json"
],
"resolutionRationale": "Packed candidate compiles in seven old/current compiler configurations; Parameters stays optional and ReturnType stays exactly HTMLCanvasElement, including assigning an ordinary Canvas. Additive MediaCanvas is explicit opt-in. Eleven invalid uses are rejected in every candidate mode; exact archived declarations retain the pre-existing 1.0.0 required to 1.1.0 optional distinction. Candidate also corrects the recorded published 1.1.0 NodeNext ESM failure.",
"workspaceState": "Packed candidate compiles in seven old/current compiler configurations; Parameters stays optional and ReturnType stays exactly HTMLCanvasElement, including assigning an ordinary Canvas. Additive MediaCanvas is explicit opt-in. Eleven invalid uses are rejected in every candidate mode; exact archived declarations retain the pre-existing 1.0.0 required to 1.1.0 optional distinction. Candidate also corrects the recorded published 1.1.0 NodeNext ESM failure."
},
{
"id": "CANVAS-DIST-01",
@@ -3393,12 +3399,18 @@
],
"compatibleResolution": "Preserve both require.default and callable factory with actual global, legacy and ESM paths.",
"closureCriteria": "Packed runtime/type and real local demo/editor consumers pass.",
"status": "open",
"status": "resolved",
"evidence": [
"refactor/baselines/canvas-contract.md",
"refactor/baselines/canvas-release.json",
"refactor/changes/2026-09-12-PKG-CANVAS-01-contract.md"
]
],
"resolutionEvidence": [
"refactor/changes/2026-09-12-PKG-CANVAS-04-types.md",
"refactor/baselines/canvas-types-validation.json"
],
"resolutionRationale": "Production main/legacy/browser global and ESM plus real isolated installed root/legacy imports retain callable factory and self default identities. Seven installed type consumer modes validate the correct declaration wrapper including no-interop. Full deep paths/content/demo and device checks remain tasks 05/06.",
"workspaceState": "Production main/legacy/browser global and ESM plus real isolated installed root/legacy imports retain callable factory and self default identities. Seven installed type consumer modes validate the correct declaration wrapper including no-interop. Full deep paths/content/demo and device checks remain tasks 05/06."
},
{
"id": "CANVAS-PIXEL-01",
+104
View File
@@ -0,0 +1,104 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import ts from 'typescript'
import compat from 'typescript-compat'
import { checkFiles, packedFiles } from '../../scripts/package-check.mjs'
import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs'
import { verifyCanvasContract } from './canvas-contract.mjs'
import { hash, readMember } from './releases.mjs'
async function main() {
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:canvas-types-package')
const yarn = process.env.npm_execpath
assert(yarn && fs.existsSync(yarn))
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/canvas-package-types-'))
console.log(`Canvas isolated package evidence: ${output}`)
const contract = await verifyCanvasContract()
const packages = []
for (const name of ['artplayer', 'artplayer-proxy-canvas']) {
const archive = path.join(output, `${name}.tgz`)
fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name)))
const manifest = JSON.parse(readMember(archive, 'package/package.json'))
const files = packedFiles(archive)
checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : [])
packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) })
}
const matrix = []
const core = packages[0]
const candidates = [
...[contract.baseline.previous[0], contract.baseline.release].map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })),
{ ...packages[1], label: 'candidate' },
]
for (const plugin of candidates) {
const consumer = consumerDirectory()
try {
writeJson(path.join(consumer, 'package.json'), { name: 'canvas-isolated-consumer', private: true, dependencies: Object.fromEntries([core, plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) })
fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock'))
fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer))
const lock = fs.readFileSync(path.join(consumer, 'yarn.lock'))
fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer))
assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock)
fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock)
for (const pkg of [core, plugin]) {
const root = path.join(consumer, 'node_modules', pkg.name)
assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link')
for (const [member, expected] of Object.entries(pkg.files))
assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`)
}
const runtimeFile = path.join(consumer, 'exports.cjs')
fs.writeFileSync(runtimeFile, plugin.label === 'candidate'
? `const assert = require('node:assert/strict');
for (const name of ['artplayer-proxy-canvas', 'artplayer-proxy-canvas/legacy']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
import('artplayer-proxy-canvas').then(module => { assert.deepEqual(Object.keys(module), ['default']); assert.equal(module.default.default, module.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
: `const assert = require('node:assert/strict'); const value = require('artplayer-proxy-canvas'); assert.equal(typeof value, '${plugin.version === '1.0.0' ? 'object' : 'function'}'); assert.equal(typeof (value.default || value)(() => {}), 'function');`)
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
if (plugin.label === 'candidate')
modes.push([ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop'])
for (const [compiler, mode] of modes) {
const next = mode.startsWith('nodenext')
const extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/canvas.ts' : 'refactor/fixtures/consumers/canvas-published.ts'), 'utf8')
const filename = path.join(consumer, `consumer.${extension}`)
const historical = path.join(consumer, `historical.${extension}`)
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/canvas-published.ts'), historical)
const files = plugin.label === 'candidate' ? [filename, historical] : [filename]
if (mode === 'nodenext-cjs' && plugin.label !== 'published-1.1.0') {
const commonjs = path.join(consumer, 'commonjs.cts')
fs.writeFileSync(commonjs, `import canvas = require('artplayer-proxy-canvas'); canvas((ctx, video) => ctx.drawImage(video, 0, 0)); ${plugin.label === 'candidate' ? 'canvas.default();' : ''}`)
files.push(commonjs)
}
const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs }
function compile(code) {
fs.writeFileSync(filename, code)
const program = compiler.createProgram(files, options)
for (const file of program.getSourceFiles()) {
const actual = fs.realpathSync(file.fileName)
assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`)
}
return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
}
const diagnostics = compile(source)
const historicalFailure = plugin.label === 'published-1.1.0' && mode === 'nodenext-esm'
writeJson(path.join(output, `${plugin.label}-${compiler.version}-${mode}-diagnostics.json`), { diagnostics, historicalFailure })
assert.deepEqual(diagnostics.map(item => item.code), historicalFailure ? [2349, 2349, 2322, 2344, 2344] : [], `${plugin.label} ${compiler.version} ${mode}`)
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
if (plugin.label === 'candidate')
assert.equal(invalid.length, 11, 'Installed declarations must reject all invalid uses')
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
}
}
finally {
removeConsumer(consumer)
}
}
writeJson(path.join(output, 'report.json'), { suite: 'canvas-isolated-package-types', introducedBy: 'PKG-CANVAS-04', scope: 'Two actual published proxies and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes, compiler/default/import= contracts. Published 1.0.0 required callback became optional in 1.1.0; candidate retains optional Parameters and exact HTMLCanvasElement return inference. This is not native proxy media, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
console.log(`Canvas installed types passed: ${matrix.length} cases; ${output}`)
}
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
+91
View File
@@ -0,0 +1,91 @@
import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
// eslint-disable-next-line test/no-import-node-test -- Node baseline runner.
import test from 'node:test'
import vm from 'node:vm'
import { ESLint } from 'eslint'
import ts from 'typescript'
import compat from 'typescript-compat'
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
import { checkConsumer } from '../../scripts/typecheck.mjs'
import { verifyCanvasContract } from './canvas-contract.mjs'
import { readMember } from './releases.mjs'
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
test('Canvas current types preserve optional Parameters and exact Canvas result, while rejecting eleven invalid uses', () => {
const source = fs.readFileSync('test/types/canvas.ts', 'utf8')
for (const [compiler, mode] of modes) {
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 11)
}
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import canvas = require('artplayer-proxy-canvas'); import legacy = require('artplayer-proxy-canvas/legacy');
const callback: canvas.Option = (ctx, video) => ctx.drawImage(video, 0, 0); canvas(callback); canvas.default(); legacy(); legacy.default(callback);`), [])
})
test('Canvas published declarations distinguish required 1.0.0 and optional 1.1.0 callbacks without changing Canvas return assignability', async () => {
const contract = await verifyCanvasContract()
const source = fs.readFileSync('refactor/fixtures/consumers/canvas-published.ts', 'utf8')
const optional = `const omitted: Parameters<typeof canvas>[0] = undefined; canvas(); void omitted;`
for (const version of ['1.0.0', '1.1.0']) {
const old = readMember(contract.archives.get(version), 'package/types/artplayer-proxy-canvas.d.ts').toString()
for (const compiler of [ts, compat]) {
const filename = path.resolve('test/types/canvas-history.ts')
const declaration = path.resolve('test/types/canvas-history.d.ts')
function historical(code) {
const host = compiler.createCompilerHost({})
const original = host.getSourceFile.bind(host)
host.getSourceFile = (file, language, ...rest) => path.resolve(file) === filename
? compiler.createSourceFile(file, code.replace('from \'artplayer-proxy-canvas\'', 'from \'./canvas-history\''), language, true)
: path.resolve(file) === declaration ? compiler.createSourceFile(file, old, language, true) : original(file, language, ...rest)
const exists = host.fileExists.bind(host)
host.fileExists = file => path.resolve(file) === declaration || exists(file)
const program = compiler.createProgram([filename, declaration], { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, module: compiler.ModuleKind.CommonJS, moduleResolution: compiler.ModuleResolutionKind.NodeJs, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'] }, host)
assert(program.getSourceFile(declaration))
return compiler.getPreEmitDiagnostics(program).map(item => item.code)
}
assert.deepEqual(historical(source), [])
assert.deepEqual(historical(`${source}\n${optional}`), version === '1.0.0' ? [2322, 2554] : [])
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', `${source}\n${optional}`), [])
}
}
})
test('Canvas editor globals are generated from public types with optional callback, media view and negative checking', async () => {
const source = fs.readFileSync('packages/artplayer-proxy-canvas/types/artplayer-proxy-canvas.d.ts', 'utf8')
const generated = generatePluginEditorDeclaration(source, 'artplayerProxyCanvas')
const file = 'docs/assets/ts/artplayer-proxy-canvas.d.ts'
const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(generated, { filePath: file })
assert.equal(formatted.errorCount, 0)
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
const consumer = `artplayerProxyCanvas(); artplayerProxyCanvas.default((ctx, video) => ctx.drawImage(video, 0, 0));
const result: artplayerProxyCanvas.Result = document.createElement('canvas');
const media = result as artplayerProxyCanvas.MediaCanvas; media.play();
// @ts-expect-error Callback must be a function in public types.
artplayerProxyCanvas(42);`
for (const compiler of [ts, compat]) {
assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [])
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 1)
}
})
test('Canvas production main, legacy, browser global and ESM preserve both callable and historical default identities', async () => {
for (const suffix of ['js', 'legacy.js']) {
const source = fs.readFileSync(`packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.${suffix}`, 'utf8')
const module = { exports: {} }
vm.runInNewContext(source, { module, exports: module.exports, window: {} })
assert.equal(typeof module.exports, 'function')
assert.equal(module.exports.default, module.exports)
assert.equal(typeof module.exports.default(), 'function')
const global = { window: {} }
vm.runInNewContext(source, global)
const factory = global.artplayerProxyCanvas || global.window.artplayerProxyCanvas
assert.equal(factory.default, factory)
}
const source = fs.readFileSync('packages/artplayer-proxy-canvas/dist/artplayer-proxy-canvas.mjs', 'utf8')
const module = await import(`data:text/javascript,${encodeURIComponent(source)}`)
assert.deepEqual(Object.keys(module), ['default'])
assert.equal(module.default.default, module.default)
})
+6 -2
View File
@@ -3314,11 +3314,15 @@
"ENG-06",
"CORE-07"
],
"status": "todo",
"status": "done",
"risk": "H",
"deliverable": "canvas 与媒体能力的精确组合类型,保持 Result",
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
"evidence": []
"evidence": [
"changes/2026-09-12-PKG-CANVAS-04-types.md",
"baselines/canvas-types-validation.json",
"canvas-validation.md"
]
},
{
"id": "PKG-CANVAS-05",
+1 -1
View File
@@ -43,7 +43,7 @@ console.log(`✨ Built ${artplayerTSoutput}`);
const type = pluginsTS[index]
const { name, file } = parsePluginInfo(type)
const source = String(fs.readFileSync(type))
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight'].includes(name)
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas'].includes(name)
const code = semanticPlugin
? generatePluginEditorDeclaration(source, name)
: `${source.replace(reg, '')}\nexport = ${name};\nexport as namespace ${name};\n`
+1 -1
View File
@@ -76,7 +76,7 @@ export function runTypechecks() {
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'test/types/ads.ts', 'refactor/fixtures/consumers/ads-published.ts', 'refactor/fixtures/consumers/ads-workspace.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
}
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts'])
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts', 'test/types/canvas.ts', 'refactor/fixtures/consumers/canvas-published.ts'])
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
if (mode === 'nodenext-cjs')
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/commonjs.cts'), 'utf8')), [], 'CommonJS export assignment consumer failed')
+59
View File
@@ -0,0 +1,59 @@
import type { Factory, MediaCanvas, Option, Result } from 'artplayer-proxy-canvas'
import Artplayer from 'artplayer'
import canvas from 'artplayer-proxy-canvas'
import legacy from 'artplayer-proxy-canvas/legacy'
type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false
type Assert<T extends true> = T
type OptionalInput = Assert<Equal<Parameters<typeof canvas>, [option?: Option]>>
type ExactResult = Assert<Equal<ReturnType<ReturnType<typeof canvas>>, HTMLCanvasElement>>
const option: Option = (context, video) => {
context.drawImage(video, 0, 0)
const source: HTMLVideoElement = video
const ctx: CanvasRenderingContext2D = context
void [source, ctx]
}
const art = new Artplayer({ container: '#player', url: 'video.mp4', proxy: canvas(option) })
canvas()
canvas(undefined)
canvas.default(option)
legacy.default()
declare const optional: Option | undefined
canvas(optional)
const missing: Parameters<typeof canvas>[0] = undefined
const factory: Factory = canvas
const registration: (art: Artplayer) => HTMLCanvasElement = factory()
const result: Result = registration(art)
const originalResult: Result = document.createElement('canvas')
const media = result as MediaCanvas
const play: Promise<void> = media.play()
media.pause()
media.src = 'replacement.mp4'
const context: CanvasRenderingContext2D | null = media.getContext('2d')
const listen: HTMLCanvasElement['addEventListener'] = media.addEventListener
const width: number = media.width
// @ts-expect-error Callback remains a function.
canvas(42)
// @ts-expect-error Strings are not typed callbacks.
canvas('callback')
// @ts-expect-error Historical runtime tolerates null, but published types never accepted it.
canvas(null)
// @ts-expect-error The callback receives a 2D context.
canvas((ctx: WebGLRenderingContext) => void ctx)
// @ts-expect-error The callback receives video, not canvas.
canvas((ctx: CanvasRenderingContext2D, video: HTMLCanvasElement) => void [ctx, video])
// @ts-expect-error Canvas return must not be relabeled as a native video.
const video: HTMLVideoElement = result
// @ts-expect-error Registration remains synchronous.
const promise: Promise<HTMLCanvasElement> = canvas()(art)
// @ts-expect-error 2D contexts have no media play method.
media.getContext('2d')?.play()
// @ts-expect-error Native Canvas dimensions remain numeric.
media.width = '320'
// @ts-expect-error Self default identity is readonly in declarations.
canvas.default = legacy
// @ts-expect-error Registration requires a host.
canvas()()
void [missing, originalResult, play, context, listen, width, video, promise]
export type { ExactResult, OptionalInput }