UNPKG

@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.

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