UNPKG

vue-danmaku

Version:

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

2 lines (1 loc) 9.76 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,s){const i=-1*(s||-1);e.style.transform="translateX(0px)",e.style[i<0?"right":"left"]=`${l}px`;const d=performance.now(),c=l/o*1e3,v=l*i,f=-t*i;a.set(e,v);const m=requestAnimationFrame((function t(l){if(u())return;const o=l-d;if(o>=c)return void r(e);const s=v+(f-v)*(o/c);e.style.transform=`translateX(${s-v}px)`,a.set(e,s);const i=requestAnimationFrame(t);n.set(e,i)}));n.set(e,m)}function o(e,t,o,u,r,s,i){const d=a.get(e);if(void 0===d)return void l(e,t,o,u,r,s);const c=-1*(i||-1),v=o*c,f=-t*c,m=o/u*1e3,p=m*((v-d)/(v-f)),h=performance.now()-p;const y=requestAnimationFrame((function t(l){if(r())return;const o=l-h;if(o>=m)return void s(e);const u=v+(f-v)*(o/m);e.style.transform=`translateX(${u-v}px)`,a.set(e,u);const i=requestAnimationFrame(t);n.set(e,i)}));n.set(e,y)}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},mirror:{type:Boolean,default:!1}},emits:["list-end","play-end","dm-over","dm-out","dm-click","dm-remove","error"],setup(e,{emit:a,slots:s,expose:i}){let d=t.ref(document.createElement("div")),c=t.ref(document.createElement("div"));const v=t.ref(0),f=t.ref(0),m=t.computed((()=>e.mirror?1:-1)),p=t.computed((()=>e.mirror?"right":"left"));let h=0;const y=t.ref(0),g=t.ref(0),x=t.ref(0),_=t.ref(!1),E=t.ref(!0),C=t.ref({}),b=t.ref([...e.danmus]),M=t.ref(new Set);let B=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 L=t.reactive({channels:t.computed((()=>e.channels||y.value)),debounce:t.computed((()=>e.debounce)),randomChannel:t.computed((()=>e.randomChannel))}),k=t.reactive({height:t.computed((()=>g.value)),speeds:t.computed((()=>e.speeds)),top:t.computed((()=>e.top)),right:t.computed((()=>e.right))});function w(){if(v.value=d.value.offsetWidth,f.value=d.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(){E.value&&(E.value=!1,h||(h=window.setInterval((()=>function(){if(!E.value&&b.value.length)if(x.value>b.value.length-1){const t=c.value.children.length;e.loop&&(t<x.value&&(a("list-end"),x.value=0,e.loopOnly&&M.value.clear()),N())}else N()}()),L.debounce)),e.performanceMode&&function(){if(c.value){Array.from(c.value.querySelectorAll(".dm")).forEach((e=>{o(e,e.offsetWidth,v.value,k.speeds,(()=>E.value),O,m.value)}))}}())}function N(n){const o=e.loop?x.value%b.value.length:x.value;if(e.loopOnly&&M.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||b.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((()=>{k.height||(g.value=u.offsetHeight),L.channels||(y.value=Math.floor(f.value/(k.height+k.top))),function(t,n){let o=function(e){let t=[...Array(L.channels).keys()];L.randomChannel&&(t=t.sort((()=>.5-Math.random())));for(let n of t){const t=C.value[n];if(!t||!t.length)return C.value[n]=[e],n%L.channels;for(let a=0;a<t.length;a++){const l=I(t[a])-10;if(l<=.88*(e.offsetWidth-t[a].offsetWidth)||l<=0)break;if(a===t.length-1)return C.value[n].push(e),n%L.channels}}return-1}(t);if(o>=0){const u=t.offsetWidth,r=k.height;t.dataset.index=`${n}`,t.dataset.channel=o.toString(),t.style.opacity="1",t.style.top=o*(r+k.top)+"px",t.style.width=u+k.right+"px",t.style.zIndex=e.zIndex.toString();const s=e=>{a("dm-click",{el:t,index:n,danmu:b.value[n],event:e})};if(t.addEventListener("click",s),t._clickHandler=s,e.loop&&e.loopOnly&&M.value.add(n),e.performanceMode)l(t,u,v.value,k.speeds,(()=>E.value),O,m.value);else{t.classList.add("move"),t.style.setProperty("--dm-scroll-width",(v.value+u)*m.value+"px"),t.style[p.value]=`${v.value}px`,t.style.animationDuration=v.value/k.speeds+"s";const n=()=>{Number(t.dataset.index)!==b.value.length-1||e.loop||a("play-end",t.dataset.index);const n=Number(t.dataset.index);e.loop&&e.loopOnly&&n>=0&&M.value.delete(n),a("dm-remove",{el:t,index:n,danmu:n>=0?b.value[n]:null}),D(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)};t._animationEndHandler=n,t.addEventListener("animationend",n)}x.value++}else D(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)}(u,o)}))}function I(t){return e.mirror?function(e){const t=e.offsetWidth||parseInt(e.style.width),n=e.getBoundingClientRect().left||c.value.getBoundingClientRect().left+t;return c.value.getBoundingClientRect().left+n}(t):function(e){const t=e.offsetWidth||parseInt(e.style.width),n=e.getBoundingClientRect().right||c.value.getBoundingClientRect().right+t;return c.value.getBoundingClientRect().right-n}(t)}function R(){clearInterval(h),h=0,x.value=0,M.value.clear()}function z(){if(R(),c.value){Array.from(c.value.querySelectorAll(".dm")).forEach((e=>{D(e)})),c.value.innerHTML=""}e.performanceMode&&r(),C.value={},E.value=!0}function O(t){Number(t.dataset.index)!==b.value.length-1||e.loop||a("play-end",t.dataset.index);const n=Number(t.dataset.index);e.loop&&e.loopOnly&&n>=0&&M.value.delete(n),a("dm-remove",{el:t,index:x,danmu:n>=0?b.value[n]:null}),D(t),c.value&&t.parentNode===c.value&&c.value.removeChild(t)}function S(){const t=v.value;w();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-d.value.getBoundingClientRect().left))/(t+r);if(e=Math.max(0,Math.min(1,e)),u(l),e>=1)O(l);else{const t=v.value-(v.value+r)*e;l.style[p.value]=`${v.value}px`,l.style.transform=`translateX(${t-v.value}px)`,o(l,r,v.value,k.speeds,(()=>E.value),O,m.value)}}else l.style.setProperty("--dm-scroll-width",(v.value+r)*m.value+"px"),l.style[p.value]=`${v.value}px`,l.style.animationDuration=v.value/k.speeds+"s"}}t.onMounted((()=>{!function(){w(),e.isSuspend&&(c.value.addEventListener("mouseover",H),c.value.addEventListener("mouseout",W)),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)B=new ResizeObserver((()=>{S()})),B.observe(d.value);else{const e=()=>S();window.addEventListener("resize",e),t.onBeforeUnmount((()=>{window.removeEventListener("resize",e)}))}}()})),t.onBeforeUnmount((()=>{z(),c.value&&(c.value.removeEventListener("mouseover",H),c.value.removeEventListener("mouseout",W)),B&&(B.disconnect(),B=null),e.performanceMode&&r()}));let $=[];function H(t){let n=t.target;n.classList.contains("dm")||(n=n.closest(".dm")||n),n.classList.contains("dm")&&($.includes(n)||(a("dm-over",{el:n}),n.style.zIndex=(e.zIndex+1).toString(),e.performanceMode?u(n):n.classList.add("pause"),$.push(n)))}function W(e){let t=e.target;t.classList.contains("dm")||(t=t.closest(".dm")||t),t.classList.contains("dm")&&(a("dm-out",{el:t}),q(t),$.forEach(q),$=[])}function q(t){if(t.style.zIndex=e.zIndex.toString(),e.performanceMode){o(t,t.offsetWidth,v.value,k.speeds,(()=>E.value),O,m.value)}else t.classList.remove("pause")}function D(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&&C.value[n]){const e=C.value[n].indexOf(t);e>-1&&C.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:d,dmContainer:c,hidden:_,paused:E,danmuList:b,getPlayState:function(){return!E.value},getMaxChannels:function(){return k.height?Math.floor(f.value/(k.height+k.top)):0},resize:S,play:A,pause:function(){E.value=!0,e.performanceMode&&(n.forEach((e=>{cancelAnimationFrame(e)})),n.clear())},stop:z,show:function(){_.value=!1},hide:function(){_.value=!0},clear:R,reset:function(){z(),g.value=0,_.value=!1,w()},addDanmu:function(e,t="current"){if("current"===t){if(x.value===b.value.length)return b.value.push(e),b.value.length-1;{const t=x.value%b.value.length;return b.value.splice(t,0,e),t+1}}return b.value.push(e),b.value.length-1},insert:N}}});const i={ref:"container",class:"vue-danmaku"};s.render=function(e,n,a,l,o,u){return t.openBlock(),t.createElementBlock("div",i,[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})}));