UNPKG

vue-danmaku

Version:

基于 Vue 的弹幕交互组件 | A danmaku component for Vue

2 lines (1 loc) 9.32 kB
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).VueDanmaku={},e.Vue)}(this,(function(e,t){"use strict";const n=new Map,a=new Map;function l(e,t,l,o,u,r){e.style.transform="translateX(0px)",e.style.left=`${l}px`;const s=performance.now(),d=l/o*1e3,i=l,c=-t;a.set(e,i);const v=requestAnimationFrame((function t(l){if(u())return;const o=l-s;if(o>=d)return void r(e);const v=i+(c-i)*(o/d);e.style.transform=`translateX(${v-i}px)`,a.set(e,v);const f=requestAnimationFrame(t);n.set(e,f)}));n.set(e,v)}function o(e,t,o,u,r,s){const d=a.get(e);if(void 0===d)return void l(e,t,o,u,r,s);const i=o,c=-t,v=o/u*1e3,f=v*((i-d)/(i-c)),m=performance.now()-f;const p=requestAnimationFrame((function t(l){if(r())return;const o=l-m;if(o>=v)return void s(e);const u=i+(c-i)*(o/v);e.style.transform=`translateX(${u-i}px)`,a.set(e,u);const d=requestAnimationFrame(t);n.set(e,d)}));n.set(e,p)}function u(e){const t=n.get(e);t&&(cancelAnimationFrame(t),n.delete(e))}function r(){n.forEach((e=>{cancelAnimationFrame(e)})),n.clear(),a.clear()}var s=t.defineComponent({name:"vue-danmaku",components:{},props:{danmus:{type:Array,required:!0,default:()=>[]},channels:{type:Number,default:0},autoplay:{type:Boolean,default:!0},loop:{type:Boolean,default:!1},loopOnly:{type:Boolean,default:!1},randomChannel:{type:Boolean,default:!1},isSuspend:{type:Boolean,default:!1},performanceMode:{type:Boolean,default:!0},debounce:{type:Number,default:100},speeds:{type:Number,default:200},top:{type:Number,default:4},right:{type:Number,default:0},zIndex:{type:Number,default:1},autoResize:{type:Boolean,default:!0}},emits:["list-end","play-end","dm-over","dm-out","dm-click","dm-remove","error"],setup(e,{emit:a,slots:s,expose:d}){let i=t.ref(document.createElement("div")),c=t.ref(document.createElement("div"));const v=t.ref(0),f=t.ref(0);let m=0;const p=t.ref(0),h=t.ref(0),y=t.ref(0),g=t.ref(!1),x=t.ref(!0),_=t.ref({}),E=t.ref([...e.danmus]),b=t.ref(new Set);let N=null;!function(e,n,a="modelValue",l){t.computed({get:()=>e[a],set:e=>{n(`update:${a}`,l?l(e):e)}})}(e,a,"danmus");const M=t.reactive({channels:t.computed((()=>e.channels||p.value)),debounce:t.computed((()=>e.debounce)),randomChannel:t.computed((()=>e.randomChannel))}),C=t.reactive({height:t.computed((()=>h.value)),speeds:t.computed((()=>e.speeds)),top:t.computed((()=>e.top)),right:t.computed((()=>e.right))});function k(){if(v.value=i.value.offsetWidth,f.value=i.value.offsetHeight,0===v.value||0===f.value){const e="获取不到容器宽高";a("error",{message:e,code:"CONTAINER_SIZE_ERROR"}),console.error(`[vue-danmaku] ${e}`)}}function A(){x.value&&(x.value=!1,m||(m=window.setInterval((()=>function(){if(!x.value&&E.value.length)if(y.value>E.value.length-1){const t=c.value.children.length;e.loop&&(t<y.value&&(a("list-end"),y.value=0,e.loopOnly&&b.value.clear()),w())}else w()}()),M.debounce)),e.performanceMode&&function(){if(c.value){Array.from(c.value.querySelectorAll(".dm")).forEach((e=>{o(e,e.offsetWidth,v.value,C.speeds,(()=>x.value),z)}))}}())}function w(n){const o=e.loop?y.value%E.value.length:y.value;if(e.loopOnly&&b.value.has(o))return;const u=function(e,n){const a=t.createApp({render:()=>t.h("div",{},[s.danmu&&s.danmu({danmu:e,index:n})])}),l=a.mount(document.createElement("div"));return l.$el.__vueApp=a,l}(n||E.value[o],o).$el;u.classList.add("dm"),c.value.appendChild(u),u.style.opacity="0",u._vueInstance={instance:u.__vueParentComponent||{ctx:{}},el:u},t.nextTick((()=>{C.height||(h.value=u.offsetHeight),M.channels||(p.value=Math.floor(f.value/(C.height+C.top))),function(t,n){let o=function(e){let t=[...Array(M.channels).keys()];M.randomChannel&&(t=t.sort((()=>.5-Math.random())));for(let n of t){const t=_.value[n];if(!t||!t.length)return _.value[n]=[e],n%M.channels;for(let a=0;a<t.length;a++){const l=B(t[a])-10;if(l<=.88*(e.offsetWidth-t[a].offsetWidth)||l<=0)break;if(a===t.length-1)return _.value[n].push(e),n%M.channels}}return-1}(t);if(o>=0){const u=t.offsetWidth,r=C.height;t.dataset.index=`${n}`,t.dataset.channel=o.toString(),t.style.opacity="1",t.style.top=o*(r+C.top)+"px",t.style.width=u+C.right+"px",t.style.zIndex=e.zIndex.toString();const s=e=>{a("dm-click",{el:t,index:n,danmu:E.value[n],event:e})};if(t.addEventListener("click",s),t._clickHandler=s,e.loop&&e.loopOnly&&b.value.add(n),e.performanceMode)l(t,u,v.value,C.speeds,(()=>x.value),z);else{t.classList.add("move"),t.style.setProperty("--dm-scroll-width",`-${v.value+u}px`),t.style.left=`${v.value}px`,t.style.animationDuration=v.value/C.speeds+"s";const n=()=>{Number(t.dataset.index)!==E.value.length-1||e.loop||a("play-end",t.dataset.index);const n=Number(t.dataset.index);e.loop&&e.loopOnly&&n>=0&&b.value.delete(n),a("dm-remove",{el:t,index:n,danmu:n>=0?E.value[n]:null}),q(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)};t._animationEndHandler=n,t.addEventListener("animationend",n)}y.value++}else q(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)}(u,o)}))}function B(e){const t=e.offsetWidth||parseInt(e.style.width),n=e.getBoundingClientRect().right||c.value.getBoundingClientRect().right+t;return c.value.getBoundingClientRect().right-n}function L(){clearInterval(m),m=0,y.value=0,b.value.clear()}function I(){if(L(),c.value){Array.from(c.value.querySelectorAll(".dm")).forEach((e=>{q(e)})),c.value.innerHTML=""}e.performanceMode&&r(),_.value={},x.value=!0}function z(t){Number(t.dataset.index)!==E.value.length-1||e.loop||a("play-end",t.dataset.index);const n=Number(t.dataset.index);e.loop&&e.loopOnly&&n>=0&&b.value.delete(n),a("dm-remove",{el:t,index:y,danmu:n>=0?E.value[n]:null}),q(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)}function $(){const t=v.value;k();const n=c.value.getElementsByClassName("dm");for(let a=0;a<n.length;a++){const l=n[a],r=l.offsetWidth;if(e.performanceMode){let e=(t-(l.getBoundingClientRect().left-i.value.getBoundingClientRect().left))/(t+r);if(e=Math.max(0,Math.min(1,e)),u(l),e>=1)z(l);else{const t=v.value-(v.value+r)*e;l.style.left=`${v.value}px`,l.style.transform=`translateX(${t-v.value}px)`,o(l,r,v.value,C.speeds,(()=>x.value),z)}}else l.style.setProperty("--dm-scroll-width",`-${v.value+r}px`),l.style.left=`${v.value}px`,l.style.animationDuration=v.value/C.speeds+"s"}}t.onMounted((()=>{!function(){k(),e.isSuspend&&(c.value.addEventListener("mouseover",R),c.value.addEventListener("mouseout",S)),s.danmu||(a("error",{message:'没有提供弹幕插槽内容(slot="danmu"),无法展示弹幕',code:"NO_DANMU_SLOT"}),console.error('[vue-danmaku] 警告:没有提供弹幕插槽内容(slot="danmu"),无法展示弹幕'));e.autoplay&&A()}(),e.autoResize&&function(){if("undefined"!=typeof ResizeObserver)N=new ResizeObserver((()=>{$()})),N.observe(i.value);else{const e=()=>$();window.addEventListener("resize",e),t.onBeforeUnmount((()=>{window.removeEventListener("resize",e)}))}}()})),t.onBeforeUnmount((()=>{I(),c.value&&(c.value.removeEventListener("mouseover",R),c.value.removeEventListener("mouseout",S)),N&&(N.disconnect(),N=null),e.performanceMode&&r()}));let O=[];function R(t){let n=t.target;n.className.includes("dm")||(n=n.closest(".dm")||n),n.className.includes("dm")&&(O.includes(n)||(a("dm-over",{el:n}),n.style.zIndex=(e.zIndex+1).toString(),e.performanceMode?u(n):n.classList.add("pause"),O.push(n)))}function S(e){let t=e.target;t.className.includes("dm")||(t=t.closest(".dm")||t),t.className.includes("dm")&&(a("dm-out",{el:t}),H(t),O.forEach(H),O=[])}function H(t){if(t.style.zIndex=e.zIndex.toString(),e.performanceMode){o(t,t.offsetWidth,v.value,C.speeds,(()=>x.value),z)}else t.classList.remove("pause")}function q(t){e.performanceMode?u(t):t._animationEndHandler&&(t.removeEventListener("animationend",t._animationEndHandler),delete t._animationEndHandler),t._clickHandler&&(t.removeEventListener("click",t._clickHandler),delete t._clickHandler);const n=t.dataset.channel?parseInt(t.dataset.channel):-1;if(n>=0&&_.value[n]){const e=_.value[n].indexOf(t);e>-1&&_.value[n].splice(e,1)}if(t.__vueApp){try{t.__vueApp.unmount()}catch(e){console.warn("卸载组件实例失败",e)}delete t.__vueApp}t._vueInstance&&delete t._vueInstance}return{container:i,dmContainer:c,hidden:g,paused:x,danmuList:E,getPlayState:function(){return!x.value},getMaxChannels:function(){return C.height?Math.floor(f.value/(C.height+C.top)):0},resize:$,play:A,pause:function(){x.value=!0,e.performanceMode&&(n.forEach((e=>{cancelAnimationFrame(e)})),n.clear())},stop:I,show:function(){g.value=!1},hide:function(){g.value=!0},clear:L,reset:function(){I(),h.value=0,g.value=!1,k()},addDanmu:function(e,t="current"){if("current"===t){if(y.value===E.value.length)return E.value.push(e),E.value.length-1;{const t=y.value%E.value.length;return E.value.splice(t,0,e),t+1}}return E.value.push(e),E.value.length-1},insert:w}}});const d={ref:"container",class:"vue-danmaku"};s.render=function(e,n,a,l,o,u){return t.openBlock(),t.createElementBlock("div",d,[t.createElementVNode("div",{ref:"dmContainer",class:t.normalizeClass(["danmus",{show:!e.hidden},{paused:e.paused}])},null,2),t.renderSlot(e.$slots,"default")],512)},s.__file="src/lib/Danmaku.vue",e.Danmaku=s,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})}));