import { defineComponent, nextTick, onMounted, onUnmounted, reactive, watch, ref } from 'vue'; // import WaveSurfer from 'wavesurfer.js'; import styles from './index.module.less'; import MSticky from '@/components/m-sticky'; import MHeader from '@/components/m-header'; import { Button, Cell, Image, List, Popup, Slider, showDialog, showToast, Sticky, TextEllipsis } from 'vant'; import iconDownload from './images/icon-download.png'; import iconShare from './images/icon-share.png'; import iconDelete from './images/icon-delete.png'; import iconEdit from './images/edit.png'; import iconUpward from './images/upward.png'; import iconMember from './images/icon-member.png'; import iconZan from './images/icon-zan.png'; import promptImg from './images/prompt.png'; import confirmImg from './images/confirm.png'; import canceImg from './images/cance.png'; import iconZanActive from './images/icon-zan-active.png'; import iconPlay from './images/icon-play.png'; import iconPause from './images/icon-pause.png'; import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'; import { browser, getGradeCh, getSecondRPM, vaildMusicScoreUrl } from '@/helpers/utils'; import { useRoute, useRouter } from 'vue-router'; import { api_userMusicDetail, api_userMusicRemove, api_userMusicStarPage } from './api'; import MEmpty from '@/components/m-empty'; import dayjs from 'dayjs'; import MVideo from '@/components/m-video'; import ShareModel from './share-model'; import { usePageVisibility, useEventListener } from '@vant/use'; import "plyr/dist/plyr.css"; import Plyr from "plyr"; import { Vue3Lottie } from "vue3-lottie"; import audioBga from "./images/audioBga.json"; import videobg from "./images/videobg.png"; export default defineComponent({ name: 'creation-detail', setup() { const route = useRoute(); const router = useRouter(); const isScreenScroll = ref(false) const lottieDom = ref() const state = reactive({ id: route.query.id, deleteStatus: false, shareStatus: false, playType: '' as 'Audio' | 'Video' | '', // 播放类型 musicDetail: {} as any, isClick: false, list: [] as any, listState: { dataShow: true, // 判断是否有数据 loading: false, finished: false }, params: { page: 1, rows: 20 }, _plrl: null as any, heightV:0, heightB:0 }); const plyrState = reactive({ duration: 0, currentTime: 0, mediaTimeShow: false, playIngShow: true }) // 谱面 const staffState = reactive({ staffSrc: "", isShow: false, height:"initial", speedRate:1, musicRenderType:"staff", partIndex: 0 }) const staffDom= ref() const {playStaff, pauseStaff, updateProgressStaff} = staffMoveInstance() // 获取列表 const getStarList = async () => { try { if (state.isClick) return; state.isClick = true; const res = await api_userMusicStarPage({ userMusicId: state.id, ...state.params }); state.listState.loading = false; const result = res.data || {}; // 处理重复请求数据 if (state.list.length > 0 && result.current === 1) { return; } state.list = state.list.concat(result.rows || []); state.listState.finished = result.current >= result.pages; state.params.page = result.current + 1; state.listState.dataShow = state.list.length > 0; state.isClick = false; } catch { state.listState.dataShow = false; state.listState.finished = true; state.isClick = false; } }; // 删除作品 const onDelete = async () => { try { await api_userMusicRemove({ id: state.id }); setTimeout(() => { state.deleteStatus = false; showToast('删除成功'); }, 100); setTimeout(() => { if (browser().isApp) { postMessage({ api: 'goBack' }); } else { router.back(); } }, 1200); } catch { // } }; // 下载 const onDownload = async () => { await promisefiyPostMessage({ api: 'saveFile', content: { url: state.musicDetail.videoUrl } }); }; // 滚动事件 const cleanScrollEvent = useEventListener('scroll', () => { const height = window.scrollY || document.documentElement.scrollTop // 防止多次调用 if(height > 0 && isScreenScroll.value === false){ isScreenScroll.value = true setStatusBarTextColor(false) } if(height <= 0){ isScreenScroll.value = false setStatusBarTextColor(true) } }) // 设置导航栏颜色 function setStatusBarTextColor(isWhite:boolean){ postMessage({ api: 'setStatusBarTextColor', content: { statusBarTextColor: isWhite } }) } // 初始化 媒体播放 function initMediaPlay(){ const id = state.playType === "Audio" ? "#audioMediaSrc" : "#videoMediaSrc"; state._plrl = new Plyr(id, { controls: ["progress"], fullscreen: { enabled: false }, }); const player = state._plrl // 创建音波数据 if(state.playType === "Audio"){ const audioDom = document.querySelector("#audioMediaSrc") as HTMLAudioElement const canvasDom = document.querySelector("#audioVisualizer") as HTMLCanvasElement const { pauseVisualDraw, playVisualDraw } = audioVisualDraw(audioDom, canvasDom) player.on('play', () => { lottieDom.value.play() playVisualDraw() }); player.on('pause', () => { lottieDom.value.pause() pauseVisualDraw() }); } player.on("timeupdate", ()=>{ plyrState.currentTime = player.currentTime }) player.on('play', () => { plyrState.playIngShow = false playStaff() }); player.on('pause', () => { plyrState.playIngShow = true pauseStaff() }); // 处理按压事件 const handleStart = () => { plyrState.duration = player.duration plyrState.mediaTimeShow = true }; // 处理松开事件 const handleEnd = () => { plyrState.mediaTimeShow = false // 暂停的时候调用 if(!player.playing){ updateProgressStaff(player.currentTime) } }; const progressDom = document.querySelector("#playMediaSection .plyr__controls .plyr__progress__container") as HTMLElement progressDom.addEventListener('mousedown', handleStart); progressDom.addEventListener('touchstart', handleStart); progressDom.addEventListener('mouseup', handleEnd); progressDom.addEventListener('touchend', handleEnd); } //点击改变播放状态 function handlerClickPlay(){ if (state._plrl.playing) { state._plrl.pause(); } else { state._plrl.play(); } } /** * 音频可视化 * @param audioDom * @param canvasDom * @param fftSize 2的幂数,最小为32 */ function audioVisualDraw(audioDom: HTMLAudioElement, canvasDom: HTMLCanvasElement, fftSize = 128) { type propsType = { canvWidth: number; canvHeight: number; canvFillColor: string; lineColor: string; lineGap: number } // canvas const canvasCtx = canvasDom.getContext("2d")! const { width, height } = canvasDom.getBoundingClientRect() canvasDom.width = width canvasDom.height = height // audio let audioCtx : AudioContext | null = null let analyser : AnalyserNode | null = null let source : MediaElementAudioSourceNode | null = null const dataArray = new Uint8Array(fftSize / 2) const draw = (data: Uint8Array, ctx: CanvasRenderingContext2D, { lineGap, canvWidth, canvHeight, canvFillColor, lineColor }: propsType) => { if (!ctx) return const w = canvWidth const h = canvHeight fillCanvasBackground(ctx, w, h, canvFillColor) // 可视化 const dataLen = data.length let step = (w / 2 - lineGap * dataLen) / dataLen step < 1 && (step = 1) const midX = w / 2 const midY = h / 2 let xLeft = midX for (let i = 0; i < dataLen; i++) { const value = data[i] const percent = value / 255 // 最大值为255 const barHeight = percent * midY canvasCtx.fillStyle = lineColor // 中间加间隙 if (i === 0) { xLeft -= lineGap / 2 } canvasCtx.fillRect(xLeft - step, midY - barHeight, step, barHeight) canvasCtx.fillRect(xLeft - step, midY, step, barHeight) xLeft -= step + lineGap } let xRight = midX for (let i = 0; i < dataLen; i++) { const value = data[i] const percent = value / 255 // 最大值为255 const barHeight = percent * midY canvasCtx.fillStyle = lineColor if (i === 0) { xRight += lineGap / 2 } canvasCtx.fillRect(xRight, midY - barHeight, step, barHeight) canvasCtx.fillRect(xRight, midY, step, barHeight) xRight += step + lineGap } } const fillCanvasBackground = (ctx: CanvasRenderingContext2D, w: number, h: number, colors: string) => { ctx.clearRect(0, 0, w, h) ctx.fillStyle = colors ctx.fillRect(0, 0, w, h) } const requestAnimationFrameFun = () => { requestAnimationFrame(() => { analyser?.getByteFrequencyData(dataArray) draw(dataArray, canvasCtx, { lineGap: 2, canvWidth: width, canvHeight: height, canvFillColor: "transparent", lineColor: "rgba(255, 255, 255, 0.7)" }) if (!isPause) { requestAnimationFrameFun() } }) } let isPause = true const playVisualDraw = () => { if (!audioCtx) { audioCtx = new AudioContext() source = audioCtx.createMediaElementSource(audioDom) analyser = audioCtx.createAnalyser() analyser.fftSize = fftSize source?.connect(analyser) analyser.connect(audioCtx.destination) } //audioCtx.resume() // 重新更新状态 加了暂停和恢复音频音质发生了变化 所以这里取消了 isPause = false requestAnimationFrameFun() } const pauseVisualDraw = () => { isPause = true //audioCtx?.suspend() // 暂停 加了暂停和恢复音频音质发生了变化 所以这里取消了 // source?.disconnect() // analyser?.disconnect() } return { playVisualDraw, pauseVisualDraw } } function handlerLandscapeScreen(event:any){ event.stopPropagation() router.push({ path:"/playCreation", query:{ resourceUrl:encodeURIComponent(state.musicDetail?.videoUrl), musicSheetName:encodeURIComponent(state.musicDetail?.musicSheetName), username:encodeURIComponent(state.musicDetail?.username), musicSheetId:encodeURIComponent(state.musicDetail?.musicSheetId), speedRate:encodeURIComponent(staffState.speedRate), musicRenderType:encodeURIComponent(staffState.musicRenderType), partIndex:encodeURIComponent(staffState.partIndex), } }) } // 初始化五线谱 function initStaff(){ const src = `${vaildMusicScoreUrl()}/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`; //const src = `https://dev.kt.colexiu.com/instrument/#/simple-detail?id=${state.musicDetail.musicSheetId}&musicRenderType=${staffState.musicRenderType}&part-index=${staffState.partIndex}`; staffState.staffSrc = src window.addEventListener('message', (event) => { const { api, height } = event.data; if (api === 'api_musicPage') { staffState.isShow = true staffState.height = height + "px" // 如果是播放中自动开始 播放 if(state._plrl.playing){ playStaff() } } }); } function staffMoveInstance(){ let isPause = true const requestAnimationFrameFun = () => { requestAnimationFrame(() => { staffDom.value?.contentWindow?.postMessage( { api: 'api_playProgress', content: { currentTime: state._plrl.currentTime * staffState.speedRate } }, "*" ) if (!isPause) { requestAnimationFrameFun() } }) } const playStaff = () => { // 没渲染不执行 if(!staffState.isShow) return isPause = false staffDom.value?.contentWindow?.postMessage( { api: 'api_play' }, "*" ) requestAnimationFrameFun() } const pauseStaff = () => { // 没渲染不执行 if(!staffState.isShow) return isPause = true staffDom.value?.contentWindow?.postMessage( { api: 'api_paused' }, "*" ) } const updateProgressStaff = (currentTime: number) => { // 没渲染不执行 if(!staffState.isShow) return staffDom.value?.contentWindow?.postMessage( { api: 'api_updateProgress', content: { currentTime: currentTime * staffState.speedRate } }, "*" ) } return { playStaff, pauseStaff, updateProgressStaff } } onMounted(async () => { setStatusBarTextColor(true) try { const res = await api_userMusicDetail(state.id); // console.log(res); if (res.code === 999) { showDialog({ message: res.message, theme: 'round-button', confirmButtonColor: 'linear-gradient(73deg, #5BECFF 0%, #259CFE 100%)' }).then(() => { if (browser().isApp) { postMessage({ api: 'goBack' }); } else { router.back(); } }); return; } state.musicDetail = res.data || {}; try{ const jsonConfig = JSON.parse(res.data.jsonConfig) jsonConfig.speedRate && (staffState.speedRate = jsonConfig.speedRate) jsonConfig.musicRenderType && (staffState.musicRenderType = jsonConfig.musicRenderType) jsonConfig.partIndex && (staffState.partIndex = jsonConfig.partIndex) }catch{ } // 五线谱 initStaff() getStarList(); // 判断是视频还是音频 if (res.data.videoUrl.lastIndexOf('mp4') !== -1) { state.playType = 'Video'; } else { state.playType = 'Audio'; } nextTick(()=>{ initMediaPlay() }) } catch { // } }); onUnmounted(() => { setStatusBarTextColor(false) cleanScrollEvent() }); return () => (
{ console.log(height, 'height', height) state.heightV = height }} >
演奏:{state.musicDetail?.username}
{ state.playType === 'Audio' &&
} { state.playType === 'Video' &&
{state.musicDetail?.username} {state.musicDetail.vipFlag && ( )}
{state.musicDetail.subjectName}{' '} {getGradeCh(state.musicDetail.currentGradeNum - 1)}
{state.musicDetail.likeNum}
点赞记录
{state.listState.dataShow ? ( {state.list.map((item: any, index: number) => ( {{ icon: () => ( ), title: () => (

{item.userName}

{item.subjectName}{' '} {getGradeCh(item.currentGradeNum - 1)}

), value: () => (
{dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
) }}
))}
) : ( )}
{ !isScreenScroll.value &&
} { console.log(height, 'height', height) state.heightB = height }}>

下载

(state.shareStatus = true)}> 分享

(state.deleteStatus = true)}> 删除

{ router.push({ path: '/creation-edit', query: { id: state.id } }); }} />

确定删除吗?

(state.deleteStatus = false)} />
(state.shareStatus = false)} />
); } });