import { defineComponent, reactive, ref, nextTick, onMounted, watch, onUnmounted } from 'vue'; import styles from './audio.module.less'; import iconplay from '../image/icon-pause.png'; import iconpause from '../image/icon-play.png'; import iconReplay from '../image/icon-replay.png'; import { NSlider } from 'naive-ui'; import Vudio from 'vudio.js'; import tickMp3 from '../image/tick.mp3'; export default defineComponent({ name: 'audio-play', props: { item: { type: Object, default: () => { return {}; } }, activeStatus: { type: Boolean, default: false }, isEmtry: { type: Boolean, default: false }, imagePos: { type: String, default: 'left' } }, emits: ['loadedmetadata', 'togglePlay', 'ended', 'reset'], setup(props, { emit, expose }) { const audioForms = reactive({ paused: true, speedInKbps: '', currentTimeNum: 0, isOnline: false, currentTime: '00:00', durationNum: 0, duration: '00:00', showBar: true, afterMa3: true, count: 0, previousBytesLoaded: 0, previousTime: Date.now() }); const canvas: any = ref(); const audio: any = ref(); let vudio: any = null; // 切换音频播放 const onToggleAudio = (e?: any) => { e?.stopPropagation(); // console.log(audio.value.paused, 'audio.value.paused'); if (audio.value.paused) { audio.value.play(); audioForms.afterMa3 = false; } else { audio.value?.pause(); } audioForms.paused = audio.value?.paused; e?.target?.focus(); emit('togglePlay', audioForms.paused); }; const onInit = (audio: undefined, canvas: undefined) => { if (!vudio) { vudio = new Vudio(audio, canvas, { effect: 'waveform', accuracy: 256, width: 1024, height: 600, waveform: { maxHeight: 200, color: [ [0, '#44D1FF'], [0.5, '#44D1FF'], [0.5, '#198CFE'], [1, '#198CFE'] ], prettify: false } }); vudio.dance(); } }; // 对时间进行格式化 const timeFormat = (num: number) => { if (num > 0) { const m = Math.floor(num / 60); const s = num % 60; return (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s); } else { return '00:00'; } }; // const toggleHideControl = (isShow: false) => { audioForms.showBar = isShow; }; const onReplay = () => { if (!audio.value) return; audio.value.currentTime = 0; }; let vudio1 = null; const canvas1: any = ref(); const audio1: any = ref(); nextTick(() => { vudio1 = new Vudio(audio1.value, canvas1.value, { effect: 'waveform', accuracy: 256, width: 1024, height: 600, waveform: { maxHeight: 200, color: [ [0, '#44D1FF'], [0.5, '#44D1FF'], [0.5, '#198CFE'], [1, '#198CFE'] ], prettify: false } }); vudio1.dance(); }); watch( () => props.activeStatus, (val: any) => { // console.log(val, 'val'); audioForms.count = 0; if (val && props.item.autoPlay) { vudio = null; onToggleAudio(); } else { audio.value.pause(); } } ); const calculateSpeed = (element: any) => { let previousBytesLoaded = 0; let timer: any = null; let previousTime = Date.now(); let isWaiting = false; // 缓存检测状态 let isBuffering = false; // 缓存检测计时器 let bufferTimeout: any = null; // 设定一个检测缓存停止的时间间隔,这里我们设置为2500毫秒(2秒) const BUFFER_CHECK_INTERVAL = 2500; function resetDownloadSpeed() { timer = setTimeout(() => { // displayElement.textContent = `视屏下载速度: 0 KB/s`; audioForms.speedInKbps = `0 KB/s`; }, 1500); } function buffterCatch() { // 设定一个计时器,检查是否在指定的时间内再次触发了progress事件 bufferTimeout = setTimeout(() => { if (isBuffering) { // 如果计时器到达且isBuffering仍为true,则认为缓存停止 console.log('停止缓存数据。'); isBuffering = false; audioForms.speedInKbps = ''; } }, BUFFER_CHECK_INTERVAL); } function resetBuffterCatch() { // 如果有缓存检测计时器,则清除它 if (bufferTimeout) { clearTimeout(bufferTimeout); } // 标记为正在缓存 isBuffering = true; buffterCatch(); } element.addEventListener('loadedmetadata', () => { // 获取视频总时长 const duration = element.duration; element.addEventListener('progress', () => { const currentTime = Date.now(); const buffered = element.buffered; let currentBytesLoaded = 0; // 计算视频已缓存的总时长 let cachedDuration = 0; if (buffered.length > 0) { for (let i = 0; i < buffered.length; i++) { currentBytesLoaded += buffered.end(i) - buffered.start(i); cachedDuration += buffered.end(i) - buffered.start(i); } currentBytesLoaded *= element.duration * element.seekable.end(0); // 更精确地近似字节加载量 } // console.log( // 'progress', // currentBytesLoaded, // previousBytesLoaded, // currentBytesLoaded > previousBytesLoaded // ); // 计算未缓存的时间段 const uncachedDuration = duration - cachedDuration; console.log(uncachedDuration, duration, cachedDuration, 'duration'); // 如果存在未缓存的时间段,可以根据具体情况做出相应处理 if (uncachedDuration > 0) { if ( currentBytesLoaded > previousBytesLoaded && audioForms.isOnline ) { const timeDiff = (currentTime - previousTime) / 1000; // 时间差转换为秒 const bytesDiff = currentBytesLoaded - previousBytesLoaded; // 字节差值 const speed = bytesDiff / timeDiff; // 字节每秒 if (!element.paused) { const kbps = speed / 1024; const speedInKbps = kbps.toFixed(2); // 转换为千字节每秒并保留两位小数 if (kbps > 1024) { audioForms.speedInKbps = `${Number( (kbps / 1024).toFixed(2) )} M/s`; } else { audioForms.speedInKbps = `${Number(speedInKbps)} KB/s`; } } previousBytesLoaded = currentBytesLoaded; previousTime = currentTime; } if (!element.paused && audioForms.isOnline) { // 如果1秒钟没有返回就重置数据 clearTimeout(timer); resetDownloadSpeed(); } else { audioForms.speedInKbps = ''; } resetBuffterCatch(); } else { resetBuffterCatch(); } }); element.addEventListener('waiting', () => { console.log('waiting'); isWaiting = true; if (!element.paused) { // 如果1秒钟没有返回就重置数据 clearTimeout(timer); resetDownloadSpeed(); } // 如果有缓存检测计时器,则清除它 if (bufferTimeout) { clearTimeout(bufferTimeout); } }); element.addEventListener('canplay', () => { console.log('canplay'); isWaiting = false; resetBuffterCatch(); }); element.addEventListener('pause', () => { clearTimeout(timer); // 如果有缓存检测计时器,则清除它 if (bufferTimeout) { clearTimeout(bufferTimeout); } audioForms.speedInKbps = ''; }); // element.addEventListener('error', () => { // element.pause(); // }); }); }; const onChangeOnlineStatus = (val: any) => { if (val.type === 'online') { audioForms.isOnline = true; } else if (val.type === 'offline') { audioForms.isOnline = false; } }; onMounted(() => { nextTick(() => { calculateSpeed(audio.value); }); window.addEventListener('online', onChangeOnlineStatus); window.addEventListener('offline', onChangeOnlineStatus); }); onUnmounted(() => { window.removeEventListener('online', onChangeOnlineStatus); window.removeEventListener('offline', onChangeOnlineStatus); }); expose({ toggleHideControl }); return () => (