index.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { defineComponent, onMounted, onUnmounted, reactive, nextTick } from "vue";
  2. import state, { getMusicDetail, handleSetSpeed, addNoteBBox, getNote, gotoNext, fillWordColor, moveSvgDom } from "/src/state";
  3. import MusicScore from "../../view/music-score";
  4. import styles from "./index.module.less";
  5. import { getQuery } from "/src/utils/queryString";
  6. import { closeToast, showLoadingToast } from "vant";
  7. import store from "store";
  8. import { formateTimes } from "../../helpers/formateMusic";
  9. import { setCustomGradual, setCustomNoteRealValue } from "/src/helpers/customMusicScore"
  10. import { initSmoothAnimation, smoothAnimationState, destroySmoothAnimation, moveSmoothAnimationByPlayTime } from "../view-detail/smoothAnimation";
  11. import { api_cloudLoading, simple_musicPage } from "/src/helpers/communication";
  12. export default defineComponent({
  13. name: "simple-detail",
  14. setup() {
  15. const query: any = getQuery();
  16. const detailData = reactive({
  17. isLoading: true,
  18. currentTime: 0, // 当前播放的时间
  19. totalTime: 0, // 音视频总时长
  20. backgroundRendMode: "" as "video" | "audio", // 嵌入的时音频还是视频
  21. });
  22. const communicateCb = (res: any) => {
  23. let resInfo: any = {};
  24. try {
  25. resInfo = typeof res?.data === 'string' ? JSON.parse(res.data) : res.data;
  26. // console.log('Received data:', resInfo);
  27. } catch (error) {
  28. console.error('parse_error: Invalid JSON data received');
  29. }
  30. // 开始播放
  31. if (resInfo?.api === "api_play") {
  32. console.log('h5开始播放')
  33. state.playState = 'play';
  34. setStep();
  35. }
  36. // 暂停播放
  37. if (resInfo?.api === "api_paused") {
  38. const currentTime = resInfo?.content?.currentTime
  39. console.log('暂停播放',currentTime)
  40. state.playState = 'paused';
  41. if (currentTime === 0) {
  42. // 坐标和小节都改为初始值
  43. setTimeout(() => {
  44. detailData.currentTime = 0
  45. state.activeNoteIndex = 0
  46. state.activeMeasureIndex = state.times[0].MeasureNumberXML;
  47. handlePlaying(true);
  48. }, 200);
  49. }
  50. }
  51. // 暂停状态下,拖动进度
  52. if (resInfo?.api === "api_updateProgress") {
  53. console.log('拖动的进度',state.playState)
  54. if (state.playState === 'paused') {
  55. detailData.currentTime = resInfo?.content?.currentTime ?? detailData.currentTime;
  56. // 坐标和小节都改为初始值
  57. state.activeNoteIndex = 0
  58. state.activeMeasureIndex = state.times[0].MeasureNumberXML;
  59. handlePlaying(true);
  60. }
  61. }
  62. // 播放进度
  63. if (resInfo?.api === "api_playProgress") {
  64. // console.log('播放进度',resInfo)
  65. const currentTime = resInfo?.content?.currentTime
  66. if (currentTime) {
  67. if (currentTime < detailData.currentTime) {
  68. // 坐标和小节都改为初始值
  69. state.activeNoteIndex = 0
  70. let item = getNote(currentTime) || state.times[0];
  71. if(item.i === state.activeNoteIndex){
  72. state.activeMeasureIndex = item.MeasureNumberXML;
  73. }
  74. // 当拖动到MP3节拍器时候 需要手动移动到当前为止
  75. const fixtime = state.times[0].fixtime
  76. if(currentTime <= fixtime){
  77. moveSvgDom(true)
  78. }
  79. }
  80. detailData.currentTime = currentTime
  81. }
  82. }
  83. };
  84. // 监听评测曲谱音频播放进度,返回
  85. const progress = (res: any) => {
  86. console.log('app播放进度',res)
  87. const currentTime = res?.currentTime || res?.content?.currentTime;
  88. if (currentTime) {
  89. if (currentTime < detailData.currentTime) {
  90. state.activeNoteIndex = 0
  91. }
  92. detailData.currentTime = currentTime
  93. }
  94. };
  95. onMounted(async () => {
  96. const id = query.id || '';
  97. detailData.backgroundRendMode = query.backgroundRendMode;
  98. state.isSimplePage = true;
  99. await getMusicDetail(id, 'open');
  100. detailData.isLoading = false;
  101. state.isSingleLine = true;
  102. window.addEventListener("message", communicateCb);
  103. });
  104. onUnmounted(() => {
  105. state.isSimplePage = false;
  106. window.removeEventListener("message", communicateCb);
  107. });
  108. /** 渲染完成 */
  109. const handleRendered = async (osmd: any) => {
  110. const svgHeight = document.getElementById('scrollContainer')?.getBoundingClientRect().height;
  111. state.osmd = osmd;
  112. // 没有设置速度使用读取的速度
  113. if (state.originSpeed === 0) {
  114. state.originSpeed = state.speed = (osmd as any).bpm || osmd.Sheet.userStartTempoInBPM || 100;
  115. }
  116. const saveSpeed = (store.get("speeds") || {})[state.examSongId] || state.speed || (osmd as any).bpm || osmd.Sheet.userStartTempoInBPM;
  117. // 加载本地缓存的速度
  118. if (saveSpeed) {
  119. handleSetSpeed(saveSpeed);
  120. }
  121. setCustomGradual();
  122. setCustomNoteRealValue();
  123. state.times = formateTimes(osmd);
  124. console.log("🚀 ~ state.times:", state.times, state);
  125. nextTick(() => {
  126. state.activeMeasureIndex = state.times[0].MeasureNumberXML;
  127. // fillWordColor();
  128. })
  129. // 音符添加位置信息bbox
  130. addNoteBBox(state.times);
  131. // 一行谱创建 动画
  132. initSmoothAnimation();
  133. //destroySmoothAnimation();
  134. //smoothAnimationState.isShow.value = false;
  135. api_cloudLoading();
  136. console.log('渲染完成',svgHeight)
  137. window.parent.postMessage(
  138. {
  139. api: "api_musicPage",
  140. height: svgHeight
  141. },
  142. "*"
  143. );
  144. simple_musicPage({
  145. height: svgHeight
  146. })
  147. // state.playState = 'play';
  148. // setStep();
  149. };
  150. /**
  151. * 播放一直触发的事件
  152. */
  153. const handlePlaying = (skipNote?: boolean) => {
  154. //detailData.currentTime += 0.03
  155. const currentTime = detailData.currentTime;
  156. // console.log('👀~播放进度',currentTime)
  157. let item = getNote(currentTime) || state.times[0];
  158. if (item) {
  159. gotoNext(item, skipNote);
  160. }
  161. state.activeNoteIndex = item?.i || 0
  162. // 一行谱,需要滚动小节
  163. if (state.isSingleLine) {
  164. moveSmoothAnimationByPlayTime(currentTime, true)
  165. }
  166. };
  167. /** 在渲染前后计算光标应该走到的音符 */
  168. const setStep = () => {
  169. // console.log('播放状态',state.playState)
  170. if (state.playState !== "play") {
  171. console.log("暂停播放");
  172. return;
  173. }
  174. let startTime = Date.now();
  175. requestAnimationFrame(() => {
  176. const endTime = Date.now();
  177. // 渲染时间大于16.6,就会让页面卡顿, 如果渲染时间大与16.6就下一个渲染帧去计算
  178. if (endTime - startTime < 16.7) {
  179. handlePlaying();
  180. setStep();
  181. } else {
  182. setTimeout(() => {
  183. handlePlaying();
  184. setStep();
  185. }, 16.7);
  186. }
  187. });
  188. };
  189. return () => (
  190. <div class={[styles.detail, detailData.backgroundRendMode === 'video' && styles.whiteBg]}>
  191. <div class={styles.mask}></div>
  192. <div id="scrollContainer" class={[styles.container]}>
  193. {/* 曲谱渲染 */}
  194. {!detailData.isLoading &&
  195. <MusicScore
  196. onRendered={handleRendered}
  197. musicColor={'#000000'}
  198. />}
  199. </div>
  200. </div>
  201. );
  202. },
  203. });