@applemusic-like-lyrics/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # AMLL Core
2
+
3
+ AMLL 的纯 JS 核心组件框架,包括歌词显示组件和背景组件等其它可以复用的组件。
4
+
5
+ 此处的东西都是 UI 框架无关的,所以可以间接在各种动态页面框架下引用。
6
+
7
+ ## 开发调试
@@ -0,0 +1 @@
1
+ "use strict";var w=Object.defineProperty;var v=(d,t,e)=>t in d?w(d,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):d[t]=e;var l=(d,t,e)=>(v(d,typeof t!="symbol"?t+"":t,e),e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("@pixi/display"),C=require("@pixi/app"),f=require("@pixi/filter-blur"),b=require("@pixi/filter-color-matrix"),M=require("@pixi/core"),L=require("@pixi/sprite"),z=require("jss"),B=require("jss-preset-default");class I extends x.Container{constructor(){super(...arguments);l(this,"time",0)}}class A{constructor(t){l(this,"observer");l(this,"app");l(this,"curContainer");l(this,"lastContainer",new Set);l(this,"onTick",t=>{for(const e of this.lastContainer)e.alpha=Math.max(0,e.alpha-t/60),e.alpha<=0&&(this.app.stage.removeChild(e),this.lastContainer.delete(e));if(this.curContainer){this.curContainer.alpha=Math.min(1,this.curContainer.alpha+t/60);const[e,r,s,a]=this.curContainer.children,h=Math.max(this.app.screen.width,this.app.screen.height);e.position.set(this.app.screen.width/2,this.app.screen.height/2),r.position.set(this.app.screen.width/2.5,this.app.screen.height/2.5),s.position.set(this.app.screen.width/2,this.app.screen.height/2),a.position.set(this.app.screen.width/2,this.app.screen.height/2),e.width=h*Math.sqrt(2),e.height=e.width,r.width=h*.8,r.height=r.width,s.width=h*.5,s.height=s.width,a.width=h*.25,a.height=a.width,this.curContainer.time+=t*this.flowSpeed,e.rotation+=t/1e3*this.flowSpeed,r.rotation-=t/500*this.flowSpeed,s.rotation+=t/1e3*this.flowSpeed,a.rotation-=t/750*this.flowSpeed,s.x=this.app.screen.width/2+this.app.screen.width/4*Math.cos(this.curContainer.time/1e3*.75),s.y=this.app.screen.height/2+this.app.screen.width/4*Math.cos(this.curContainer.time/1e3*.75),a.x=this.app.screen.width/2+this.app.screen.width/4*.1+Math.cos(this.curContainer.time*.006*.75),a.y=this.app.screen.height/2+this.app.screen.width/4*.1+Math.cos(this.curContainer.time*.006*.75)}});l(this,"flowSpeed",2);l(this,"currerntRenderScale",.75);this.canvas=t;const e=t.getBoundingClientRect();this.canvas.width=e.width*this.currerntRenderScale,this.canvas.height=e.height*this.currerntRenderScale,this.observer=new ResizeObserver(()=>{const r=t.getBoundingClientRect();this.canvas.width=Math.max(1,r.width),this.canvas.height=Math.max(1,r.height),this.app.renderer.resize(this.canvas.width*this.currerntRenderScale,this.canvas.height*this.currerntRenderScale),this.rebuildFilters()}),this.observer.observe(t),this.app=new C.Application({view:t,resizeTo:this.canvas,powerPreference:"low-power",backgroundAlpha:0}),this.rebuildFilters(),this.app.ticker.add(this.onTick),this.app.ticker.start()}setRenderScale(t){this.currerntRenderScale=t;const e=this.canvas.getBoundingClientRect();this.canvas.width=Math.max(1,e.width),this.canvas.height=Math.max(1,e.height),this.app.renderer.resize(this.canvas.width*this.currerntRenderScale,this.canvas.height*this.currerntRenderScale),this.rebuildFilters()}rebuildFilters(){const t=Math.min(this.canvas.width,this.canvas.height),e=new b.ColorMatrixFilter;e.saturate(1.2,!1);const r=new b.ColorMatrixFilter;r.brightness(.6,!1);const s=new b.ColorMatrixFilter;s.contrast(.3,!0),this.app.stage.filters=[],this.app.stage.filters.push(new f.BlurFilter(5,1)),this.app.stage.filters.push(new f.BlurFilter(10,1)),this.app.stage.filters.push(new f.BlurFilter(20,2)),this.app.stage.filters.push(new f.BlurFilter(40,2)),t>512&&this.app.stage.filters.push(new f.BlurFilter(80,2)),t>768&&this.app.stage.filters.push(new f.BlurFilter(160,4)),t>768*2&&this.app.stage.filters.push(new f.BlurFilter(320,4)),this.app.stage.filters.push(e,r,s),this.app.stage.filters.push(new f.BlurFilter(5,1))}setFPS(t){this.app.ticker.maxFPS=t}pause(){this.app.ticker.stop(),this.app.render()}resume(){this.app.ticker.start()}async setAlbumImage(t){const e=await M.Texture.fromURL(t),r=new I,s=new L.Sprite(e),a=new L.Sprite(e),h=new L.Sprite(e),i=new L.Sprite(e);s.anchor.set(.5,.5),a.anchor.set(.5,.5),h.anchor.set(.5,.5),i.anchor.set(.5,.5),s.rotation=Math.random()*Math.PI*2,a.rotation=Math.random()*Math.PI*2,h.rotation=Math.random()*Math.PI*2,i.rotation=Math.random()*Math.PI*2,r.addChild(s,a,h,i),this.curContainer&&this.lastContainer.add(this.curContainer),this.curContainer=r,this.app.stage.addChild(this.curContainer),this.curContainer.alpha=0}dispose(){this.observer.disconnect(),this.app.ticker.remove(this.onTick)}}class D extends A{constructor(){const e=document.createElement("canvas");super(e);l(this,"element");this.element=e,e.style.pointerEvents="none",e.style.zIndex="-1"}getElement(){return this.element}dispose(){super.dispose(),this.element.remove()}}const k=(d,t)=>d.size===t.size&&[...d].every(e=>t.has(e));function q(d){const e=2.5949095;return d<.5?Math.pow(2*d,2)*((e+1)*2*d-e)/2:(Math.pow(2*d-2,2)*((e+1)*(d*2-2)+e)+2)/2}const y=(d,t,e)=>Math.max(d,Math.min(t,e));class F{constructor(t){l(this,"element",document.createElement("div"));l(this,"dot0",document.createElement("span"));l(this,"dot1",document.createElement("span"));l(this,"dot2",document.createElement("span"));l(this,"left",0);l(this,"top",0);l(this,"scale",1);l(this,"lastStyle","");l(this,"currentInterlude");l(this,"currentTime",0);l(this,"targetBreatheDuration",1500);this.lyricPlayer=t,this.element.className=this.lyricPlayer.style.classes.interludeDots,this.element.appendChild(this.dot0),this.element.appendChild(this.dot1),this.element.appendChild(this.dot2)}getElement(){return this.element}setTransform(t=this.left,e=this.top){this.left=t,this.top=e,this.update()}setInterlude(t){this.currentInterlude=t,this.currentTime=(t==null?void 0:t[0])??0}update(t=0){this.currentTime+=t;let e="";if(e+=`transform:translate(${this.left}px, ${this.top}px)`,this.currentInterlude){const r=this.currentInterlude[1]-this.currentInterlude[0],s=this.currentTime-this.currentInterlude[0];if(s<=r){const a=r/Math.ceil(r/this.targetBreatheDuration);let h=1,i=1;h*=Math.sin(1.5*Math.PI-s/a*2)/10+1,s<1e3&&(h*=1-Math.pow((1e3-s)/1e3,2)),s<500?i=0:s<1e3&&(i*=(s-500)/500),r-s<750&&(h*=1-q((750-(r-s))/750/2)),r-s<375&&(i*=y(0,(r-s)/375,1)),h=Math.max(0,h),e+=` scale(${h})`;const n=y(.25,s*3/r*.75,1),c=y(.25,(s-r/3)*3/r*.75,1),o=y(.25,(s-r/3*2)*3/r*.75,1);this.dot0.style.opacity=`${y(0,Math.max(0,i*n),1)}`,this.dot1.style.opacity=`${y(0,Math.max(0,i*c),1)}`,this.dot2.style.opacity=`${y(0,Math.max(0,i*o),1)}`}else e+=" scale(0)",this.dot0.style.opacity="0",this.dot1.style.opacity="0",this.dot2.style.opacity="0"}else e+=" scale(0)",this.dot0.style.opacity="0",this.dot1.style.opacity="0",this.dot2.style.opacity="0";e+=";",this.lastStyle!==e&&(this.element.setAttribute("style",e),this.lastStyle=e)}dispose(){this.element.remove()}}class S{constructor(t=0){l(this,"currentPosition",0);l(this,"targetPosition",0);l(this,"currentTime",0);l(this,"params",{});l(this,"currentSolver");l(this,"getV");l(this,"queueParams");l(this,"queuePosition");this.targetPosition=t,this.currentPosition=this.targetPosition,this.currentSolver=()=>this.targetPosition,this.getV=()=>0}resetSolver(){const t=this.getV(this.currentTime);this.currentTime=0,this.currentSolver=$(this.currentPosition,t,this.targetPosition,0,this.params),this.getV=O(this.currentSolver)}arrived(){return Math.abs(this.targetPosition-this.currentPosition)<.01&&this.getV(this.currentTime)<.01&&this.queueParams===void 0&&this.queuePosition===void 0}setPosition(t){this.targetPosition=t,this.currentPosition=t,this.currentSolver=()=>this.targetPosition,this.getV=()=>0}update(t=0){this.currentTime+=t,this.currentPosition=this.currentSolver(this.currentTime),this.queueParams&&(this.queueParams.time-=t,this.queueParams.time<=0&&this.updateParams({...this.queueParams})),this.queuePosition&&(this.queuePosition.time-=t,this.queuePosition.time<=0&&this.setTargetPosition(this.queuePosition.position)),this.arrived()&&this.setPosition(this.targetPosition)}updateParams(t,e=0){e>0?this.queueParams={...t,time:e}:(this.params={...this.params,...t},this.resetSolver())}setTargetPosition(t,e=0){e>0?this.queuePosition={position:t,time:e}:(this.queuePosition=void 0,this.targetPosition=t,this.resetSolver())}getCurrentPosition(){return this.currentPosition}}function $(d,t,e,r=0,s){const a=(s==null?void 0:s.soft)??!1,h=(s==null?void 0:s.stiffness)??100,i=(s==null?void 0:s.damping)??10,n=(s==null?void 0:s.mass)??1,c=e-d;if(a||1<=i/(2*Math.sqrt(h*n))){const o=-Math.sqrt(h/n),m=-o*c-t;return p=>(p-=r,p<0?d:e-(c+p*m)*Math.E**(p*o))}else{const o=Math.sqrt(4*n*h-i**2),m=(i*c-2*n*t)/o,p=.5*o/n,g=-(.5*i)/n;return u=>(u-=r,u<0?d:e-(Math.cos(u*p)*c+Math.sin(u*p)*m)*Math.E**(u*g))}}function R(d){return e=>(d(e+.001)-d(e-.001))/(2*.001)}function O(d){return R(d)}const T=/^[\p{Unified_Ideograph}\u0800-\u9FFC]+$/u;function W(d,t="rgba(0,0,0,1)",e="rgba(0,0,0,0.5)"){const r=2+d,s=d/r,a=(1-s)/2;return[`linear-gradient(to right,${t} ${a*100}%,${e} ${(a+s)*100}%)`,s,r]}function P(d){return d.endTime-d.startTime>=1e3&&d.word.length<=7}class G{constructor(t,e={words:[],translatedLyric:"",romanLyric:"",startTime:0,endTime:0,isBG:!1,isDuet:!1}){l(this,"element",document.createElement("div"));l(this,"left",0);l(this,"top",0);l(this,"scale",1);l(this,"blur",0);l(this,"delay",0);l(this,"splittedWords",[]);l(this,"lineSize",[0,0]);l(this,"lineTransforms",{posX:new S(0),posY:new S(0),scale:new S(1)});l(this,"_hide",!0);l(this,"lastStyle","");this.lyricPlayer=t,this.lyricLine=e,this.element.setAttribute("class",this.lyricPlayer.style.classes.lyricLine),this.lyricLine.isBG&&this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine),this.lyricLine.isDuet&&this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine),this.element.appendChild(document.createElement("div")),this.element.appendChild(document.createElement("div")),this.element.appendChild(document.createElement("div"));const r=this.element.children[0],s=this.element.children[1],a=this.element.children[2];r.setAttribute("class",this.lyricPlayer.style.classes.lyricMainLine),s.setAttribute("class",this.lyricPlayer.style.classes.lyricSubLine),a.setAttribute("class",this.lyricPlayer.style.classes.lyricSubLine),this.rebuildElement(),this.rebuildStyle()}enable(){this.element.classList.add("active");const t=this.element.children[0];this.splittedWords.forEach(e=>{e.elementAnimations.forEach(r=>{r.currentTime=0,r.playbackRate=1,r.play()})}),t.classList.add("active")}disable(){this.element.classList.remove("active");const t=this.element.children[0];this.splittedWords.forEach(e=>{e.elementAnimations.forEach(r=>{r.id==="float-word"&&(r.playbackRate=-1,r.play())})}),t.classList.remove("active")}setLine(t){this.lyricLine=t,this.lyricLine.isBG?this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine):this.element.classList.remove(this.lyricPlayer.style.classes.lyricBgLine),this.lyricLine.isDuet?this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine):this.element.classList.remove(this.lyricPlayer.style.classes.lyricDuetLine),this.rebuildElement(),this.rebuildStyle()}getLine(){return this.lyricLine}show(){this._hide=!1,this.rebuildStyle()}hide(){this._hide=!0,this.rebuildStyle()}rebuildStyle(){if(this._hide){this.lastStyle!=="visibility:hidden;transform:translate(0,-10000px);"&&(this.lastStyle="visibility:hidden;transform:translate(0,-10000px);",this.element.setAttribute("style","visibility:hidden;transform:translate(0,-10000px);"));return}let t=`transform:translate(${this.lineTransforms.posX.getCurrentPosition()}px,${this.lineTransforms.posY.getCurrentPosition()}px) scale(${this.lineTransforms.scale.getCurrentPosition()});`;this.lyricPlayer.disableSpring&&this.isInSight&&(t+=`transition-delay:${this.delay}ms;`),t+=`filter:blur(${Math.min(32,this.blur)}px);`,t!==this.lastStyle&&(this.lastStyle=t,this.element.setAttribute("style",t))}rebuildElement(){const t=this.element.children[0],e=this.element.children[1],r=this.element.children[2];this.splittedWords=[],this.lyricLine.words.forEach(n=>{if(T.test(n.word))this.splittedWords.push({...n,width:0,height:0,elements:[],elementAnimations:[],shouldEmphasize:P(n)});else{const c=/(\s*)(\S*)(\s*)/.exec(n.word);c&&(c[1].length>0&&this.splittedWords.push({word:" ",startTime:0,endTime:0,width:0,height:0,elements:[],elementAnimations:[],shouldEmphasize:!1}),this.splittedWords.push({word:c[2],startTime:n.startTime,endTime:n.endTime,width:0,height:0,elements:[],elementAnimations:[],shouldEmphasize:P(n)}),c[3].length>0&&this.splittedWords.push({word:" ",startTime:0,endTime:0,width:0,height:0,elements:[],elementAnimations:[],shouldEmphasize:!1}))}});const s=[],a=[];function h(n){for(;n.firstChild;)n.firstChild.nodeType===Node.ELEMENT_NODE?s.push(t.firstChild):n.firstChild.nodeType===Node.TEXT_NODE&&a.push(t.firstChild),n.removeChild(n.firstChild),h(n.firstChild)}h(t);let i=null;this.splittedWords.forEach(n=>{if(n.word.trim().length>0)if(n.shouldEmphasize){const c=s.pop()??document.createElement("span");c.className="emphasize",n.elements=[c];for(const o of n.word){const m=s.pop()??document.createElement("span");m.className="",m.innerText=o,c.appendChild(m),n.elements.push(m)}if(n.elementAnimations=this.initEmphasizeAnimation(n),console.log(n.word,T.test(n.word)),i&&!T.test(n.word))if(i.childElementCount>0)i.appendChild(c);else{const o=s.pop()??document.createElement("span");o.className="",i.remove(),o.appendChild(i),o.appendChild(c),t.appendChild(o),i=o}else i=T.test(n.word)?null:c,t.appendChild(c)}else{const c=s.pop()??document.createElement("span");if(c.className="",c.innerText=n.word,n.elements=[c],n.elementAnimations.push(this.initFloatAnimation(n,c)),i)if(i.childElementCount>0)i.appendChild(c);else{const o=s.pop()??document.createElement("span");o.className="",i.remove(),o.appendChild(i),o.appendChild(c),t.appendChild(o),i=o}else i=c,t.appendChild(c)}else if(n.word.length>0){const c=a.pop()??document.createTextNode(" ");t.appendChild(c),i=null}else i=null}),e.innerText=this.lyricLine.translatedLyric,r.innerText=this.lyricLine.romanLyric}initFloatAnimation(t,e){const r=e.animate([{transform:"translateY(0px)"},{transform:"translateY(-3%)"}],{duration:Math.max(1e3,t.endTime-t.startTime),delay:t.startTime-this.lyricLine.startTime,id:"float-word",composite:"add",fill:"both"});return r.pause(),r}initEmphasizeAnimation(t){const e=t.startTime-this.lyricLine.startTime,r=t.endTime-t.startTime;return t.elements.map((s,a,h)=>{if(a===0)return this.initFloatAnimation(t,s);{const i=s.animate([{offset:0,transform:"translate3d(0, 0px, 0px)",filter:"drop-shadow(0 0 0 var(--amll-lyric-line-color))"},{offset:.5,transform:"translate3d(0, -2%, 20px)",filter:"drop-shadow(0 0 0.2rem var(--amll-lyric-line-color))"},{offset:1,transform:"translate3d(0, 0px, 0)",filter:"drop-shadow(0 0 0 var(--amll-lyric-line-color))"}],{duration:Math.max(1e3,t.endTime-t.startTime),delay:e+r/(h.length-1)*(a-1),id:"glow-word",iterations:1,composite:"replace",fill:"both"});return i.pause(),i}})}updateMaskImage(){this.splittedWords.forEach(t=>{const e=t.elements[0];if(e){t.width=e.clientWidth,t.height=e.clientHeight;const[r,s,a]=W(16/t.width,"rgba(0,0,0,0.75)","rgba(0,0,0,0.25)"),h=`${a*100}% 100%`;this.lyricPlayer.supportMaskImage?(e.style.maskImage=r,e.style.maskOrigin="left",e.style.maskSize=h):(e.style.webkitMaskImage=r,e.style.webkitMaskOrigin="left",e.style.webkitMaskSize=h);const i=t.width+16,n=`clamp(${-i}px,calc(${-i}px + (var(--amll-player-time) - ${t.startTime})*${i/Math.abs(t.endTime-t.startTime)}px),0px) 0px, left top`;e.style.maskPosition=n,e.style.webkitMaskPosition=n}})}getElement(){return this.element}setTransform(t=this.left,e=this.top,r=this.scale,s=1,a=0,h=!1,i=0){this.left=t,this.top=e,this.scale=r,this.delay=i*1e3|0;const n=this.element.children[0];n.style.opacity=`${s}`,h||this.lyricPlayer.disableSpring?(this.blur=Math.min(32,a),h&&this.element.classList.add(this.lyricPlayer.style.classes.tmpDisableTransition),this.lineTransforms.posX.setPosition(t),this.lineTransforms.posY.setPosition(e),this.lineTransforms.scale.setPosition(r),this.lyricPlayer.disableSpring?this.show():this.rebuildStyle(),h&&requestAnimationFrame(()=>{this.element.classList.remove(this.lyricPlayer.style.classes.tmpDisableTransition)})):(this.lineTransforms.posX.setTargetPosition(t,i),this.lineTransforms.posY.setTargetPosition(e,i),this.lineTransforms.scale.setTargetPosition(r),this.blur!==Math.min(32,a)&&(this.blur=Math.min(32,a),this.element.style.filter=`blur(${Math.min(32,a)}px)`))}update(t=0){this.lyricPlayer.disableSpring||(this.lineTransforms.posX.update(t),this.lineTransforms.posY.update(t),this.lineTransforms.scale.update(t),this.isInSight?this.show():this.hide())}get isInSight(){const t=this.lineTransforms.posX.getCurrentPosition(),e=this.lineTransforms.posY.getCurrentPosition(),r=t+this.lineSize[0],s=e+this.lineSize[1],a=this.lyricPlayer.pos[0],h=this.lyricPlayer.pos[1],i=this.lyricPlayer.pos[0]+this.lyricPlayer.size[0],n=this.lyricPlayer.pos[1]+this.lyricPlayer.size[1];return!(t>i||e>n||r<a||s<h)}dispose(){this.element.remove()}}const X=z.create(B());class Y extends EventTarget{constructor(){super();l(this,"element",document.createElement("div"));l(this,"currentTime",0);l(this,"lyricLines",[]);l(this,"processedLines",[]);l(this,"lyricLinesEl",[]);l(this,"lyricLinesSize",new Map);l(this,"hotLines",new Set);l(this,"bufferedLines",new Set);l(this,"scrollToIndex",0);l(this,"resizeObserver",new ResizeObserver(e=>{const r=e[0].contentRect;this.size[0]=r.width,this.size[1]=r.height,this.pos[0]=r.left,this.pos[1]=r.top,this.rebuildStyle(),this.calcLayout(!0),this.lyricLinesEl.forEach(s=>s.updateMaskImage())}));l(this,"posXSpringParams",{mass:1,damping:10,stiffness:100});l(this,"posYSpringParams",{mass:1,damping:15,stiffness:100});l(this,"scaleSpringParams",{mass:1,damping:20,stiffness:100});l(this,"enableBlur",!0);l(this,"interludeDots");l(this,"interludeDotsSize",[0,0]);l(this,"supportPlusLighter",CSS.supports("mix-blend-mode","plus-lighter"));l(this,"supportMaskImage",CSS.supports("mask-image","none"));l(this,"disableSpring",!1);l(this,"alignAnchor",.5);l(this,"size",[0,0]);l(this,"pos",[0,0]);l(this,"style",X.createStyleSheet({lyricPlayer:{userSelect:"none",padding:"1rem",boxSizing:"border-box",fontSize:"max(5vh, 12px)",fontWeight:"bold",width:"100%",height:"100%",overflow:"hidden",maxWidth:"100%",maxHeight:"100%",zIndex:1,color:"var(--amll-lyric-line-color)",mixBlendMode:"plus-lighter",contain:"strict"},lyricLine:{position:"absolute",transformOrigin:"left",maxWidth:"65%",padding:"max(2vh, 1rem) 1rem",contain:"content",transition:"filter 0.25s",margin:"max(2vh, 1rem) -1rem"},"@media (max-width: 1024px)":{lyricLine:{maxWidth:"75%",padding:"max(1vh, 1rem) 1rem",margin:"0 -1rem"}},lyricDuetLine:{textAlign:"right",transformOrigin:"right"},lyricBgLine:{opacity:0,fontSize:"max(50%, 10px)",transition:"opacity 0.25s","&.active":{transition:"opacity 0.25s 0.25s",opacity:1}},lyricMainLine:{transition:"opacity 0.3s 0.25s",margin:"-1rem",padding:"1rem","& span":{display:"inline-block",margin:"-1rem",padding:"1rem"},"& > span":{whiteSpace:"pre-wrap",wordBreak:"keep-all",maxLines:"1","&.emphasize":{margin:"-1rem",padding:"1rem",transformStyle:"preserve-3d",perspective:"50vw"}}},lyricSubLine:{fontSize:"max(50%, 10px)",opacity:.5},disableSpring:{"& > *":{transition:"filter 0.25s, transform 0.5s"}},interludeDots:{height:"min(1rem,2.5vh)",transformOrigin:"center",width:"fit-content",padding:"2.5% 0",position:"absolute",display:"flex",gap:"0.5rem","& > *":{width:"100%",display:"inline-block",borderRadius:"50%",aspectRatio:"1 / 1",backgroundColor:"var(--amll-lyric-line-color)",marginRight:"4px"},"&.duet":{right:"1rem",transformOrigin:"center"}},"@supports (mix-blend-mode: plus-lighter)":{lyricSubLine:{opacity:.3}},tmpDisableTransition:{transition:"none !important"}}));l(this,"onPageShow",()=>{this.calcLayout(!0)});this.interludeDots=new F(this),this.element.setAttribute("class",this.style.classes.lyricPlayer),this.disableSpring&&this.element.classList.add(this.style.classes.disableSpring),this.rebuildStyle(),this.resizeObserver.observe(this.element),this.element.appendChild(this.interludeDots.getElement()),this.style.attach(),this.interludeDots.setTransform(0,200),window.addEventListener("pageshow",this.onPageShow)}setEnableSpring(e=!0){this.disableSpring=!e,e?this.element.classList.remove(this.style.classes.disableSpring):this.element.classList.add(this.style.classes.disableSpring),this.calcLayout(!0)}getCurrentInterlude(){var s,a,h;if(this.bufferedLines.size>0)return;const e=this.currentTime+20,r=this.scrollToIndex;if(r===0){if((s=this.processedLines[0])!=null&&s.startTime&&this.processedLines[0].startTime>e)return[0,this.processedLines[0].startTime]}else if((a=this.processedLines[r])!=null&&a.endTime&&((h=this.processedLines[r+1])!=null&&h.startTime)&&this.processedLines[r+1].startTime>e&&this.processedLines[r].endTime<e)return[this.processedLines[r].endTime,this.processedLines[r+1].startTime]}rebuildStyle(){let e="";e+="--amll-lyric-player-width:",e+=this.element.clientWidth,e+="px;",e+="--amll-lyric-player-height:",e+=this.element.clientHeight,e+="px;",e+="--amll-lyric-line-color:",e+="#FFFFFF;",e+="--amll-player-time:",e+=this.currentTime,e+=";",this.element.setAttribute("style",e)}setEnableBlur(e){this.enableBlur!==e&&(this.enableBlur=e,this.calcLayout())}setLyricLines(e){this.lyricLines=e;const r=750;this.processedLines=e.map((s,a,h)=>{if(s.isBG)return{...s};{const i=h[a-1],n=h[a-2];if(i!=null&&i.isBG&&n){if(n.endTime<s.startTime)return{...s,startTime:Math.max(n.endTime,s.startTime-r)||s.startTime}}else if(i!=null&&i.endTime&&i.endTime<s.startTime)return{...s,startTime:Math.max(i==null?void 0:i.endTime,s.startTime-r)||s.startTime};return{...s}}}),this.processedLines.forEach((s,a,h)=>{const i=h[a+1],n=s.words[s.words.length-1];n&&P(n)&&(i?i.startTime>s.endTime&&(s.endTime=Math.min(s.endTime+1500,i.startTime)):s.endTime=s.endTime+1500)}),this.processedLines.forEach((s,a,h)=>{if(s.isBG)return;const i=h[a+1];i!=null&&i.isBG&&(i.startTime=Math.min(i.startTime,s.startTime))}),this.lyricLinesEl.forEach(s=>s.dispose()),this.lyricLinesEl=this.processedLines.map(s=>new G(this,s)),this.lyricLinesEl.forEach(s=>(this.element.appendChild(s.getElement()),s.updateMaskImage())),this.setLinePosXSpringParams({}),this.setLinePosYSpringParams({}),this.setLineScaleSpringParams({}),this.setCurrentTime(0,!0),this.calcLayout(!0)}calcLayout(e=!1){e&&(this.lyricLinesEl.forEach(o=>{const m=[o.getElement().clientWidth,o.getElement().clientHeight];this.lyricLinesSize.set(o,m),o.lineSize=m}),this.interludeDotsSize[0]=this.interludeDots.getElement().clientWidth,this.interludeDotsSize[1]=this.interludeDots.getElement().clientHeight);const r=.95;let a=-this.lyricLinesEl.slice(0,this.scrollToIndex).reduce((o,m)=>o+(m.getLine().isBG?0:this.lyricLinesSize.get(m)[1]),0);if(this.alignAnchor==="bottom"){a+=this.element.clientHeight/2;const o=this.lyricLinesEl[this.scrollToIndex];if(o){const m=this.lyricLinesSize.get(o)[1];a-=m/2}}else if(typeof this.alignAnchor=="number"){a+=this.element.clientHeight*this.alignAnchor;const o=this.lyricLinesEl[this.scrollToIndex];if(o){const m=this.lyricLinesSize.get(o)[1];a-=m/2}}const h=this.currentTime===0?void 0:this.getCurrentInterlude();let i=0;if(h&&(i=h[1]-h[0],i>=5e3)){const o=this.currentTime===0?this.lyricLinesEl[0]:this.lyricLinesEl[this.scrollToIndex+1];o&&(a-=this.lyricLinesSize.get(o)[1])}const n=Math.max(...this.bufferedLines);let c=0;this.lyricLinesEl.forEach((o,m)=>{const p=this.bufferedLines.has(m),g=p||m>=this.scrollToIndex&&m<n,u=o.getLine();let E=0;u.isDuet&&(E=this.size[0]-this.lyricLinesSize.get(o)[0]),m===this.scrollToIndex+1&&i>=5e3&&(this.interludeDots.setTransform(0,a),this.interludeDots.setInterlude(this.getCurrentInterlude()),a+=this.interludeDotsSize[1]),o.setTransform(E,a,g?1:r,p?1:1/3,this.enableBlur?3*(g?0:1+(m<this.scrollToIndex?Math.abs(this.scrollToIndex-m):Math.abs(m-Math.max(this.scrollToIndex,n)))):0,e,c),u.isBG&&g?a+=this.lyricLinesSize.get(o)[1]:u.isBG||(a+=this.lyricLinesSize.get(o)[1]),a>=0&&(c+=.05)})}getCurrentTime(){return this.currentTime}getLyrics(){return this.lyricLines}getElement(){return this.element}setCurrentTime(e,r=!1){this.currentTime=e,this.element.style.setProperty("--amll-player-time",`${e}`);const s=new Set,a=new Set,h=new Set;this.hotLines.forEach(i=>{const n=this.processedLines[i];if(!n.isBG)if(n){const c=this.processedLines[i+1];if(c!=null&&c.isBG){const o=Math.min(n.startTime,c==null?void 0:c.startTime),m=Math.max(n.endTime,c==null?void 0:c.endTime);(o>e||m<=e)&&(this.hotLines.delete(i),s.add(i),this.hotLines.delete(i+1),s.add(i+1),r&&(this.lyricLinesEl[i].disable(),this.lyricLinesEl[i+1].disable()))}else(n.startTime>e||n.endTime<=e)&&(this.hotLines.delete(i),s.add(i),r&&this.lyricLinesEl[i].disable())}else this.hotLines.delete(i),s.add(i),r&&this.lyricLinesEl[i].disable()}),this.processedLines.forEach((i,n,c)=>{var o;!i.isBG&&i.startTime<=e&&i.endTime>e&&(this.hotLines.has(n)||(this.hotLines.add(n),h.add(n),r&&this.lyricLinesEl[n].enable(),(o=c[n+1])!=null&&o.isBG&&(this.hotLines.add(n+1),h.add(n+1),r&&this.lyricLinesEl[n+1].enable())))}),this.bufferedLines.forEach(i=>{this.hotLines.has(i)||(a.add(i),r&&this.lyricLinesEl[i].disable())}),r?(this.bufferedLines.size>0?this.scrollToIndex=Math.min(...this.bufferedLines):this.scrollToIndex=this.processedLines.findIndex(i=>i.startTime>=e),this.bufferedLines.clear(),this.hotLines.forEach(i=>this.bufferedLines.add(i)),this.calcLayout(!0)):(a.size>0||h.size>0)&&(a.size===0&&h.size>0?(h.forEach(i=>{this.bufferedLines.add(i),this.lyricLinesEl[i].enable()}),this.scrollToIndex=Math.min(...this.bufferedLines)):h.size===0&&a.size>0?k(a,this.bufferedLines)&&this.bufferedLines.forEach(i=>{this.hotLines.has(i)||(this.bufferedLines.delete(i),this.lyricLinesEl[i].disable())}):(h.forEach(i=>{this.bufferedLines.add(i),this.lyricLinesEl[i].enable()}),a.forEach(i=>{this.bufferedLines.delete(i),this.lyricLinesEl[i].disable()}),this.bufferedLines.size>0&&(this.scrollToIndex=Math.min(...this.bufferedLines))),this.calcLayout())}update(e=0){const r=e/1e3;this.interludeDots.update(e),this.lyricLinesEl.forEach(s=>s.update(r))}setLinePosXSpringParams(e){this.posXSpringParams={...this.posXSpringParams,...e},this.lyricLinesEl.forEach(r=>r.lineTransforms.posX.updateParams(this.posXSpringParams))}setLinePosYSpringParams(e){this.posYSpringParams={...this.posYSpringParams,...e},this.lyricLinesEl.forEach(r=>r.lineTransforms.posY.updateParams(this.posYSpringParams))}setLineScaleSpringParams(e){this.scaleSpringParams={...this.scaleSpringParams,...e},this.lyricLinesEl.forEach(r=>r.lineTransforms.scale.updateParams(this.scaleSpringParams))}dispose(){this.element.remove(),this.resizeObserver.disconnect(),this.style.detach(),this.lyricLinesEl.forEach(e=>e.dispose()),window.removeEventListener("pageshow",this.onPageShow)}}exports.BackgroundRender=D;exports.LyricPlayer=Y;
@@ -0,0 +1,904 @@
1
+ var w = Object.defineProperty;
2
+ var v = (d, t, e) => t in d ? w(d, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : d[t] = e;
3
+ var l = (d, t, e) => (v(d, typeof t != "symbol" ? t + "" : t, e), e);
4
+ import { Container as x } from "@pixi/display";
5
+ import { Application as C } from "@pixi/app";
6
+ import { BlurFilter as f } from "@pixi/filter-blur";
7
+ import { ColorMatrixFilter as b } from "@pixi/filter-color-matrix";
8
+ import { Texture as M } from "@pixi/core";
9
+ import { Sprite as L } from "@pixi/sprite";
10
+ import { create as z } from "jss";
11
+ import I from "jss-preset-default";
12
+ class A extends x {
13
+ constructor() {
14
+ super(...arguments);
15
+ l(this, "time", 0);
16
+ }
17
+ }
18
+ class B {
19
+ constructor(t) {
20
+ l(this, "observer");
21
+ l(this, "app");
22
+ l(this, "curContainer");
23
+ l(this, "lastContainer", /* @__PURE__ */ new Set());
24
+ l(this, "onTick", (t) => {
25
+ for (const e of this.lastContainer)
26
+ e.alpha = Math.max(0, e.alpha - t / 60), e.alpha <= 0 && (this.app.stage.removeChild(e), this.lastContainer.delete(e));
27
+ if (this.curContainer) {
28
+ this.curContainer.alpha = Math.min(
29
+ 1,
30
+ this.curContainer.alpha + t / 60
31
+ );
32
+ const [e, n, s, a] = this.curContainer.children, h = Math.max(this.app.screen.width, this.app.screen.height);
33
+ e.position.set(this.app.screen.width / 2, this.app.screen.height / 2), n.position.set(
34
+ this.app.screen.width / 2.5,
35
+ this.app.screen.height / 2.5
36
+ ), s.position.set(this.app.screen.width / 2, this.app.screen.height / 2), a.position.set(this.app.screen.width / 2, this.app.screen.height / 2), e.width = h * Math.sqrt(2), e.height = e.width, n.width = h * 0.8, n.height = n.width, s.width = h * 0.5, s.height = s.width, a.width = h * 0.25, a.height = a.width, this.curContainer.time += t * this.flowSpeed, e.rotation += t / 1e3 * this.flowSpeed, n.rotation -= t / 500 * this.flowSpeed, s.rotation += t / 1e3 * this.flowSpeed, a.rotation -= t / 750 * this.flowSpeed, s.x = this.app.screen.width / 2 + this.app.screen.width / 4 * Math.cos(this.curContainer.time / 1e3 * 0.75), s.y = this.app.screen.height / 2 + this.app.screen.width / 4 * Math.cos(this.curContainer.time / 1e3 * 0.75), a.x = this.app.screen.width / 2 + this.app.screen.width / 4 * 0.1 + Math.cos(this.curContainer.time * 6e-3 * 0.75), a.y = this.app.screen.height / 2 + this.app.screen.width / 4 * 0.1 + Math.cos(this.curContainer.time * 6e-3 * 0.75);
37
+ }
38
+ });
39
+ l(this, "flowSpeed", 2);
40
+ l(this, "currerntRenderScale", 0.75);
41
+ this.canvas = t;
42
+ const e = t.getBoundingClientRect();
43
+ this.canvas.width = e.width * this.currerntRenderScale, this.canvas.height = e.height * this.currerntRenderScale, this.observer = new ResizeObserver(() => {
44
+ const n = t.getBoundingClientRect();
45
+ this.canvas.width = Math.max(1, n.width), this.canvas.height = Math.max(1, n.height), this.app.renderer.resize(
46
+ this.canvas.width * this.currerntRenderScale,
47
+ this.canvas.height * this.currerntRenderScale
48
+ ), this.rebuildFilters();
49
+ }), this.observer.observe(t), this.app = new C({
50
+ view: t,
51
+ resizeTo: this.canvas,
52
+ powerPreference: "low-power",
53
+ backgroundAlpha: 0
54
+ }), this.rebuildFilters(), this.app.ticker.add(this.onTick), this.app.ticker.start();
55
+ }
56
+ setRenderScale(t) {
57
+ this.currerntRenderScale = t;
58
+ const e = this.canvas.getBoundingClientRect();
59
+ this.canvas.width = Math.max(1, e.width), this.canvas.height = Math.max(1, e.height), this.app.renderer.resize(
60
+ this.canvas.width * this.currerntRenderScale,
61
+ this.canvas.height * this.currerntRenderScale
62
+ ), this.rebuildFilters();
63
+ }
64
+ rebuildFilters() {
65
+ const t = Math.min(this.canvas.width, this.canvas.height), e = new b();
66
+ e.saturate(1.2, !1);
67
+ const n = new b();
68
+ n.brightness(0.6, !1);
69
+ const s = new b();
70
+ s.contrast(0.3, !0), this.app.stage.filters = [], this.app.stage.filters.push(new f(5, 1)), this.app.stage.filters.push(new f(10, 1)), this.app.stage.filters.push(new f(20, 2)), this.app.stage.filters.push(new f(40, 2)), t > 512 && this.app.stage.filters.push(new f(80, 2)), t > 768 && this.app.stage.filters.push(new f(160, 4)), t > 768 * 2 && this.app.stage.filters.push(new f(320, 4)), this.app.stage.filters.push(e, n, s), this.app.stage.filters.push(new f(5, 1));
71
+ }
72
+ setFPS(t) {
73
+ this.app.ticker.maxFPS = t;
74
+ }
75
+ pause() {
76
+ this.app.ticker.stop(), this.app.render();
77
+ }
78
+ resume() {
79
+ this.app.ticker.start();
80
+ }
81
+ async setAlbumImage(t) {
82
+ const e = await M.fromURL(t), n = new A(), s = new L(e), a = new L(e), h = new L(e), i = new L(e);
83
+ s.anchor.set(0.5, 0.5), a.anchor.set(0.5, 0.5), h.anchor.set(0.5, 0.5), i.anchor.set(0.5, 0.5), s.rotation = Math.random() * Math.PI * 2, a.rotation = Math.random() * Math.PI * 2, h.rotation = Math.random() * Math.PI * 2, i.rotation = Math.random() * Math.PI * 2, n.addChild(s, a, h, i), this.curContainer && this.lastContainer.add(this.curContainer), this.curContainer = n, this.app.stage.addChild(this.curContainer), this.curContainer.alpha = 0;
84
+ }
85
+ dispose() {
86
+ this.observer.disconnect(), this.app.ticker.remove(this.onTick);
87
+ }
88
+ }
89
+ class Q extends B {
90
+ constructor() {
91
+ const e = document.createElement("canvas");
92
+ super(e);
93
+ l(this, "element");
94
+ this.element = e, e.style.pointerEvents = "none", e.style.zIndex = "-1";
95
+ }
96
+ getElement() {
97
+ return this.element;
98
+ }
99
+ dispose() {
100
+ super.dispose(), this.element.remove();
101
+ }
102
+ }
103
+ const D = (d, t) => d.size === t.size && [...d].every((e) => t.has(e));
104
+ function k(d) {
105
+ const e = 2.5949095;
106
+ return d < 0.5 ? Math.pow(2 * d, 2) * ((e + 1) * 2 * d - e) / 2 : (Math.pow(2 * d - 2, 2) * ((e + 1) * (d * 2 - 2) + e) + 2) / 2;
107
+ }
108
+ const y = (d, t, e) => Math.max(d, Math.min(t, e));
109
+ class $ {
110
+ constructor(t) {
111
+ l(this, "element", document.createElement("div"));
112
+ l(this, "dot0", document.createElement("span"));
113
+ l(this, "dot1", document.createElement("span"));
114
+ l(this, "dot2", document.createElement("span"));
115
+ l(this, "left", 0);
116
+ l(this, "top", 0);
117
+ l(this, "scale", 1);
118
+ l(this, "lastStyle", "");
119
+ l(this, "currentInterlude");
120
+ l(this, "currentTime", 0);
121
+ l(this, "targetBreatheDuration", 1500);
122
+ this.lyricPlayer = t, this.element.className = this.lyricPlayer.style.classes.interludeDots, this.element.appendChild(this.dot0), this.element.appendChild(this.dot1), this.element.appendChild(this.dot2);
123
+ }
124
+ getElement() {
125
+ return this.element;
126
+ }
127
+ setTransform(t = this.left, e = this.top) {
128
+ this.left = t, this.top = e, this.update();
129
+ }
130
+ setInterlude(t) {
131
+ this.currentInterlude = t, this.currentTime = (t == null ? void 0 : t[0]) ?? 0;
132
+ }
133
+ update(t = 0) {
134
+ this.currentTime += t;
135
+ let e = "";
136
+ if (e += `transform:translate(${this.left}px, ${this.top}px)`, this.currentInterlude) {
137
+ const n = this.currentInterlude[1] - this.currentInterlude[0], s = this.currentTime - this.currentInterlude[0];
138
+ if (s <= n) {
139
+ const a = n / Math.ceil(n / this.targetBreatheDuration);
140
+ let h = 1, i = 1;
141
+ h *= Math.sin(1.5 * Math.PI - s / a * 2) / 10 + 1, s < 1e3 && (h *= 1 - Math.pow((1e3 - s) / 1e3, 2)), s < 500 ? i = 0 : s < 1e3 && (i *= (s - 500) / 500), n - s < 750 && (h *= 1 - k(
142
+ (750 - (n - s)) / 750 / 2
143
+ )), n - s < 375 && (i *= y(
144
+ 0,
145
+ (n - s) / 375,
146
+ 1
147
+ )), h = Math.max(0, h), e += ` scale(${h})`;
148
+ const r = y(
149
+ 0.25,
150
+ s * 3 / n * 0.75,
151
+ 1
152
+ ), c = y(
153
+ 0.25,
154
+ (s - n / 3) * 3 / n * 0.75,
155
+ 1
156
+ ), o = y(
157
+ 0.25,
158
+ (s - n / 3 * 2) * 3 / n * 0.75,
159
+ 1
160
+ );
161
+ this.dot0.style.opacity = `${y(
162
+ 0,
163
+ Math.max(0, i * r),
164
+ 1
165
+ )}`, this.dot1.style.opacity = `${y(
166
+ 0,
167
+ Math.max(0, i * c),
168
+ 1
169
+ )}`, this.dot2.style.opacity = `${y(
170
+ 0,
171
+ Math.max(0, i * o),
172
+ 1
173
+ )}`;
174
+ } else
175
+ e += " scale(0)", this.dot0.style.opacity = "0", this.dot1.style.opacity = "0", this.dot2.style.opacity = "0";
176
+ } else
177
+ e += " scale(0)", this.dot0.style.opacity = "0", this.dot1.style.opacity = "0", this.dot2.style.opacity = "0";
178
+ e += ";", this.lastStyle !== e && (this.element.setAttribute("style", e), this.lastStyle = e);
179
+ }
180
+ dispose() {
181
+ this.element.remove();
182
+ }
183
+ }
184
+ class S {
185
+ constructor(t = 0) {
186
+ l(this, "currentPosition", 0);
187
+ l(this, "targetPosition", 0);
188
+ l(this, "currentTime", 0);
189
+ l(this, "params", {});
190
+ l(this, "currentSolver");
191
+ l(this, "getV");
192
+ l(this, "queueParams");
193
+ l(this, "queuePosition");
194
+ this.targetPosition = t, this.currentPosition = this.targetPosition, this.currentSolver = () => this.targetPosition, this.getV = () => 0;
195
+ }
196
+ resetSolver() {
197
+ const t = this.getV(this.currentTime);
198
+ this.currentTime = 0, this.currentSolver = q(
199
+ this.currentPosition,
200
+ t,
201
+ this.targetPosition,
202
+ 0,
203
+ this.params
204
+ ), this.getV = W(this.currentSolver);
205
+ }
206
+ arrived() {
207
+ return Math.abs(this.targetPosition - this.currentPosition) < 0.01 && this.getV(this.currentTime) < 0.01 && this.queueParams === void 0 && this.queuePosition === void 0;
208
+ }
209
+ setPosition(t) {
210
+ this.targetPosition = t, this.currentPosition = t, this.currentSolver = () => this.targetPosition, this.getV = () => 0;
211
+ }
212
+ update(t = 0) {
213
+ this.currentTime += t, this.currentPosition = this.currentSolver(this.currentTime), this.queueParams && (this.queueParams.time -= t, this.queueParams.time <= 0 && this.updateParams({
214
+ ...this.queueParams
215
+ })), this.queuePosition && (this.queuePosition.time -= t, this.queuePosition.time <= 0 && this.setTargetPosition(this.queuePosition.position)), this.arrived() && this.setPosition(this.targetPosition);
216
+ }
217
+ updateParams(t, e = 0) {
218
+ e > 0 ? this.queueParams = {
219
+ ...t,
220
+ time: e
221
+ } : (this.params = {
222
+ ...this.params,
223
+ ...t
224
+ }, this.resetSolver());
225
+ }
226
+ setTargetPosition(t, e = 0) {
227
+ e > 0 ? this.queuePosition = {
228
+ position: t,
229
+ time: e
230
+ } : (this.queuePosition = void 0, this.targetPosition = t, this.resetSolver());
231
+ }
232
+ getCurrentPosition() {
233
+ return this.currentPosition;
234
+ }
235
+ }
236
+ function q(d, t, e, n = 0, s) {
237
+ const a = (s == null ? void 0 : s.soft) ?? !1, h = (s == null ? void 0 : s.stiffness) ?? 100, i = (s == null ? void 0 : s.damping) ?? 10, r = (s == null ? void 0 : s.mass) ?? 1, c = e - d;
238
+ if (a || 1 <= i / (2 * Math.sqrt(h * r))) {
239
+ const o = -Math.sqrt(h / r), m = -o * c - t;
240
+ return (p) => (p -= n, p < 0 ? d : e - (c + p * m) * Math.E ** (p * o));
241
+ } else {
242
+ const o = Math.sqrt(
243
+ 4 * r * h - i ** 2
244
+ ), m = (i * c - 2 * r * t) / o, p = 0.5 * o / r, g = -(0.5 * i) / r;
245
+ return (u) => (u -= n, u < 0 ? d : e - (Math.cos(u * p) * c + Math.sin(u * p) * m) * Math.E ** (u * g));
246
+ }
247
+ }
248
+ function R(d) {
249
+ return (e) => (d(e + 1e-3) - d(e - 1e-3)) / (2 * 1e-3);
250
+ }
251
+ function W(d) {
252
+ return R(d);
253
+ }
254
+ const T = /^[\p{Unified_Ideograph}\u0800-\u9FFC]+$/u;
255
+ function F(d, t = "rgba(0,0,0,1)", e = "rgba(0,0,0,0.5)") {
256
+ const n = 2 + d, s = d / n, a = (1 - s) / 2;
257
+ return [
258
+ `linear-gradient(to right,${t} ${a * 100}%,${e} ${(a + s) * 100}%)`,
259
+ s,
260
+ n
261
+ ];
262
+ }
263
+ function P(d) {
264
+ return d.endTime - d.startTime >= 1e3 && d.word.length <= 7;
265
+ }
266
+ class O {
267
+ constructor(t, e = {
268
+ words: [],
269
+ translatedLyric: "",
270
+ romanLyric: "",
271
+ startTime: 0,
272
+ endTime: 0,
273
+ isBG: !1,
274
+ isDuet: !1
275
+ }) {
276
+ l(this, "element", document.createElement("div"));
277
+ l(this, "left", 0);
278
+ l(this, "top", 0);
279
+ l(this, "scale", 1);
280
+ l(this, "blur", 0);
281
+ l(this, "delay", 0);
282
+ l(this, "splittedWords", []);
283
+ // 由 LyricPlayer 来设置
284
+ l(this, "lineSize", [0, 0]);
285
+ l(this, "lineTransforms", {
286
+ posX: new S(0),
287
+ posY: new S(0),
288
+ scale: new S(1)
289
+ });
290
+ l(this, "_hide", !0);
291
+ l(this, "lastStyle", "");
292
+ this.lyricPlayer = t, this.lyricLine = e, this.element.setAttribute(
293
+ "class",
294
+ this.lyricPlayer.style.classes.lyricLine
295
+ ), this.lyricLine.isBG && this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine), this.lyricLine.isDuet && this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine), this.element.appendChild(document.createElement("div")), this.element.appendChild(document.createElement("div")), this.element.appendChild(document.createElement("div"));
296
+ const n = this.element.children[0], s = this.element.children[1], a = this.element.children[2];
297
+ n.setAttribute("class", this.lyricPlayer.style.classes.lyricMainLine), s.setAttribute("class", this.lyricPlayer.style.classes.lyricSubLine), a.setAttribute("class", this.lyricPlayer.style.classes.lyricSubLine), this.rebuildElement(), this.rebuildStyle();
298
+ }
299
+ enable() {
300
+ this.element.classList.add("active");
301
+ const t = this.element.children[0];
302
+ this.splittedWords.forEach((e) => {
303
+ e.elementAnimations.forEach((n) => {
304
+ n.currentTime = 0, n.playbackRate = 1, n.play();
305
+ });
306
+ }), t.classList.add("active");
307
+ }
308
+ disable() {
309
+ this.element.classList.remove("active");
310
+ const t = this.element.children[0];
311
+ this.splittedWords.forEach((e) => {
312
+ e.elementAnimations.forEach((n) => {
313
+ n.id === "float-word" && (n.playbackRate = -1, n.play());
314
+ });
315
+ }), t.classList.remove("active");
316
+ }
317
+ setLine(t) {
318
+ this.lyricLine = t, this.lyricLine.isBG ? this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine) : this.element.classList.remove(this.lyricPlayer.style.classes.lyricBgLine), this.lyricLine.isDuet ? this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine) : this.element.classList.remove(
319
+ this.lyricPlayer.style.classes.lyricDuetLine
320
+ ), this.rebuildElement(), this.rebuildStyle();
321
+ }
322
+ getLine() {
323
+ return this.lyricLine;
324
+ }
325
+ show() {
326
+ this._hide = !1, this.rebuildStyle();
327
+ }
328
+ hide() {
329
+ this._hide = !0, this.rebuildStyle();
330
+ }
331
+ rebuildStyle() {
332
+ if (this._hide) {
333
+ this.lastStyle !== "visibility:hidden;transform:translate(0,-10000px);" && (this.lastStyle = "visibility:hidden;transform:translate(0,-10000px);", this.element.setAttribute(
334
+ "style",
335
+ "visibility:hidden;transform:translate(0,-10000px);"
336
+ ));
337
+ return;
338
+ }
339
+ let t = `transform:translate(${this.lineTransforms.posX.getCurrentPosition()}px,${this.lineTransforms.posY.getCurrentPosition()}px) scale(${this.lineTransforms.scale.getCurrentPosition()});`;
340
+ this.lyricPlayer.disableSpring && this.isInSight && (t += `transition-delay:${this.delay}ms;`), t += `filter:blur(${Math.min(32, this.blur)}px);`, t !== this.lastStyle && (this.lastStyle = t, this.element.setAttribute("style", t));
341
+ }
342
+ rebuildElement() {
343
+ const t = this.element.children[0], e = this.element.children[1], n = this.element.children[2];
344
+ this.splittedWords = [], this.lyricLine.words.forEach((r) => {
345
+ if (T.test(r.word))
346
+ this.splittedWords.push({
347
+ ...r,
348
+ width: 0,
349
+ height: 0,
350
+ elements: [],
351
+ elementAnimations: [],
352
+ shouldEmphasize: P(r)
353
+ });
354
+ else {
355
+ const c = /(\s*)(\S*)(\s*)/.exec(r.word);
356
+ c && (c[1].length > 0 && this.splittedWords.push({
357
+ word: " ",
358
+ startTime: 0,
359
+ endTime: 0,
360
+ width: 0,
361
+ height: 0,
362
+ elements: [],
363
+ elementAnimations: [],
364
+ shouldEmphasize: !1
365
+ }), this.splittedWords.push({
366
+ word: c[2],
367
+ startTime: r.startTime,
368
+ endTime: r.endTime,
369
+ width: 0,
370
+ height: 0,
371
+ elements: [],
372
+ elementAnimations: [],
373
+ shouldEmphasize: P(r)
374
+ }), c[3].length > 0 && this.splittedWords.push({
375
+ word: " ",
376
+ startTime: 0,
377
+ endTime: 0,
378
+ width: 0,
379
+ height: 0,
380
+ elements: [],
381
+ elementAnimations: [],
382
+ shouldEmphasize: !1
383
+ }));
384
+ }
385
+ });
386
+ const s = [], a = [];
387
+ function h(r) {
388
+ for (; r.firstChild; )
389
+ r.firstChild.nodeType === Node.ELEMENT_NODE ? s.push(t.firstChild) : r.firstChild.nodeType === Node.TEXT_NODE && a.push(t.firstChild), r.removeChild(r.firstChild), h(r.firstChild);
390
+ }
391
+ h(t);
392
+ let i = null;
393
+ this.splittedWords.forEach((r) => {
394
+ if (r.word.trim().length > 0)
395
+ if (r.shouldEmphasize) {
396
+ const c = s.pop() ?? document.createElement("span");
397
+ c.className = "emphasize", r.elements = [c];
398
+ for (const o of r.word) {
399
+ const m = s.pop() ?? document.createElement("span");
400
+ m.className = "", m.innerText = o, c.appendChild(m), r.elements.push(m);
401
+ }
402
+ if (r.elementAnimations = this.initEmphasizeAnimation(r), console.log(r.word, T.test(r.word)), i && !T.test(r.word))
403
+ if (i.childElementCount > 0)
404
+ i.appendChild(c);
405
+ else {
406
+ const o = s.pop() ?? document.createElement("span");
407
+ o.className = "", i.remove(), o.appendChild(i), o.appendChild(c), t.appendChild(o), i = o;
408
+ }
409
+ else
410
+ i = T.test(r.word) ? null : c, t.appendChild(c);
411
+ } else {
412
+ const c = s.pop() ?? document.createElement("span");
413
+ if (c.className = "", c.innerText = r.word, r.elements = [c], r.elementAnimations.push(this.initFloatAnimation(r, c)), i)
414
+ if (i.childElementCount > 0)
415
+ i.appendChild(c);
416
+ else {
417
+ const o = s.pop() ?? document.createElement("span");
418
+ o.className = "", i.remove(), o.appendChild(i), o.appendChild(c), t.appendChild(o), i = o;
419
+ }
420
+ else
421
+ i = c, t.appendChild(c);
422
+ }
423
+ else if (r.word.length > 0) {
424
+ const c = a.pop() ?? document.createTextNode(" ");
425
+ t.appendChild(c), i = null;
426
+ } else
427
+ i = null;
428
+ }), e.innerText = this.lyricLine.translatedLyric, n.innerText = this.lyricLine.romanLyric;
429
+ }
430
+ initFloatAnimation(t, e) {
431
+ const n = e.animate(
432
+ [
433
+ {
434
+ transform: "translateY(0px)"
435
+ },
436
+ {
437
+ transform: "translateY(-3%)"
438
+ }
439
+ ],
440
+ {
441
+ duration: Math.max(1e3, t.endTime - t.startTime),
442
+ delay: t.startTime - this.lyricLine.startTime,
443
+ id: "float-word",
444
+ composite: "add",
445
+ fill: "both"
446
+ }
447
+ );
448
+ return n.pause(), n;
449
+ }
450
+ initEmphasizeAnimation(t) {
451
+ const e = t.startTime - this.lyricLine.startTime, n = t.endTime - t.startTime;
452
+ return t.elements.map((s, a, h) => {
453
+ if (a === 0)
454
+ return this.initFloatAnimation(t, s);
455
+ {
456
+ const i = s.animate(
457
+ [
458
+ {
459
+ offset: 0,
460
+ transform: "translate3d(0, 0px, 0px)",
461
+ filter: "drop-shadow(0 0 0 var(--amll-lyric-line-color))"
462
+ },
463
+ {
464
+ offset: 0.5,
465
+ transform: "translate3d(0, -2%, 20px)",
466
+ filter: "drop-shadow(0 0 0.2rem var(--amll-lyric-line-color))"
467
+ },
468
+ {
469
+ offset: 1,
470
+ transform: "translate3d(0, 0px, 0)",
471
+ filter: "drop-shadow(0 0 0 var(--amll-lyric-line-color))"
472
+ }
473
+ ],
474
+ {
475
+ duration: Math.max(1e3, t.endTime - t.startTime),
476
+ delay: e + n / (h.length - 1) * (a - 1),
477
+ id: "glow-word",
478
+ iterations: 1,
479
+ composite: "replace",
480
+ fill: "both"
481
+ }
482
+ );
483
+ return i.pause(), i;
484
+ }
485
+ });
486
+ }
487
+ updateMaskImage() {
488
+ this.splittedWords.forEach((t) => {
489
+ const e = t.elements[0];
490
+ if (e) {
491
+ t.width = e.clientWidth, t.height = e.clientHeight;
492
+ const [n, s, a] = F(
493
+ 16 / t.width,
494
+ "rgba(0,0,0,0.75)",
495
+ "rgba(0,0,0,0.25)"
496
+ ), h = `${a * 100}% 100%`;
497
+ this.lyricPlayer.supportMaskImage ? (e.style.maskImage = n, e.style.maskOrigin = "left", e.style.maskSize = h) : (e.style.webkitMaskImage = n, e.style.webkitMaskOrigin = "left", e.style.webkitMaskSize = h);
498
+ const i = t.width + 16, r = `clamp(${-i}px,calc(${-i}px + (var(--amll-player-time) - ${t.startTime})*${i / Math.abs(t.endTime - t.startTime)}px),0px) 0px, left top`;
499
+ e.style.maskPosition = r, e.style.webkitMaskPosition = r;
500
+ }
501
+ });
502
+ }
503
+ getElement() {
504
+ return this.element;
505
+ }
506
+ setTransform(t = this.left, e = this.top, n = this.scale, s = 1, a = 0, h = !1, i = 0) {
507
+ this.left = t, this.top = e, this.scale = n, this.delay = i * 1e3 | 0;
508
+ const r = this.element.children[0];
509
+ r.style.opacity = `${s}`, h || this.lyricPlayer.disableSpring ? (this.blur = Math.min(32, a), h && this.element.classList.add(
510
+ this.lyricPlayer.style.classes.tmpDisableTransition
511
+ ), this.lineTransforms.posX.setPosition(t), this.lineTransforms.posY.setPosition(e), this.lineTransforms.scale.setPosition(n), this.lyricPlayer.disableSpring ? this.show() : this.rebuildStyle(), h && requestAnimationFrame(() => {
512
+ this.element.classList.remove(
513
+ this.lyricPlayer.style.classes.tmpDisableTransition
514
+ );
515
+ })) : (this.lineTransforms.posX.setTargetPosition(t, i), this.lineTransforms.posY.setTargetPosition(e, i), this.lineTransforms.scale.setTargetPosition(n), this.blur !== Math.min(32, a) && (this.blur = Math.min(32, a), this.element.style.filter = `blur(${Math.min(32, a)}px)`));
516
+ }
517
+ update(t = 0) {
518
+ this.lyricPlayer.disableSpring || (this.lineTransforms.posX.update(t), this.lineTransforms.posY.update(t), this.lineTransforms.scale.update(t), this.isInSight ? this.show() : this.hide());
519
+ }
520
+ get isInSight() {
521
+ const t = this.lineTransforms.posX.getCurrentPosition(), e = this.lineTransforms.posY.getCurrentPosition(), n = t + this.lineSize[0], s = e + this.lineSize[1], a = this.lyricPlayer.pos[0], h = this.lyricPlayer.pos[1], i = this.lyricPlayer.pos[0] + this.lyricPlayer.size[0], r = this.lyricPlayer.pos[1] + this.lyricPlayer.size[1];
522
+ return !(t > i || e > r || n < a || s < h);
523
+ }
524
+ dispose() {
525
+ this.element.remove();
526
+ }
527
+ }
528
+ const G = z(I());
529
+ class Z extends EventTarget {
530
+ constructor() {
531
+ super();
532
+ l(this, "element", document.createElement("div"));
533
+ l(this, "currentTime", 0);
534
+ l(this, "lyricLines", []);
535
+ l(this, "processedLines", []);
536
+ l(this, "lyricLinesEl", []);
537
+ l(this, "lyricLinesSize", /* @__PURE__ */ new Map());
538
+ l(this, "hotLines", /* @__PURE__ */ new Set());
539
+ l(this, "bufferedLines", /* @__PURE__ */ new Set());
540
+ l(this, "scrollToIndex", 0);
541
+ l(this, "resizeObserver", new ResizeObserver((e) => {
542
+ const n = e[0].contentRect;
543
+ this.size[0] = n.width, this.size[1] = n.height, this.pos[0] = n.left, this.pos[1] = n.top, this.rebuildStyle(), this.calcLayout(!0), this.lyricLinesEl.forEach((s) => s.updateMaskImage());
544
+ }));
545
+ l(this, "posXSpringParams", {
546
+ mass: 1,
547
+ damping: 10,
548
+ stiffness: 100
549
+ });
550
+ l(this, "posYSpringParams", {
551
+ mass: 1,
552
+ damping: 15,
553
+ stiffness: 100
554
+ });
555
+ l(this, "scaleSpringParams", {
556
+ mass: 1,
557
+ damping: 20,
558
+ stiffness: 100
559
+ });
560
+ l(this, "enableBlur", !0);
561
+ l(this, "interludeDots");
562
+ l(this, "interludeDotsSize", [0, 0]);
563
+ l(this, "supportPlusLighter", CSS.supports("mix-blend-mode", "plus-lighter"));
564
+ l(this, "supportMaskImage", CSS.supports("mask-image", "none"));
565
+ l(this, "disableSpring", !1);
566
+ l(this, "alignAnchor", 0.5);
567
+ l(this, "size", [0, 0]);
568
+ l(this, "pos", [0, 0]);
569
+ l(this, "style", G.createStyleSheet({
570
+ lyricPlayer: {
571
+ userSelect: "none",
572
+ padding: "1rem",
573
+ boxSizing: "border-box",
574
+ fontSize: "max(5vh, 12px)",
575
+ fontWeight: "bold",
576
+ width: "100%",
577
+ height: "100%",
578
+ overflow: "hidden",
579
+ maxWidth: "100%",
580
+ maxHeight: "100%",
581
+ zIndex: 1,
582
+ color: "var(--amll-lyric-line-color)",
583
+ mixBlendMode: "plus-lighter",
584
+ contain: "strict"
585
+ },
586
+ lyricLine: {
587
+ position: "absolute",
588
+ transformOrigin: "left",
589
+ maxWidth: "65%",
590
+ padding: "max(2vh, 1rem) 1rem",
591
+ contain: "content",
592
+ transition: "filter 0.25s",
593
+ margin: "max(2vh, 1rem) -1rem"
594
+ },
595
+ "@media (max-width: 1024px)": {
596
+ lyricLine: {
597
+ maxWidth: "75%",
598
+ padding: "max(1vh, 1rem) 1rem",
599
+ margin: "0 -1rem"
600
+ }
601
+ },
602
+ lyricDuetLine: {
603
+ textAlign: "right",
604
+ transformOrigin: "right"
605
+ },
606
+ lyricBgLine: {
607
+ opacity: 0,
608
+ fontSize: "max(50%, 10px)",
609
+ transition: "opacity 0.25s",
610
+ "&.active": {
611
+ transition: "opacity 0.25s 0.25s",
612
+ opacity: 1
613
+ }
614
+ },
615
+ lyricMainLine: {
616
+ transition: "opacity 0.3s 0.25s",
617
+ margin: "-1rem",
618
+ padding: "1rem",
619
+ "& span": {
620
+ display: "inline-block",
621
+ margin: "-1rem",
622
+ padding: "1rem"
623
+ },
624
+ "& > span": {
625
+ whiteSpace: "pre-wrap",
626
+ wordBreak: "keep-all",
627
+ maxLines: "1",
628
+ "&.emphasize": {
629
+ margin: "-1rem",
630
+ padding: "1rem",
631
+ transformStyle: "preserve-3d",
632
+ perspective: "50vw"
633
+ }
634
+ }
635
+ },
636
+ lyricSubLine: {
637
+ fontSize: "max(50%, 10px)",
638
+ opacity: 0.5
639
+ },
640
+ disableSpring: {
641
+ "& > *": {
642
+ transition: "filter 0.25s, transform 0.5s"
643
+ }
644
+ },
645
+ interludeDots: {
646
+ height: "min(1rem,2.5vh)",
647
+ transformOrigin: "center",
648
+ width: "fit-content",
649
+ padding: "2.5% 0",
650
+ position: "absolute",
651
+ display: "flex",
652
+ gap: "0.5rem",
653
+ "& > *": {
654
+ width: "100%",
655
+ display: "inline-block",
656
+ borderRadius: "50%",
657
+ aspectRatio: "1 / 1",
658
+ backgroundColor: "var(--amll-lyric-line-color)",
659
+ marginRight: "4px"
660
+ },
661
+ "&.duet": {
662
+ right: "1rem",
663
+ transformOrigin: "center"
664
+ }
665
+ },
666
+ "@supports (mix-blend-mode: plus-lighter)": {
667
+ lyricSubLine: {
668
+ opacity: 0.3
669
+ }
670
+ },
671
+ tmpDisableTransition: {
672
+ transition: "none !important"
673
+ }
674
+ }));
675
+ l(this, "onPageShow", () => {
676
+ this.calcLayout(!0);
677
+ });
678
+ this.interludeDots = new $(this), this.element.setAttribute("class", this.style.classes.lyricPlayer), this.disableSpring && this.element.classList.add(this.style.classes.disableSpring), this.rebuildStyle(), this.resizeObserver.observe(this.element), this.element.appendChild(this.interludeDots.getElement()), this.style.attach(), this.interludeDots.setTransform(0, 200), window.addEventListener("pageshow", this.onPageShow);
679
+ }
680
+ /**
681
+ * 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
682
+ *
683
+ * 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
684
+ *
685
+ * 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
686
+ */
687
+ setEnableSpring(e = !0) {
688
+ this.disableSpring = !e, e ? this.element.classList.remove(this.style.classes.disableSpring) : this.element.classList.add(this.style.classes.disableSpring), this.calcLayout(!0);
689
+ }
690
+ /**
691
+ * 获取当前播放时间里是否处于间奏区间
692
+ * 如果是则会返回单位为毫秒的始末时间
693
+ * 否则返回 undefined
694
+ * @returns [开始时间,结束时间] 或 undefined 如果不处于间奏区间
695
+ */
696
+ getCurrentInterlude() {
697
+ var s, a, h;
698
+ if (this.bufferedLines.size > 0)
699
+ return;
700
+ const e = this.currentTime + 20, n = this.scrollToIndex;
701
+ if (n === 0) {
702
+ if ((s = this.processedLines[0]) != null && s.startTime && this.processedLines[0].startTime > e)
703
+ return [0, this.processedLines[0].startTime];
704
+ } else if ((a = this.processedLines[n]) != null && a.endTime && ((h = this.processedLines[n + 1]) != null && h.startTime) && this.processedLines[n + 1].startTime > e && this.processedLines[n].endTime < e)
705
+ return [
706
+ this.processedLines[n].endTime,
707
+ this.processedLines[n + 1].startTime
708
+ ];
709
+ }
710
+ /**
711
+ * 重建样式,这个只允许内部调用
712
+ */
713
+ rebuildStyle() {
714
+ let e = "";
715
+ e += "--amll-lyric-player-width:", e += this.element.clientWidth, e += "px;", e += "--amll-lyric-player-height:", e += this.element.clientHeight, e += "px;", e += "--amll-lyric-line-color:", e += "#FFFFFF;", e += "--amll-player-time:", e += this.currentTime, e += ";", this.element.setAttribute("style", e);
716
+ }
717
+ /**
718
+ * 设置是否启用歌词行的模糊效果
719
+ * @param enable 是否启用
720
+ */
721
+ setEnableBlur(e) {
722
+ this.enableBlur !== e && (this.enableBlur = e, this.calcLayout());
723
+ }
724
+ /**
725
+ * 设置当前播放歌词,要注意传入后这个数组内的信息不得修改,否则会发生错误
726
+ * @param lines 歌词数组
727
+ */
728
+ setLyricLines(e) {
729
+ this.lyricLines = e;
730
+ const n = 750;
731
+ this.processedLines = e.map((s, a, h) => {
732
+ if (s.isBG)
733
+ return {
734
+ ...s
735
+ };
736
+ {
737
+ const i = h[a - 1], r = h[a - 2];
738
+ if (i != null && i.isBG && r) {
739
+ if (r.endTime < s.startTime)
740
+ return {
741
+ ...s,
742
+ startTime: Math.max(r.endTime, s.startTime - n) || s.startTime
743
+ };
744
+ } else if (i != null && i.endTime && i.endTime < s.startTime)
745
+ return {
746
+ ...s,
747
+ startTime: Math.max(i == null ? void 0 : i.endTime, s.startTime - n) || s.startTime
748
+ };
749
+ return {
750
+ ...s
751
+ };
752
+ }
753
+ }), this.processedLines.forEach((s, a, h) => {
754
+ const i = h[a + 1], r = s.words[s.words.length - 1];
755
+ r && P(r) && (i ? i.startTime > s.endTime && (s.endTime = Math.min(s.endTime + 1500, i.startTime)) : s.endTime = s.endTime + 1500);
756
+ }), this.processedLines.forEach((s, a, h) => {
757
+ if (s.isBG)
758
+ return;
759
+ const i = h[a + 1];
760
+ i != null && i.isBG && (i.startTime = Math.min(i.startTime, s.startTime));
761
+ }), this.lyricLinesEl.forEach((s) => s.dispose()), this.lyricLinesEl = this.processedLines.map(
762
+ (s) => new O(this, s)
763
+ ), this.lyricLinesEl.forEach(
764
+ (s) => (this.element.appendChild(s.getElement()), s.updateMaskImage())
765
+ ), this.setLinePosXSpringParams({}), this.setLinePosYSpringParams({}), this.setLineScaleSpringParams({}), this.setCurrentTime(0, !0), this.calcLayout(!0);
766
+ }
767
+ calcLayout(e = !1) {
768
+ e && (this.lyricLinesEl.forEach((o) => {
769
+ const m = [
770
+ o.getElement().clientWidth,
771
+ o.getElement().clientHeight
772
+ ];
773
+ this.lyricLinesSize.set(o, m), o.lineSize = m;
774
+ }), this.interludeDotsSize[0] = this.interludeDots.getElement().clientWidth, this.interludeDotsSize[1] = this.interludeDots.getElement().clientHeight);
775
+ const n = 0.95;
776
+ let a = -this.lyricLinesEl.slice(0, this.scrollToIndex).reduce(
777
+ (o, m) => o + (m.getLine().isBG ? 0 : this.lyricLinesSize.get(m)[1]),
778
+ 0
779
+ );
780
+ if (this.alignAnchor === "bottom") {
781
+ a += this.element.clientHeight / 2;
782
+ const o = this.lyricLinesEl[this.scrollToIndex];
783
+ if (o) {
784
+ const m = this.lyricLinesSize.get(o)[1];
785
+ a -= m / 2;
786
+ }
787
+ } else if (typeof this.alignAnchor == "number") {
788
+ a += this.element.clientHeight * this.alignAnchor;
789
+ const o = this.lyricLinesEl[this.scrollToIndex];
790
+ if (o) {
791
+ const m = this.lyricLinesSize.get(o)[1];
792
+ a -= m / 2;
793
+ }
794
+ }
795
+ const h = this.currentTime === 0 ? void 0 : this.getCurrentInterlude();
796
+ let i = 0;
797
+ if (h && (i = h[1] - h[0], i >= 5e3)) {
798
+ const o = this.currentTime === 0 ? this.lyricLinesEl[0] : this.lyricLinesEl[this.scrollToIndex + 1];
799
+ o && (a -= this.lyricLinesSize.get(o)[1]);
800
+ }
801
+ const r = Math.max(...this.bufferedLines);
802
+ let c = 0;
803
+ this.lyricLinesEl.forEach((o, m) => {
804
+ const p = this.bufferedLines.has(m), g = p || m >= this.scrollToIndex && m < r, u = o.getLine();
805
+ let E = 0;
806
+ u.isDuet && (E = this.size[0] - this.lyricLinesSize.get(o)[0]), m === this.scrollToIndex + 1 && i >= 5e3 && (this.interludeDots.setTransform(0, a), this.interludeDots.setInterlude(this.getCurrentInterlude()), a += this.interludeDotsSize[1]), o.setTransform(
807
+ E,
808
+ a,
809
+ g ? 1 : n,
810
+ p ? 1 : 1 / 3,
811
+ this.enableBlur ? 3 * (g ? 0 : 1 + (m < this.scrollToIndex ? Math.abs(this.scrollToIndex - m) : Math.abs(m - Math.max(this.scrollToIndex, r)))) : 0,
812
+ e,
813
+ c
814
+ ), u.isBG && g ? a += this.lyricLinesSize.get(o)[1] : u.isBG || (a += this.lyricLinesSize.get(o)[1]), a >= 0 && (c += 0.05);
815
+ });
816
+ }
817
+ getCurrentTime() {
818
+ return this.currentTime;
819
+ }
820
+ getLyrics() {
821
+ return this.lyricLines;
822
+ }
823
+ getElement() {
824
+ return this.element;
825
+ }
826
+ /**
827
+ * 设置当前播放进度,单位为毫秒且**必须是整数**,此时将会更新内部的歌词进度信息
828
+ * 内部会根据调用间隔和播放进度自动决定如何滚动和显示歌词,所以这个的调用频率越快越准确越好
829
+ *
830
+ * 调用完成后,可以每帧调用 `update` 函数来执行歌词动画效果
831
+ * @param time 当前播放进度,单位为毫秒
832
+ */
833
+ setCurrentTime(e, n = !1) {
834
+ this.currentTime = e, this.element.style.setProperty("--amll-player-time", `${e}`);
835
+ const s = /* @__PURE__ */ new Set(), a = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set();
836
+ this.hotLines.forEach((i) => {
837
+ const r = this.processedLines[i];
838
+ if (!r.isBG)
839
+ if (r) {
840
+ const c = this.processedLines[i + 1];
841
+ if (c != null && c.isBG) {
842
+ const o = Math.min(r.startTime, c == null ? void 0 : c.startTime), m = Math.max(r.endTime, c == null ? void 0 : c.endTime);
843
+ (o > e || m <= e) && (this.hotLines.delete(i), s.add(i), this.hotLines.delete(i + 1), s.add(i + 1), n && (this.lyricLinesEl[i].disable(), this.lyricLinesEl[i + 1].disable()));
844
+ } else
845
+ (r.startTime > e || r.endTime <= e) && (this.hotLines.delete(i), s.add(i), n && this.lyricLinesEl[i].disable());
846
+ } else
847
+ this.hotLines.delete(i), s.add(i), n && this.lyricLinesEl[i].disable();
848
+ }), this.processedLines.forEach((i, r, c) => {
849
+ var o;
850
+ !i.isBG && i.startTime <= e && i.endTime > e && (this.hotLines.has(r) || (this.hotLines.add(r), h.add(r), n && this.lyricLinesEl[r].enable(), (o = c[r + 1]) != null && o.isBG && (this.hotLines.add(r + 1), h.add(r + 1), n && this.lyricLinesEl[r + 1].enable())));
851
+ }), this.bufferedLines.forEach((i) => {
852
+ this.hotLines.has(i) || (a.add(i), n && this.lyricLinesEl[i].disable());
853
+ }), n ? (this.bufferedLines.size > 0 ? this.scrollToIndex = Math.min(...this.bufferedLines) : this.scrollToIndex = this.processedLines.findIndex(
854
+ (i) => i.startTime >= e
855
+ ), this.bufferedLines.clear(), this.hotLines.forEach((i) => this.bufferedLines.add(i)), this.calcLayout(!0)) : (a.size > 0 || h.size > 0) && (a.size === 0 && h.size > 0 ? (h.forEach((i) => {
856
+ this.bufferedLines.add(i), this.lyricLinesEl[i].enable();
857
+ }), this.scrollToIndex = Math.min(...this.bufferedLines)) : h.size === 0 && a.size > 0 ? D(a, this.bufferedLines) && this.bufferedLines.forEach((i) => {
858
+ this.hotLines.has(i) || (this.bufferedLines.delete(i), this.lyricLinesEl[i].disable());
859
+ }) : (h.forEach((i) => {
860
+ this.bufferedLines.add(i), this.lyricLinesEl[i].enable();
861
+ }), a.forEach((i) => {
862
+ this.bufferedLines.delete(i), this.lyricLinesEl[i].disable();
863
+ }), this.bufferedLines.size > 0 && (this.scrollToIndex = Math.min(...this.bufferedLines))), this.calcLayout());
864
+ }
865
+ /**
866
+ * 更新动画,这个函数应该被逐帧调用
867
+ * @param delta 距离上一次被调用到现在的时长,单位为毫秒(可为浮点数)
868
+ */
869
+ update(e = 0) {
870
+ const n = e / 1e3;
871
+ this.interludeDots.update(e), this.lyricLinesEl.forEach((s) => s.update(n));
872
+ }
873
+ setLinePosXSpringParams(e) {
874
+ this.posXSpringParams = {
875
+ ...this.posXSpringParams,
876
+ ...e
877
+ }, this.lyricLinesEl.forEach(
878
+ (n) => n.lineTransforms.posX.updateParams(this.posXSpringParams)
879
+ );
880
+ }
881
+ setLinePosYSpringParams(e) {
882
+ this.posYSpringParams = {
883
+ ...this.posYSpringParams,
884
+ ...e
885
+ }, this.lyricLinesEl.forEach(
886
+ (n) => n.lineTransforms.posY.updateParams(this.posYSpringParams)
887
+ );
888
+ }
889
+ setLineScaleSpringParams(e) {
890
+ this.scaleSpringParams = {
891
+ ...this.scaleSpringParams,
892
+ ...e
893
+ }, this.lyricLinesEl.forEach(
894
+ (n) => n.lineTransforms.scale.updateParams(this.scaleSpringParams)
895
+ );
896
+ }
897
+ dispose() {
898
+ this.element.remove(), this.resizeObserver.disconnect(), this.style.detach(), this.lyricLinesEl.forEach((e) => e.dispose()), window.removeEventListener("pageshow", this.onPageShow);
899
+ }
900
+ }
901
+ export {
902
+ Q as BackgroundRender,
903
+ Z as LyricPlayer
904
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview
3
+ * 一个播放歌词的组件
4
+ * @author SteveXMH
5
+ */
6
+ import type { HasElement, Disposable } from "../interfaces";
7
+ import { PixiRenderer } from "./pixi-renderer";
8
+ export declare class BackgroundRender extends PixiRenderer implements HasElement, Disposable {
9
+ private element;
10
+ constructor();
11
+ getElement(): HTMLCanvasElement;
12
+ dispose(): void;
13
+ }
@@ -0,0 +1,19 @@
1
+ import { Disposable } from "../interfaces";
2
+ export declare class PixiRenderer implements Disposable {
3
+ private canvas;
4
+ private observer;
5
+ private app;
6
+ private curContainer?;
7
+ private lastContainer;
8
+ private onTick;
9
+ flowSpeed: number;
10
+ private currerntRenderScale;
11
+ constructor(canvas: HTMLCanvasElement);
12
+ setRenderScale(scale: number): void;
13
+ rebuildFilters(): void;
14
+ setFPS(fps: number): void;
15
+ pause(): void;
16
+ resume(): void;
17
+ setAlbumImage(albumUrl: string): Promise<void>;
18
+ dispose(): void;
19
+ }
@@ -0,0 +1,2 @@
1
+ export { BackgroundRender } from "./bg-render";
2
+ export { LyricPlayer } from "./lyric-player";
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 拥有一个 HTML 元素的接口
3
+ *
4
+ * 可以通过 `getElement` 获取这个类所对应的 HTML 元素实例
5
+ */
6
+ export interface HasElement {
7
+ /** 获取这个类所对应的 HTML 元素实例 */
8
+ getElement(): HTMLElement;
9
+ }
10
+ /**
11
+ * 实现了这个接口的东西需要在使用完毕后
12
+ *
13
+ * 手动调用 `dispose` 函数来销毁清除占用资源
14
+ *
15
+ * 以免产生泄露
16
+ */
17
+ export interface Disposable {
18
+ /**
19
+ * 销毁实现了该接口的对象实例,释放占用的资源
20
+ *
21
+ * 一般情况下,调用本函数后就不可以再调用对象的任何函数了
22
+ */
23
+ dispose(): void;
24
+ }
25
+ /** 一个歌词单词 */
26
+ export interface LyricWord {
27
+ /** 单词的起始时间 */
28
+ startTime: number;
29
+ /** 单词的结束时间 */
30
+ endTime: number;
31
+ /** 单词 */
32
+ word: string;
33
+ }
34
+ /** 一行歌词,存储多个单词 */
35
+ export interface LyricLine {
36
+ /**
37
+ * 该行的所有单词
38
+ * 如果是 LyRiC 等只能表达一行歌词的格式,这里就只会有一个单词
39
+ */
40
+ words: LyricWord[];
41
+ translatedLyric: string;
42
+ romanLyric: string;
43
+ /** 句子的起始时间 */
44
+ startTime: number;
45
+ /** 句子的结束时间 */
46
+ endTime: number;
47
+ /** 该行是否为背景歌词行 */
48
+ isBG: boolean;
49
+ /** 该行是否为对唱歌词行(即歌词行靠右对齐) */
50
+ isDuet: boolean;
51
+ }
@@ -0,0 +1,2 @@
1
+ import { LyricLine } from "../interfaces";
2
+ export declare function parseTTML(ttmlText: string): LyricLine[];
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @fileoverview
3
+ * 一个播放歌词的组件
4
+ * @author SteveXMH
5
+ */
6
+ import type { Disposable, HasElement, LyricLine } from "../interfaces";
7
+ import { SpringParams } from "../utils/spring";
8
+ /**
9
+ * 一个播放歌词的组件
10
+ */
11
+ export declare class LyricPlayer extends EventTarget implements HasElement, Disposable {
12
+ private element;
13
+ private currentTime;
14
+ private lyricLines;
15
+ private processedLines;
16
+ private lyricLinesEl;
17
+ private lyricLinesSize;
18
+ private hotLines;
19
+ private bufferedLines;
20
+ private scrollToIndex;
21
+ private resizeObserver;
22
+ private posXSpringParams;
23
+ private posYSpringParams;
24
+ private scaleSpringParams;
25
+ private enableBlur;
26
+ private interludeDots;
27
+ private interludeDotsSize;
28
+ readonly supportPlusLighter: boolean;
29
+ readonly supportMaskImage: boolean;
30
+ disableSpring: boolean;
31
+ alignAnchor: "top" | "bottom" | number;
32
+ readonly size: [number, number];
33
+ readonly pos: [number, number];
34
+ /**
35
+ * 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
36
+ *
37
+ * 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
38
+ *
39
+ * 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
40
+ */
41
+ setEnableSpring(enable?: boolean): void;
42
+ readonly style: import("jss").StyleSheet<"lyricPlayer" | "lyricLine" | "@media (max-width: 1024px)" | "lyricDuetLine" | "lyricBgLine" | "lyricMainLine" | "lyricSubLine" | "disableSpring" | "interludeDots" | "@supports (mix-blend-mode: plus-lighter)" | "tmpDisableTransition">;
43
+ private onPageShow;
44
+ constructor();
45
+ /**
46
+ * 获取当前播放时间里是否处于间奏区间
47
+ * 如果是则会返回单位为毫秒的始末时间
48
+ * 否则返回 undefined
49
+ * @returns [开始时间,结束时间] 或 undefined 如果不处于间奏区间
50
+ */
51
+ getCurrentInterlude(): [number, number] | undefined;
52
+ /**
53
+ * 重建样式,这个只允许内部调用
54
+ */
55
+ rebuildStyle(): void;
56
+ /**
57
+ * 设置是否启用歌词行的模糊效果
58
+ * @param enable 是否启用
59
+ */
60
+ setEnableBlur(enable: boolean): void;
61
+ /**
62
+ * 设置当前播放歌词,要注意传入后这个数组内的信息不得修改,否则会发生错误
63
+ * @param lines 歌词数组
64
+ */
65
+ setLyricLines(lines: LyricLine[]): void;
66
+ calcLayout(reflow?: boolean): void;
67
+ getCurrentTime(): number;
68
+ getLyrics(): LyricLine[];
69
+ getElement(): HTMLElement;
70
+ /**
71
+ * 设置当前播放进度,单位为毫秒且**必须是整数**,此时将会更新内部的歌词进度信息
72
+ * 内部会根据调用间隔和播放进度自动决定如何滚动和显示歌词,所以这个的调用频率越快越准确越好
73
+ *
74
+ * 调用完成后,可以每帧调用 `update` 函数来执行歌词动画效果
75
+ * @param time 当前播放进度,单位为毫秒
76
+ */
77
+ setCurrentTime(time: number, isSeek?: boolean): void;
78
+ /**
79
+ * 更新动画,这个函数应该被逐帧调用
80
+ * @param delta 距离上一次被调用到现在的时长,单位为毫秒(可为浮点数)
81
+ */
82
+ update(delta?: number): void;
83
+ setLinePosXSpringParams(params: Partial<SpringParams>): void;
84
+ setLinePosYSpringParams(params: Partial<SpringParams>): void;
85
+ setLineScaleSpringParams(params: Partial<SpringParams>): void;
86
+ dispose(): void;
87
+ }
@@ -0,0 +1,22 @@
1
+ import { LyricPlayer } from ".";
2
+ import type { Disposable, HasElement } from "../interfaces";
3
+ export declare class InterludeDots implements HasElement, Disposable {
4
+ private readonly lyricPlayer;
5
+ private element;
6
+ private dot0;
7
+ private dot1;
8
+ private dot2;
9
+ private left;
10
+ private top;
11
+ private scale;
12
+ private lastStyle;
13
+ private currentInterlude?;
14
+ private currentTime;
15
+ private targetBreatheDuration;
16
+ constructor(lyricPlayer: LyricPlayer);
17
+ getElement(): HTMLElement;
18
+ setTransform(left?: number, top?: number): void;
19
+ setInterlude(interlude?: [number, number]): void;
20
+ update(delta?: number): void;
21
+ dispose(): void;
22
+ }
@@ -0,0 +1,40 @@
1
+ import { LyricPlayer } from ".";
2
+ import { Disposable, HasElement, LyricLine, LyricWord } from "../interfaces";
3
+ import { Spring } from "../utils/spring";
4
+ export declare function shouldEmphasize(word: LyricWord): boolean;
5
+ export declare class LyricLineEl implements HasElement, Disposable {
6
+ private lyricPlayer;
7
+ private lyricLine;
8
+ private element;
9
+ private left;
10
+ private top;
11
+ private scale;
12
+ private blur;
13
+ private delay;
14
+ private splittedWords;
15
+ lineSize: number[];
16
+ readonly lineTransforms: {
17
+ posX: Spring;
18
+ posY: Spring;
19
+ scale: Spring;
20
+ };
21
+ constructor(lyricPlayer: LyricPlayer, lyricLine?: LyricLine);
22
+ enable(): void;
23
+ disable(): void;
24
+ setLine(line: LyricLine): void;
25
+ getLine(): LyricLine;
26
+ private _hide;
27
+ private lastStyle;
28
+ show(): void;
29
+ hide(): void;
30
+ rebuildStyle(): void;
31
+ rebuildElement(): void;
32
+ private initFloatAnimation;
33
+ private initEmphasizeAnimation;
34
+ updateMaskImage(): void;
35
+ getElement(): HTMLElement;
36
+ setTransform(left?: number, top?: number, scale?: number, opacity?: number, blur?: number, force?: boolean, delay?: number): void;
37
+ update(delta?: number): void;
38
+ get isInSight(): boolean;
39
+ dispose(): void;
40
+ }
@@ -0,0 +1 @@
1
+ export declare const eqSet: <T>(xs: Set<T>, ys: Set<T>) => boolean;
@@ -0,0 +1,25 @@
1
+ /** MIT License github.com/pushkine/ */
2
+ export interface SpringParams {
3
+ mass: number;
4
+ damping: number;
5
+ stiffness: number;
6
+ soft: boolean;
7
+ }
8
+ export declare class Spring {
9
+ private currentPosition;
10
+ private targetPosition;
11
+ private currentTime;
12
+ private params;
13
+ private currentSolver;
14
+ private getV;
15
+ private queueParams;
16
+ private queuePosition;
17
+ constructor(currentPosition?: number);
18
+ private resetSolver;
19
+ arrived(): boolean;
20
+ setPosition(targetPosition: number): void;
21
+ update(delta?: number): void;
22
+ updateParams(params: Partial<SpringParams>, delay?: number): void;
23
+ setTargetPosition(targetPosition: number, delay?: number): void;
24
+ getCurrentPosition(): number;
25
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@applemusic-like-lyrics/core",
3
+ "packageManager": "yarn@3.6.1",
4
+ "version": "0.0.1",
5
+ "files": [
6
+ "dist"
7
+ ],
8
+ "main": "./dist/amll-core.cjs",
9
+ "module": "./dist/amll-core.mjs",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/amll-core.mjs",
13
+ "require": "./dist/amll-core.cjs"
14
+ }
15
+ },
16
+ "devDependencies": {
17
+ "@types/stats.js": "^0.17.0",
18
+ "lil-gui": "^0.18.2",
19
+ "rome": "^12.1.3",
20
+ "stats.js": "^0.17.0",
21
+ "vite": "^4.4.6",
22
+ "vite-plugin-dts": "^3.3.1"
23
+ },
24
+ "dependencies": {
25
+ "jss": "^10.10.0",
26
+ "jss-preset-default": "^10.10.0",
27
+ "pixi.js": "^7.2.4"
28
+ }
29
+ }