index.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <script setup lang="ts">
  2. import { danmus as danmusData, getDanmuData } from "./danmu.js";
  3. import { onMounted, onUnmounted, reactive, ref } from "vue";
  4. import VueDanmaku from "vue3-danmaku";
  5. defineOptions({
  6. name: "Danmaku"
  7. });
  8. const danmaku = ref();
  9. const danmus = ref<any[]>(getDanmuData());
  10. const danmuMsg = ref<string>("");
  11. let timer = 0;
  12. const config = reactive({
  13. channels: 5, // 轨道数量,为0则弹幕轨道数会撑满容器
  14. useSlot: true, // 是否开启slot
  15. loop: true, // 是否开启弹幕循环
  16. speeds: 200, // 弹幕速度,实际为弹幕滚动完一整屏的秒数,值越小速度越快
  17. fontSize: 20, // 文本模式下的字号
  18. top: 10, // 弹幕轨道间的垂直间距
  19. right: 0, // 同一轨道弹幕的水平间距
  20. debounce: 100, // 弹幕刷新频率(多少毫秒插入一条弹幕,建议不小于50)
  21. randomChannel: true // 随机弹幕轨道
  22. });
  23. onMounted(() => {
  24. window.onresize = () => resizeHandler();
  25. });
  26. onUnmounted(() => {
  27. window.onresize = null;
  28. });
  29. function play(type: string) {
  30. switch (type) {
  31. case "play":
  32. danmaku.value.play();
  33. break;
  34. case "pause":
  35. danmaku.value.pause();
  36. break;
  37. case "stop":
  38. danmaku.value.stop();
  39. break;
  40. case "show":
  41. danmaku.value.show();
  42. break;
  43. case "hide":
  44. danmaku.value.hide();
  45. break;
  46. case "reset":
  47. danmaku.value.reset();
  48. break;
  49. default:
  50. break;
  51. }
  52. }
  53. function switchSlot(slot: boolean) {
  54. config.useSlot = slot;
  55. danmus.value = slot ? getDanmuData() : danmusData;
  56. setTimeout(() => {
  57. danmaku.value.stop();
  58. danmaku.value.play();
  59. });
  60. }
  61. function speedsChange(val: number) {
  62. if (config.speeds <= 10 && val === -10) {
  63. return;
  64. }
  65. config.speeds += val;
  66. danmaku.value.reset();
  67. }
  68. function fontChange(val: number) {
  69. config.fontSize += val;
  70. danmaku.value.reset();
  71. }
  72. function channelChange(val: number) {
  73. if (!config.channels && val === -1) {
  74. return;
  75. }
  76. config.channels += val;
  77. }
  78. function resizeHandler() {
  79. if (timer) clearTimeout(timer);
  80. timer = window.setTimeout(() => {
  81. danmaku.value.resize();
  82. }, 500);
  83. }
  84. function addDanmu() {
  85. if (!danmuMsg.value) return;
  86. const _danmuMsg = config.useSlot
  87. ? {
  88. avatar: "https://i.loli.net/2021/01/17/xpwbm3jKytfaNOD.jpg",
  89. name: "你",
  90. text: danmuMsg.value
  91. }
  92. : danmuMsg.value;
  93. danmaku.value.add(_danmuMsg);
  94. danmuMsg.value = "";
  95. }
  96. </script>
  97. <template>
  98. <el-card shadow="never">
  99. <template #header>
  100. <div class="card-header">
  101. <span class="font-medium">
  102. 弹幕组件,采用开源的
  103. <el-link
  104. href="https://github.com/hellodigua/vue-danmaku/tree/vue3"
  105. target="_blank"
  106. style="margin: 0 4px 5px; font-size: 16px"
  107. >
  108. vue3-danmaku
  109. </el-link>
  110. </span>
  111. </div>
  112. <el-link
  113. class="mt-2"
  114. href="https://github.com/pure-admin/vue-pure-admin/blob/main/src/views/able/danmaku"
  115. target="_blank"
  116. >
  117. 代码位置 src/views/able/danmaku
  118. </el-link>
  119. </template>
  120. <div class="flex gap-5">
  121. <vue-danmaku
  122. ref="danmaku"
  123. v-model:danmus="danmus"
  124. class="demo"
  125. isSuspend
  126. v-bind="config"
  127. >
  128. <!-- 弹幕slot -->
  129. <template v-slot:dm="{ danmu, index }">
  130. <div class="danmu-item">
  131. <img class="img" :src="danmu.avatar" />
  132. <span>{{ index }}{{ danmu.name }}:</span>
  133. <span>{{ danmu.text }}</span>
  134. </div>
  135. </template>
  136. </vue-danmaku>
  137. <div class="main">
  138. <p>
  139. 播放:
  140. <el-button @click="play('play')">播放</el-button>
  141. <el-button @click="play('pause')">暂停</el-button>
  142. <el-button @click="play('stop')">停止</el-button>
  143. </p>
  144. <p>
  145. 模式:
  146. <el-button @click="switchSlot(true)">弹幕 slot</el-button>
  147. <el-button @click="switchSlot(false)">普通文本</el-button>
  148. </p>
  149. <p>
  150. 显示:
  151. <el-button @click="play('show')">显示</el-button>
  152. <el-button @click="play('hide')">隐藏</el-button>
  153. </p>
  154. <p>
  155. 速度:
  156. <el-button @click="speedsChange(-10)">减速</el-button>
  157. <el-button @click="speedsChange(10)">增速</el-button>
  158. <span class="ml-5">当前速度:{{ config.speeds }}像素/s</span>
  159. </p>
  160. <p>
  161. 字号:
  162. <el-button :disabled="config.useSlot" @click="fontChange(-1)">
  163. 缩小
  164. </el-button>
  165. <el-button :disabled="config.useSlot" @click="fontChange(1)">
  166. 放大
  167. </el-button>
  168. <span class="ml-5">当前字号:{{ config.fontSize }}px</span>
  169. </p>
  170. <p>
  171. 轨道:
  172. <el-button @click="channelChange(-1)">-1</el-button>
  173. <el-button @click="channelChange(1)">+1</el-button>
  174. <el-button @click="channelChange(-config.channels)"> 填满 </el-button>
  175. <span class="ml-5">当前轨道:{{ config.channels }}</span>
  176. </p>
  177. <p class="flex">
  178. <el-input
  179. v-model="danmuMsg"
  180. type="text"
  181. placeholder="输入评论后,回车发送弹幕"
  182. @keyup.enter="addDanmu"
  183. />
  184. </p>
  185. </div>
  186. </div>
  187. </el-card>
  188. </template>
  189. <style lang="scss" scoped>
  190. .demo {
  191. flex: 1;
  192. height: 600px;
  193. background: linear-gradient(45deg, #5ac381, #20568b);
  194. .danmu-item {
  195. display: flex;
  196. align-items: center;
  197. .img {
  198. width: 25px;
  199. height: 25px;
  200. margin-right: 5px;
  201. border-radius: 50%;
  202. }
  203. }
  204. }
  205. .main {
  206. flex: 1;
  207. p {
  208. margin-top: 10px;
  209. }
  210. }
  211. </style>