@webav/av-canvas
Version:
Combine Text, Image, Video, Audio, UserMedia, DisplayMedia to generate MediaStream. With [AVRcorder](../av-recorder/README.md) you can output MP4 streams and save them as local files or push them to the server.
39 lines (38 loc) • 15.9 kB
JavaScript
(function(w,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("@webav/av-cliper"),require("@webav/internal-utils")):typeof define=="function"&&define.amd?define(["exports","@webav/av-cliper","@webav/internal-utils"],l):(w=typeof globalThis<"u"?globalThis:w||self,l(w["av-canvas"]={},w.avCliper,w.internalUtils))})(this,function(w,l,y){"use strict";var Bt=Object.defineProperty;var xt=w=>{throw TypeError(w)};var Ht=(w,l,y)=>l in w?Bt(w,l,{enumerable:!0,configurable:!0,writable:!0,value:y}):w[l]=y;var et=(w,l,y)=>Ht(w,typeof l!="symbol"?l+"":l,y),lt=(w,l,y)=>l.has(w)||xt("Cannot "+y);var i=(w,l,y)=>(lt(w,l,"read from private field"),y?y.call(w):l.get(w)),x=(w,l,y)=>l.has(w)?xt("Cannot add the same private member more than once"):l instanceof WeakSet?l.add(w):l.set(w,y),A=(w,l,y,Y)=>(lt(w,l,"write to private field"),Y?Y.call(w,y):l.set(w,y),y),D=(w,l,y)=>(lt(w,l,"access private method"),y);var z,B,H,q,g,b,X,J,Q,U,I,Z,$,L,rt,ot,C,P,W,bt,E,K;const Y=["t","b","l","r","lt","lb","rt","rb","rotate"];function N(r){return document.createElement(r)}const st=new WeakMap;function ft(r,t){if(st.has(r))return st.get(r)(t);let e=10;new ResizeObserver(s=>{const h=s[0];h!=null&&(e=10/(h.contentRect.width/r.width))}).observe(r);function o(s){const{w:h,h:d}=s,a=e,c=a/2,f=h/2,u=d/2,p=a*1.5,m=p/2;return{...s.fixedAspectRatio?{}:{t:new l.Rect(-c,-u-c,a,a,s),b:new l.Rect(-c,u-c,a,a,s),l:new l.Rect(-f-c,-c,a,a,s),r:new l.Rect(f-c,-c,a,a,s)},lt:new l.Rect(-f-c,-u-c,a,a,s),lb:new l.Rect(-f-c,u-c,a,a,s),rt:new l.Rect(f-c,-u-c,a,a,s),rb:new l.Rect(f-c,u-c,a,a,s),rotate:new l.Rect(-m,-u-a*2-m,p,p,s)}}return st.set(r,o),o(t)}const at=new WeakMap;function _(r){if(at.has(r))return at.get(r);const t={w:r.clientWidth/r.width,h:r.clientHeight/r.height};return new ResizeObserver(()=>{t.w=r.clientWidth/r.width,t.h=r.clientHeight/r.height}).observe(r),at.set(r,t),t}var G=(r=>(r.ActiveSpriteChange="activeSpriteChange",r.AddSprite="addSprite",r))(G||{});class vt{constructor(){x(this,z,[]);x(this,B,null);x(this,H,new y.EventTool);et(this,"on",i(this,H).on);x(this,q,0)}get activeSprite(){return i(this,B)}set activeSprite(t){t!==i(this,B)&&(A(this,B,t),i(this,H).emit("activeSpriteChange",t))}activeSpriteByCoord(t,e){this.activeSprite=this.getSprites().reverse().find(n=>n.visible&&n.rect.checkHit(t,e))??null}async addSprite(t){await t.ready,i(this,z).push(t),A(this,z,i(this,z).sort((e,n)=>e.zIndex-n.zIndex)),t.on("propsChange",e=>{e.zIndex!=null&&A(this,z,i(this,z).sort((n,o)=>n.zIndex-o.zIndex))}),i(this,H).emit("addSprite",t)}removeSprite(t){i(this,B)===t&&(this.activeSprite=null),A(this,z,i(this,z).filter(e=>e!==t)),t.destroy()}getSprites(t={time:!0}){return i(this,z).filter(e=>e.visible&&(t.time?i(this,q)>=e.time.offset&&i(this,q)<=e.time.offset+e.time.duration:!0))}updateRenderTime(t){A(this,q,t);const e=this.activeSprite;e!=null&&(t<e.time.offset||t>e.time.offset+e.time.duration)&&(this.activeSprite=null)}destroy(){i(this,H).destroy(),i(this,z).forEach(t=>t.destroy()),A(this,z,[])}}z=new WeakMap,B=new WeakMap,H=new WeakMap,q=new WeakMap;function gt(r,t,e){const n=_(t),o=new ResizeObserver(()=>{e.activeSprite!=null&&ct(e.activeSprite,t,h,d)});o.observe(t);let s=()=>{};const{rectEl:h,ctrlsEl:d}=Ct(r);h.addEventListener("pointerdown",c=>{if(Object.values(d).includes(c.target))return;const f=t.getBoundingClientRect(),u=(c.clientX-f.left)/n.w,p=(c.clientY-f.top)/n.h;e.activeSpriteByCoord(u,p)});const a=e.on(G.ActiveSpriteChange,c=>{if(s(),c==null){h.style.display="none";return}ct(c,t,h,d),s=c.on("propsChange",()=>{ct(c,t,h,d)}),h.style.display=""});return()=>{o.disconnect(),a(),h.remove(),s()}}function Ct(r){const t=N("div");t.classList.add("sprite-rect"),t.style.cssText=`
position: absolute;
z-index: 3;
pointer-events: auto;
border: 1px solid #eee;
box-sizing: border-box;
display: none;
cursor: move;
`;const e=Object.fromEntries(Y.map(n=>{const o=N("div");return o.classList.add(`ctrl-key-${n}`),o.style.cssText=`
display: none;
position: absolute;
border: 1px solid #3ee; border-radius: 50%;
box-sizing: border-box;
background-color: #fff;
pointer-events: auto;
cursor: ${n==="rotate"?"crosshair":"default"};
user-select: none;
`,[n,o]}));return Object.values(e).forEach(n=>t.appendChild(n)),r.appendChild(t),{rectEl:t,ctrlsEl:e}}function ct(r,t,e,n){const o=_(t),{x:s,y:h,w:d,h:a,angle:c}=r.rect;Object.assign(e.style,{left:`${s*o.w}px`,top:`${h*o.h}px`,width:`${d*o.w}px`,height:`${a*o.h}px`,rotate:`${c}rad`}),Object.entries(ft(t,r.rect)).forEach(([f,{x:u,y:p,w:m,h:S}])=>{Object.assign(n[f].style,{display:"block",left:"50%",top:"50%",width:`${m*o.w}px`,height:`${S*o.h}px`,transform:`translate(${u*o.w}px, ${p*o.h}px)`})})}function Mt(r,t){const e=n=>{if(n.button!==0||n.target!==r)return;const o=_(r),{offsetX:s,offsetY:h}=n,d=s/o.w,a=h/o.h;t.activeSpriteByCoord(d,a)};return r.addEventListener("pointerdown",e),()=>{r.removeEventListener("pointerdown",e)}}function Rt(r,t,e){let n=0,o=0,s=null;const h=kt(r,e),d=e.querySelector(".sprite-rect");if(!d)throw Error("sprite-rect DOM Node not found");const a=m=>{if(m.button!==0||t.activeSprite==null)return;const S=t.activeSprite,{clientX:M,clientY:O}=m;s=S.rect.clone(),h.magneticEffect(S.rect.x,S.rect.y,S.rect),n=M,o=O,window.addEventListener("pointermove",f),window.addEventListener("pointerup",u),m.stopPropagation()},c=_(r),f=m=>{if(t.activeSprite==null||s==null)return;const{clientX:S,clientY:M}=m;let O=s.x+(S-n)/c.w,v=s.y+(M-o)/c.h;ut(t.activeSprite.rect,r,h.magneticEffect(O,v,t.activeSprite.rect))},u=()=>{h.hide(),window.removeEventListener("pointermove",f),window.removeEventListener("pointerup",u)};d.addEventListener("pointerdown",a),r.addEventListener("pointerdown",a);const p=Tt(r,d,t);return()=>{h.destroy(),u(),d.removeEventListener("pointerdown",a),r.removeEventListener("pointerdown",a),p()}}function Tt(r,t,e){const n=Array.from(t.children),o=_(r);n.forEach((c,f)=>{const u=Y[f];c.addEventListener("pointerdown",p=>{if(p.button!==0||e.activeSprite==null)return;const{clientX:m,clientY:S}=p;u==="rotate"?Et(e.activeSprite.rect,Ot(e.activeSprite.rect.center,o,r)):At({sprRect:e.activeSprite.rect,ctrlKey:u,startX:m,startY:S,cvsRatio:o,cvsEl:r}),p.stopPropagation()})}),n[Y.indexOf("rotate")].style.cursor="crosshair";const s=["ns-resize","nesw-resize","ew-resize","nwse-resize","ns-resize","nesw-resize","ew-resize","nwse-resize"],h={t:0,rt:1,r:2,rb:3,b:4,lb:5,l:6,lt:7};let d=()=>{};const a=e.on(G.ActiveSpriteChange,c=>{if(d(),c==null)return;const f=y.debounce(function(){const{angle:u}=c.rect,p=u<0?u+2*Math.PI:u;n.forEach((m,S)=>{const M=Y[S];if(M==="rotate")return;const O=(h[M]+Math.floor((p+Math.PI/8)/(Math.PI/4)))%8;m.style.cursor=s[O]})},300);d=c.on("propsChange",u=>{var p;((p=u.rect)==null?void 0:p.angle)!=null&&f()}),f()});return()=>{d(),a()}}function At({sprRect:r,startX:t,startY:e,ctrlKey:n,cvsRatio:o,cvsEl:s}){const h=r.clone(),d=c=>{const{clientX:f,clientY:u}=c,p=(f-t)/o.w,m=(u-e)/o.h,S=n.length===1?zt:Lt,{x:M,y:O,w:v,h:R}=h,tt=Math.atan2(R,v),{incW:pt,incH:wt,incS:V,rotateAngle:mt}=S({deltaX:p,deltaY:m,angle:r.angle,ctrlKey:n,diagonalAngle:tt}),T=10;let k=v,F=R,nt=h.fixedScaleCenter?pt*2:pt,it=h.fixedScaleCenter?wt*2:wt,j=V;const yt=Math.sqrt(R**2+v**2),St=Math.sqrt((T*(R/v))**2+T**2);switch(n){case"l":k=Math.max(v+nt,T),j=Math.min(V,v-T);break;case"r":k=Math.max(v+nt,T),j=Math.max(V,T-v);break;case"b":F=Math.max(R+it,T),j=Math.min(V,R-T);break;case"t":F=Math.max(R+it,T),j=Math.max(V,T-R);break;case"lt":case"lb":k=Math.max(v+nt,T),F=k===T?R/v*k:R+it,j=Math.min(V,yt-St);break;case"rt":case"rb":k=Math.max(v+nt,T),F=k===T?R/v*k:R+it,j=Math.max(V,St-yt);break}let ht=M,dt=O;if(h.fixedScaleCenter)ht=M+v/2-k/2,dt=O+R/2-F/2;else{const Dt=j/2*Math.cos(mt)+M+v/2,Yt=j/2*Math.sin(mt)+O+R/2;ht=Dt-k/2,dt=Yt-F/2}ut(r,s,{x:ht,y:dt,w:k,h:F})},a=()=>{window.removeEventListener("pointermove",d),window.removeEventListener("pointerup",a)};window.addEventListener("pointermove",d),window.addEventListener("pointerup",a)}function zt({deltaX:r,deltaY:t,angle:e,ctrlKey:n}){let o=0,s=0,h=0,d=e;return n==="l"||n==="r"?(o=r*Math.cos(e)+t*Math.sin(e),s=o*(n==="l"?-1:1)):(n==="t"||n==="b")&&(d=e-Math.PI/2,o=r*Math.cos(d)+t*Math.sin(d),h=o*(n==="b"?-1:1)),{incW:s,incH:h,incS:o,rotateAngle:d}}function Lt({deltaX:r,deltaY:t,angle:e,ctrlKey:n,diagonalAngle:o}){const s=(n==="lt"||n==="rb"?1:-1)*o+e,h=r*Math.cos(s)+t*Math.sin(s),d=n==="lt"||n==="lb"?-1:1,a=h*Math.cos(o)*d,c=h*Math.sin(o)*d;return{incW:a,incH:c,incS:h,rotateAngle:s}}function Et(r,t){const e=({clientX:o,clientY:s})=>{const h=o-t.x,d=s-t.y,a=Math.atan2(d,h)+Math.PI/2;r.angle=a},n=()=>{window.removeEventListener("pointermove",e),window.removeEventListener("pointerup",n)};window.addEventListener("pointermove",e),window.addEventListener("pointerup",n)}function Ot(r,t,e){const n=r.x*t.w,o=r.y*t.h,{left:s,top:h}=e.getBoundingClientRect();return{x:n+s,y:o+h}}function ut(r,t,e){const n={x:r.x,y:r.y,w:r.w,h:r.h,...e},o=t.width*.05,s=t.height*.05;n.x<-n.w+o?n.x=-n.w+o:n.x>t.width-o&&(n.x=t.width-o),n.y<-n.h+s?n.y=-n.h+s:n.y>t.height-s&&(n.y=t.height-s),r.x=n.x,r.y=n.y,r.w=n.w,r.h=n.h}function kt(r,t){const e="display: none; position: absolute;",n={w:0,h:0,x:0,y:0},o={vertMiddle:{...n,h:100,x:50,ref:{prop:"x",val:({w:a})=>(r.width-a)/2}},horMiddle:{...n,w:100,y:50,ref:{prop:"y",val:({h:a})=>(r.height-a)/2}},top:{...n,w:100,ref:{prop:"y",val:()=>0}},bottom:{...n,w:100,y:100,ref:{prop:"y",val:({h:a})=>r.height-a}},left:{...n,h:100,ref:{prop:"x",val:()=>0}},right:{...n,h:100,x:100,ref:{prop:"x",val:({w:a})=>r.width-a}}},s=N("div");s.style.cssText=`
position: absolute;
z-index: 4;
top: 0; left: 0;
width: 100%; height: 100%;
pointer-events: none;
box-sizing: border-box;
`;const h=Object.fromEntries(Object.entries(o).map(([a,{w:c,h:f,x:u,y:p}])=>{const m=N("div");return m.style.cssText=`
${e}
border-${c>0?"top":"left"}: 1px solid #3ee;
top: ${p}%; left: ${u}%;
${u===100?"margin-left: -1px":""};
${p===100?"margin-top: -1px":""};
width: ${c}%; height: ${f}%;
`,s.appendChild(m),[a,m]}));t.appendChild(s);const d=6/(900/r.width);return{magneticEffect(a,c,f){const u={x:a,y:c},p={x:d,y:d},m={x:"",y:""};Object.values(h).forEach(S=>S.style.display="none");for(const S in o){const{prop:M,val:O}=o[S].ref,v=O(f),tt=Math.abs((M==="x"?a:c)-v);tt<=d&&tt<p[M]&&(p[M]=tt,u[M]=v,m[M]=S)}return m.x&&(h[m.x].style.display="block"),m.y&&(h[m.y].style.display="block"),u},hide(){Object.values(h).forEach(a=>a.style.display="none")},destroy(){s.remove()}}}const It={sampleRate:48e3,channelCount:2,codec:"mp4a.40.2"};function $t(r){const t=N("canvas");return t.style.cssText=`
width: 100%;
height: 100%;
display: block;
touch-action: none;
`,t.width=r.width,t.height=r.height,t}class Pt{constructor(t,e){x(this,L);x(this,g);x(this,b);x(this,X);x(this,J,!1);x(this,Q,[]);x(this,U);x(this,I,new y.EventTool);et(this,"on",i(this,I).on);x(this,Z);x(this,$,0);x(this,C,new AudioContext);x(this,P,i(this,C).createMediaStreamDestination());x(this,W,new Set);x(this,E,{start:0,end:0,step:0,audioPlayAt:0});x(this,K,new WeakMap);et(this,"addSprite",async t=>{i(this,C).state==="suspended"&&i(this,C).resume().catch(l.Log.error);const e=t.getClip();if(e instanceof l.MediaStreamClip&&e.audioTrack!=null){const n=i(this,C).createMediaStreamSource(new MediaStream([e.audioTrack]));n.connect(i(this,P)),i(this,K).set(t,n)}await i(this,b).addSprite(t),t.preFrame(0)});et(this,"removeSprite",t=>{var e;(e=i(this,K).get(t))==null||e.disconnect(),i(this,b).removeSprite(t)});A(this,Z,e),A(this,g,$t(e));const n=i(this,g).getContext("2d",{alpha:!1});if(n==null)throw Error("canvas context is null");A(this,X,n);const o=N("div");o.style.cssText="width: 100%; height: 100%; position: relative;",o.appendChild(i(this,g)),t.appendChild(o),jt(i(this,C)).connect(i(this,P)),ft(i(this,g),{x:0,y:0,w:0,h:0}),A(this,b,new vt),i(this,Q).push(Mt(i(this,g),i(this,b)),gt(o,i(this,g),i(this,b)),Rt(i(this,g),i(this,b),o),i(this,b).on(G.AddSprite,c=>{const{rect:f}=c;f.x===0&&f.y===0&&(f.x=(i(this,g).width-f.w)/2,f.y=(i(this,g).height-f.h)/2)}),y.EventTool.forwardEvent(i(this,b),i(this,I),[G.ActiveSpriteChange]));let s=i(this,$),h=performance.now(),d=0;const a=1e3/30;A(this,U,y.workerTimer(()=>{(performance.now()-h)/(a*d)<1||(d+=1,i(this,X).fillStyle=e.bgColor,i(this,X).fillRect(0,0,i(this,g).width,i(this,g).height),D(this,L,bt).call(this),s!==i(this,$)&&(s=i(this,$),i(this,I).emit("timeupdate",Math.round(s))))},a))}play(t){const e=i(this,b).getSprites({time:!1}).map(o=>o.time.offset+o.time.duration),n=t.end??(e.length>0?Math.max(...e):1/0);if(t.start>=n||t.start<0)throw Error(`Invalid time parameter, ${JSON.stringify({start:t.start,end:n})}`);D(this,L,rt).call(this,t.start),i(this,b).getSprites({time:!1}).forEach(o=>{const{offset:s,duration:h}=o.time,d=i(this,$)-s;o.preFrame(d>0&&d<h?d:0)}),i(this,E).start=t.start,i(this,E).end=n,i(this,E).step=(t.playbackRate??1)*(1e3/30)*1e3,i(this,C).resume(),i(this,E).audioPlayAt=0,i(this,I).emit("playing"),l.Log.info("AVCanvs play by:",i(this,E))}pause(){D(this,L,ot).call(this)}previewFrame(t){D(this,L,rt).call(this,t),D(this,L,ot).call(this)}captureImage(){return i(this,g).toDataURL()}get activeSprite(){return i(this,b).activeSprite}set activeSprite(t){i(this,b).activeSprite=t}destroy(){var t;i(this,J)||(A(this,J,!0),i(this,C).close(),i(this,P).disconnect(),i(this,I).destroy(),i(this,U).call(this),(t=i(this,g).parentElement)==null||t.remove(),i(this,Q).forEach(e=>e()),i(this,W).clear(),i(this,b).destroy())}captureStream(){i(this,C).state==="suspended"&&i(this,C).resume().catch(l.Log.error);const t=new MediaStream(i(this,g).captureStream().getTracks().concat(i(this,P).stream.getTracks()));return l.Log.info("AVCanvas.captureStream, tracks:",t.getTracks().map(e=>e.kind)),t}async createCombinator(t={}){l.Log.info("AVCanvas.createCombinator, opts:",t);const e=new l.Combinator({...i(this,Z),...t}),n=i(this,b).getSprites({time:!1});if(n.length===0)throw Error("No sprite added");for(const o of n){const s=new l.OffscreenSprite(o.getClip());s.time={...o.time},o.copyStateTo(s),await e.addSprite(s)}return e}}g=new WeakMap,b=new WeakMap,X=new WeakMap,J=new WeakMap,Q=new WeakMap,U=new WeakMap,I=new WeakMap,Z=new WeakMap,$=new WeakMap,L=new WeakSet,rt=function(t){A(this,$,t),i(this,b).updateRenderTime(t)},ot=function(){const t=i(this,E).step!==0;i(this,E).step=0,t&&(i(this,I).emit("paused"),i(this,C).suspend());for(const e of i(this,W))e.stop(),e.disconnect();i(this,W).clear()},C=new WeakMap,P=new WeakMap,W=new WeakMap,bt=function(){var a;const t=i(this,X);let e=i(this,$);const{start:n,end:o,step:s,audioPlayAt:h}=i(this,E);s!==0&&e>=n&&e<o?e+=s:D(this,L,ot).call(this),D(this,L,rt).call(this,e);const d=[];for(const c of i(this,b).getSprites()){t.save();const{audio:f}=c.render(t,e-c.time.offset);t.restore(),d.push(f)}if(t.resetTransform(),s!==0){const c=Math.max(i(this,C).currentTime,h),f=Wt(d,i(this,C));let u=0;for(const p of f)p.start(c),p.connect(i(this,C).destination),p.connect(i(this,P)),i(this,W).add(p),p.onended=()=>{p.disconnect(),i(this,W).delete(p)},u=Math.max(u,((a=p.buffer)==null?void 0:a.duration)??0);i(this,E).audioPlayAt=c+u}},E=new WeakMap,K=new WeakMap;function Wt(r,t){const e=[];if(r.length===0)return e;for(const[n,o]of r){if(n==null||n.length<=0)continue;const s=t.createBuffer(2,n.length,It.sampleRate);s.copyToChannel(n,0),s.copyToChannel(o??n,1);const h=t.createBufferSource();h.buffer=s,e.push(h)}return e}function jt(r){const t=r.createOscillator(),e=new Float32Array([0,0]),n=new Float32Array([0,0]),o=r.createPeriodicWave(e,n,{disableNormalization:!0});return t.setPeriodicWave(o),t.start(),t}w.AVCanvas=Pt,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})});
//# sourceMappingURL=av-canvas.umd.cjs.map