vue3-danmaku
Version:
基于 Vue3 的弹幕交互组件
2 lines (1 loc) • 6.64 kB
JavaScript
import{computed as e,defineComponent as t,ref as n,reactive as a,onMounted as l,onBeforeUnmount as u,nextTick as s,createApp as o,h as i,openBlock as d,createElementBlock as r,createElementVNode as m,normalizeClass as v,renderSlot as c}from"vue";var f=t({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},useSlot:{type:Boolean,default:!1},debounce:{type:Number,default:100},speeds:{type:Number,default:200},randomChannel:{type:Boolean,default:!1},fontSize:{type:Number,default:18},top:{type:Number,default:4},right:{type:Number,default:0},isSuspend:{type:Boolean,default:!1},extraStyle:{type:String,default:""}},emits:["list-end","play-end","dm-over","dm-out","update:danmus"],setup(t,{emit:d,slots:r}){let m=n(document.createElement("div")),v=n(document.createElement("div"));const c=n(0),f=n(0);let p=0;const h=n(0),y=n(0),g=n(0),w=n(!1),b=n(!1),x=n({}),S=function(t,n,a="modelValue",l){return e({get:()=>t[a],set:e=>{n(`update:${a}`,l?l(e):e)}})}(t,d,"danmus"),k=a({channels:e((()=>t.channels||h.value)),autoplay:e((()=>t.autoplay)),loop:e((()=>t.loop)),useSlot:e((()=>t.useSlot)),debounce:e((()=>t.debounce)),randomChannel:e((()=>t.randomChannel))}),C=a({height:e((()=>y.value)),fontSize:e((()=>t.fontSize)),speeds:e((()=>t.speeds)),top:e((()=>t.top)),right:e((()=>t.right))});function L(){E(),t.isSuspend&&function(){let e=[];v.value.addEventListener("mouseover",(t=>{let n=t.target;n.className.includes("dm")||(n=n.closest(".dm")||n),n.className.includes("dm")&&(e.includes(n)||(d("dm-over",{el:n}),n.classList.add("pause"),e.push(n)))})),v.value.addEventListener("mouseout",(t=>{let n=t.target;n.className.includes("dm")||(n=n.closest(".dm")||n),n.className.includes("dm")&&(d("dm-out",{el:n}),n.classList.remove("pause"),e.forEach((e=>{e.classList.remove("pause")})),e=[])}))}(),k.autoplay&&N()}function E(){if(c.value=m.value.offsetWidth,f.value=m.value.offsetHeight,0===c.value||0===f.value)throw new Error("获取不到容器宽高")}function N(){b.value=!1,p||(p=window.setInterval((()=>function(){if(!b.value&&S.value.length)if(g.value>S.value.length-1){const e=v.value.children.length;k.loop&&(e<g.value&&(d("list-end"),g.value=0),B())}else B()}()),k.debounce))}function B(e){const n=k.loop?g.value%S.value.length:g.value,a=e||S.value[n];let l=document.createElement("div");k.useSlot?l=function(e,t){const n=o({render:()=>i("div",{},[r.dm&&r.dm({danmu:e,index:t})])});return n.mount(document.createElement("div"))}(a,n).$el:(l.innerHTML=a,l.setAttribute("style",t.extraStyle),l.style.fontSize=`${C.fontSize}px`,l.style.lineHeight=`${C.fontSize}px`),l.classList.add("dm"),v.value.appendChild(l),l.style.opacity="0",s((()=>{C.height||(y.value=l.offsetHeight),k.channels||(h.value=Math.floor(f.value/(C.height+C.top)));let e=function(e){let t=[...Array(k.channels).keys()];k.randomChannel&&(t=t.sort((()=>.5-Math.random())));for(let n of t){const t=x.value[n];if(!t||!t.length)return x.value[n]=[e],e.addEventListener("animationend",(()=>x.value[n].splice(0,1))),n%k.channels;for(let a=0;a<t.length;a++){const l=$(t[a])-10;if(l<=.88*(e.offsetWidth-t[a].offsetWidth)||l<=0)break;if(a===t.length-1)return x.value[n].push(e),e.addEventListener("animationend",(()=>x.value[n].splice(0,1))),n%k.channels}}return-1}(l);if(e>=0){const t=l.offsetWidth,a=C.height;l.classList.add("move"),l.dataset.index=`${n}`,l.dataset.channel=e.toString(),l.style.opacity="1",l.style.top=e*(a+C.top)+"px",l.style.width=t+C.right+"px",l.style.setProperty("--dm-scroll-width",`-${c.value+t}px`),l.style.left=`${c.value}px`,l.style.animationDuration=c.value/C.speeds+"s",l.addEventListener("animationend",(()=>{Number(l.dataset.index)!==S.value.length-1||k.loop||d("play-end",l.dataset.index),v.value&&v.value.removeChild(l)})),g.value++}else v.value.removeChild(l)}))}function $(e){const t=e.offsetWidth||parseInt(e.style.width),n=e.getBoundingClientRect().right||v.value.getBoundingClientRect().right+t;return v.value.getBoundingClientRect().right-n}function z(){clearInterval(p),p=0,g.value=0}return l((()=>{L()})),u((()=>{z()})),{container:m,dmContainer:v,hidden:w,paused:b,danmuList:S,getPlayState:function(){return!b.value},resize:function(){E();const e=v.value.getElementsByClassName("dm");for(let t=0;t<e.length;t++){const n=e[t];n.style.setProperty("--dm-scroll-width",`-${c.value+n.offsetWidth}px`),n.style.left=`${c.value}px`,n.style.animationDuration=c.value/C.speeds+"s"}},play:N,pause:function(){b.value=!0},stop:function(){x.value={},v.value.innerHTML="",b.value=!0,w.value=!1,z()},show:function(){w.value=!1},hide:function(){w.value=!0},reset:function(){y.value=0,L()},add:function(e){if(g.value===S.value.length)return S.value.push(e),S.value.length-1;{const t=g.value%S.value.length;return S.value.splice(t,0,e),t+1}},push:function(e){return S.value.push(e),S.value.length-1},insert:B}}});const p={ref:"container",class:"vue-danmaku"};!function(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],l=document.createElement("style");l.type="text/css","top"===n&&a.firstChild?a.insertBefore(l,a.firstChild):a.appendChild(l),l.styleSheet?l.styleSheet.cssText=e:l.appendChild(document.createTextNode(e))}}(".vue-danmaku {\n position: relative;\n overflow: hidden;\n}\n.vue-danmaku .danmus {\n position: absolute;\n left: 0;\n top: 0;\n width: 100%;\n height: 100%;\n opacity: 0;\n -webkit-transition: all 0.3s;\n transition: all 0.3s;\n}\n.vue-danmaku .danmus.show {\n opacity: 1;\n}\n.vue-danmaku .danmus.paused .dm.move {\n animation-play-state: paused;\n}\n.vue-danmaku .danmus .dm {\n position: absolute;\n font-size: 20px;\n color: #ddd;\n white-space: pre;\n transform: translateX(0);\n transform-style: preserve-3d;\n}\n.vue-danmaku .danmus .dm.move {\n will-change: transform;\n animation-name: moveLeft;\n animation-timing-function: linear;\n animation-play-state: running;\n}\n.vue-danmaku .danmus .dm.pause {\n animation-play-state: paused;\n z-index: 100;\n}\n@keyframes moveLeft {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(var(--dm-scroll-width));\n }\n}\n@-webkit-keyframes moveLeft {\n from {\n -webkit-transform: translateX(0);\n }\n to {\n -webkit-transform: translateX(var(--dm-scroll-width));\n }\n}"),f.render=function(e,t,n,a,l,u){return d(),r("div",p,[m("div",{ref:"dmContainer",class:v(["danmus",{show:!e.hidden},{paused:e.paused}])},null,2),c(e.$slots,"default")],512)},f.__file="src/lib/Danmaku.vue";export{f as default};