useAudio.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /**
  2. * 利用
  3. */
  4. import store from "@/store";
  5. import { computed, onMounted, watch, ref, unref, reactive } from "vue";
  6. import debounce from "lodash-es/debounce";
  7. import mitt from "mitt";
  8. let CLICKFIRST = false;
  9. const currentPlayer = ref(null);
  10. const isInit = ref(false);
  11. const currentAudio = computed(() => store.getters["audio/currentAudio"]);
  12. const currentAudioTemp = ref("");
  13. const isDoneforCover = computed(() => store.getters["scene/isDoneforCover"]);
  14. const currentScene = computed(() => store.getters["scene/currentScene"]);
  15. const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
  16. const isShowCover = computed(
  17. () => store.getters["scene/metadata"].coverInfo.isShowCover === 1 || false
  18. );
  19. async function initDefaultAudio() {
  20. watch(
  21. [isShowCover, isDoneforCover, currentScene],
  22. ([val1, val2, val3]) => {
  23. //开场完成后才开始初始化audioplayer
  24. console.log("initDefaultAudio", unref(val1), unref(val2));
  25. if (!unref(val1)) {
  26. // 开场封面关闭时
  27. initAudioPlayer();
  28. } else {
  29. // 开场封面开启时并跳转完成时
  30. if (unref(val2)) {
  31. initAudioPlayer();
  32. }
  33. }
  34. watchUpdateCurrentScenEexplanation(unref(val3));
  35. watchResetV4BGM(unref(val3));
  36. },
  37. {
  38. deep: true,
  39. immediate: true,
  40. }
  41. );
  42. watch(
  43. currentAudio,
  44. (val) => {
  45. const oldValue = currentAudioTemp.value;
  46. const newValue = unref(val).url;
  47. const isSame = newValue === oldValue;
  48. console.log("audio-isSame", isSame);
  49. if (newValue && newValue.length > 0) {
  50. if (unref(currentPlayer)) {
  51. const url = unref(val).url;
  52. const autoplay = unref(val).isAuto;
  53. const loop = unref(val).repeat;
  54. console.log("currentAudio", unref(val).url, autoplay, loop);
  55. if (!isSame) {
  56. unref(currentPlayer).switchUrl(url, autoplay, loop);
  57. currentAudioTemp.value = newValue;
  58. } else {
  59. //相同URL的再次播放
  60. unref(currentPlayer).resume();
  61. }
  62. }
  63. } else {
  64. console.log('为空暂时')
  65. unref(currentPlayer) && unref(currentPlayer).stop();
  66. }
  67. },
  68. {
  69. deep: true,
  70. immediate: true,
  71. }
  72. );
  73. }
  74. function initAudioPlayer() {
  75. if (!unref(isInit)) {
  76. isInit.value = true;
  77. console.log("initAudioPlayer");
  78. const player = createAudioPlayer(
  79. unref(currentAudio).url,
  80. unref(currentAudio).isAuto,
  81. unref(currentAudio).repeat
  82. );
  83. currentPlayer.value = player;
  84. player.on("play", () => {
  85. console.log("play--22", player.isPlaying);
  86. store.dispatch("audio/updatePlayerStatus", player.isPlaying);
  87. });
  88. player.on("pause", () => {
  89. console.log("pause--33", player.isPlaying);
  90. store.dispatch("audio/updatePlayerStatus", player.isPlaying);
  91. });
  92. player.on("end", () => {
  93. console.log("end--33", player._loop);
  94. store.dispatch("audio/updatePlayerStatus", player.isPlaying);
  95. });
  96. window.store = store;
  97. }
  98. }
  99. function watchUpdateCurrentScenEexplanation(data) {
  100. if ("explanation" in data) {
  101. store.dispatch("audio/initExplanationBGM", {
  102. url: data.explanation.audioUrl,
  103. repeat: data.explanation.repeat,
  104. isAuto: data.explanation.openByDefault,
  105. });
  106. } else {
  107. console.log("not initExplanationBGM");
  108. store.dispatch("audio/initExplanationBGM", {
  109. url: "",
  110. repeat: false,
  111. isAuto: false,
  112. });
  113. }
  114. }
  115. function watchResetV4BGM(data) {
  116. console.log("data.type", data.type);
  117. if (data.type !== "4dkk") {
  118. store.dispatch("audio/initV4BGM", "");
  119. }
  120. }
  121. function createAudioPlayer(url, autoplay, loop = true) {
  122. const player = new AudioPlayer({
  123. src: url,
  124. autoplay: autoplay,
  125. loop: loop,
  126. });
  127. return player;
  128. }
  129. class AudioPlayer {
  130. constructor(options) {
  131. this._src = options.src;
  132. this._loop = options.loop;
  133. this._autoplay = options.autoplay || false;
  134. this._isPlaying = false;
  135. this._firstPlay = false;
  136. this._lock = false;
  137. const emitter = mitt();
  138. Object.keys(emitter).forEach((method) => {
  139. this[method] = emitter[method];
  140. });
  141. this.audio = null;
  142. this.switchUrl = debounce(this.switchUrlSource, 80).bind(this);
  143. this.play = debounce(this.toPlay, 80).bind(this);
  144. this.init();
  145. }
  146. get isPlaying() {
  147. return this._isPlaying;
  148. }
  149. switchUrlSource(url, autoplay, loop) {
  150. if ("unload" in this.audio) {
  151. console.log("switchUrlSource-1");
  152. this.audio.unload();
  153. } else {
  154. console.log("switchUrlSource-2");
  155. return;
  156. }
  157. console.log("switchUrlSource", url, autoplay, loop);
  158. this._isPlaying = false;
  159. // this._lock = false;
  160. // this._firstPlay = false;
  161. this._autoplay = autoplay || false;
  162. this._loop = loop || false;
  163. this._src = url;
  164. this.audio = null;
  165. this.createAudio();
  166. this.play();
  167. this.emit("change", this.audio);
  168. }
  169. init() {
  170. this.createAudio();
  171. this.bindElement();
  172. this.play();
  173. this.emit("ready", this.audio);
  174. }
  175. createAudio() {
  176. this.audio = new Howl({
  177. preload: true,
  178. src: [this._src],
  179. loop: this._loop || false,
  180. html5: false,
  181. onplay: () => {
  182. this._isPlaying = true;
  183. this.emit("play");
  184. },
  185. onpause: () => {
  186. this._isPlaying = false;
  187. this.emit("pause");
  188. },
  189. onend: () => {
  190. this._isPlaying = false;
  191. console.log("onend", this._loop);
  192. if (!this._loop) {
  193. this.audio.unload();
  194. }
  195. this.emit("end");
  196. },
  197. });
  198. }
  199. bindElement() {
  200. const $player = document.querySelector(".ui-view-layout");
  201. const onclick = () => {
  202. $player.removeEventListener("click", onclick);
  203. $player.removeEventListener("touchstart", onclick);
  204. //判断是否第一次进入或者是否已点击过 或已自动播放过。
  205. if (CLICKFIRST || this._isPlaying) {
  206. console.log("已点击过或自动播放中");
  207. return;
  208. }
  209. CLICKFIRST = true;
  210. this.play();
  211. };
  212. $player.addEventListener("click", onclick);
  213. $player.addEventListener("touchstart", onclick);
  214. }
  215. async toPlay() {
  216. try {
  217. if (!this._isPlaying && !this._lock) {
  218. // console.log("play---1", this._autoplay, this._lock, this._firstPlay);
  219. console.log(
  220. `playStatus: audioplay->${this._autoplay},
  221. lock:${this._lock},firstPlay:${this._firstPlay},loop:${this._loop}`
  222. );
  223. if (this._autoplay || this._firstPlay) {
  224. await this.audio.play();
  225. } else {
  226. //默认不自动播放重置状态并记录已播放过
  227. if (CLICKFIRST) {
  228. this._firstPlay = true;
  229. }
  230. this._isPlaying = false;
  231. store.dispatch("audio/updatePlayerStatus", false);
  232. }
  233. }
  234. } catch (error) {
  235. console.warn("playError", error);
  236. }
  237. }
  238. pause() {
  239. return this.audio.pause();
  240. }
  241. resume() {
  242. console.log("resume");
  243. if (!this._isPlaying) {
  244. return this.audio.play();
  245. }
  246. }
  247. stop() {
  248. this.pause();
  249. store.dispatch("audio/updatePlayerStatus", false);
  250. }
  251. destroy() {
  252. console.warn("audio-destroy");
  253. if ("unload" in this.audio) {
  254. this.audio.unload();
  255. }
  256. this._isPlaying = false;
  257. this._autoplay = false;
  258. this._loop = false;
  259. this._src = "";
  260. this.audio = null;
  261. store.dispatch("audio/updatePlayerStatus", false);
  262. this.emit("destroy");
  263. }
  264. lock() {
  265. console.log("audio-lock");
  266. this._lock = true;
  267. this.pause();
  268. }
  269. unlock() {
  270. console.log("audio-unlock");
  271. this._lock = false;
  272. }
  273. mute() {
  274. return this.audio.mute(true);
  275. }
  276. unmute() {
  277. return this.audio.mute(false);
  278. }
  279. }
  280. export function useAudio() {
  281. return {
  282. initDefaultAudio,
  283. currentPlayer,
  284. };
  285. }