import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'; import { ref } from 'vue'; import styles from './video.module.less'; import { postMessage } from '@/helpers/native-message'; // import iconLoop from '../image/icon-loop.svg'; // import iconLoopActive from '../image/icon-loop-active.svg'; // import iconplay from '../image/icon-play.svg'; // import iconpause from '../image/icon-pause.svg'; import { // iconVideoBg, iconLoop, iconLoopActive, iconPlay, iconPause, iconSpeed } from '../image/icons.json'; import TCPlayer from 'tcplayer.js'; import 'tcplayer.js/dist/tcplayer.min.css'; import { showToast, Slider } from 'vant'; import { handleShowVip, state } from '@/state'; // 秒转分 export const getSecondRPM = (second: number, type?: string) => { if (isNaN(second)) return '00:00'; const mm = Math.floor(second / 60) .toString() .padStart(2, '0'); const dd = Math.floor(second % 60) .toString() .padStart(2, '0'); if (type === 'cn') { return mm + '分' + dd + '秒'; } else { return mm + ':' + dd; } }; export default defineComponent({ name: 'video-play', props: { item: { type: Object, default: () => { return {}; } }, isEmtry: { type: Boolean, default: false }, isActive: { type: Boolean, default: false }, activeModel: { type: Boolean, default: true } }, emits: [ 'loadedmetadata', 'togglePlay', 'ended', 'reset', 'error', 'close', 'play', 'pause', 'seeked', 'seeking', 'waiting', 'timeupdate' ], setup(props, { emit, expose }) { const { item } = toRefs(props); const data = reactive({ timer: null as any, currentTime: 0, duration: 0.1, loop: false, playState: 'pause' as 'play' | 'pause', vudio: null as any, showBar: true, speedControl: false, speedStyle: { left: '1px' }, defaultSpeed: 1 // 默认速度 }); const speedBtnId = 'speed' + Date.now() + Math.floor(Math.random() * 100); /** 设置播放容器 16:9 */ const parentContainer = reactive({ width: '100%' }); const setContainer = () => { const min = Math.min(screen.width, screen.height); const max = Math.max(screen.width, screen.height); const width = min * (16 / 9); if (width > max) { parentContainer.width = '100%'; return; } else { parentContainer.width = width + 'px'; } }; // const forms = reactive({ // subjectIds: [], // orgainIds: [] // }); const videoRef = ref(); const videoItem = ref(); const videoID = 'video' + Date.now() + Math.floor(Math.random() * 100); const toggleHideControl = (isShow: boolean) => { data.speedControl = false; data.showBar = isShow; }; // const togglePlay = (e: Event) => { // e.stopPropagation() // } let playTimer = null as any; // 切换音频播放 const onToggleAudio = (state: 'play' | 'pause') => { // console.log(state, 'state') data.speedControl = false; clearTimeout(playTimer); if (state === 'play') { playTimer = setTimeout(() => { videoItem.value?.play(); data.playState = 'play'; }, 100); } else { videoItem.value?.pause(); data.playState = 'pause'; } emit('togglePlay', data.playState); }; const toggleLoop = () => { data.speedControl = false; if (!videoItem.value) return; if (data.loop) { videoItem.value.loop(false); showToast("已关闭循环播放") } else { videoItem.value.loop(true); showToast("已打开循环播放") } data.loop = !data.loop; }; const changePlayBtn = (code: string) => { // data.speedControl = false; if (code == 'play') { data.playState = 'play'; } else { data.playState = 'pause'; } }; /** 改变播放时间 */ const handleChangeTime = (val: number) => { data.currentTime = val; clearTimeout(data.timer); data.timer = setTimeout(() => { videoItem.value.currentTime(val); data.timer = null; }, 300); }; const __initVideo = () => { if (videoItem.value && props.item.id) { console.log(videoItem.value, 'videoItem.value'); nextTick(() => { videoItem.value?.currentTime(0); }); videoItem.value.poster(props.item.coverImg); // 封面 videoItem.value.src(props.item.content); // url 播放地址 videoItem.value.playbackRate(data.defaultSpeed); data.speedControl = false; // 初步加载时 videoItem.value.on('loadedmetadata', () => { videoItem.value.playbackRate(data.defaultSpeed); // 获取时长 data.duration = videoItem.value.duration(); // 必须在当前元素 if (item.value.autoPlay && videoItem.value && props.isActive) { // videoItem.value?.play() nextTick(() => { videoItem.value.currentTime(0); nextTick(handlePlayVideo); }); } emit('loadedmetadata', videoItem.value); }); // 视频播放时加载 videoItem.value.on('timeupdate', () => { if (data.timer) return; data.currentTime = videoItem.value.currentTime(); emit('timeupdate'); }); // 视频播放结束 videoItem.value.on('ended', () => { changePlayBtn('pause'); emit('ended'); }); // videoItem.value.on('pause', () => { data.playState = 'pause'; changePlayBtn('pause'); emit('togglePlay', true); emit('pause'); }); videoItem.value.on('seeked', () => { emit('seeked'); }); videoItem.value.on('seeking', () => { emit('seeking'); }); videoItem.value.on('waiting', () => { emit('waiting'); }); videoItem.value.on('play', () => { // console.log(play, 'playing') changePlayBtn('play'); if (videoItem.value) { videoItem.value.muted(false); videoItem.value.volume(1); } // if ( // !item.value.autoPlay && // !item.value.isprepare && // videoItem.value // ) { // // 加载完成后,取消静音播放 // videoItem.value.pause(); // } emit('togglePlay', videoItem.value?.paused); emit('play'); }); // 视频播放异常 videoItem.value.on('error', (e: any) => { handleErrorVideo(); emit('error'); console.log(e, 'error'); }); } }; let videoTimer = null as any; let videoTimerErrorCount = 0; const handlePlayVideo = () => { if (videoTimerErrorCount > 5) { return; } clearTimeout(videoTimer); nextTick(() => { videoItem.value?.play().catch((err: any) => { // console.log('🚀 ~ err:', err) videoTimer = setTimeout(() => { if (err?.message?.includes('play()')) { emit('play'); } handlePlayVideo(); }, 1000); }); }); videoTimerErrorCount++; }; let videoErrorTimer = null as any; let videoErrorCount = 0; const handleErrorVideo = () => { if (videoErrorCount > 5) { return; } clearTimeout(videoErrorTimer); nextTick(() => { videoErrorTimer = setTimeout(() => { videoItem.value.src(props.item?.content); emit('play'); videoItem.value.load(); // eslint-disable-next-line @typescript-eslint/no-unused-vars handleErrorVideo(); }, 1000); }); videoErrorCount++; }; onMounted(() => { setContainer() videoItem.value = TCPlayer(videoID, { appID: '', controls: false, autoplay: true }); // player-container-id 为播放器容器 ID,必须与 html 中一致 __initVideo(); document.getElementById(speedBtnId)?.addEventListener('click', e => { e.stopPropagation(); data.speedControl = !data.speedControl; }); }); watch( () => props.activeModel, () => { toggleHideControl(props.activeModel); } ); watch( () => props.item, () => { videoItem.value?.currentTime(0); setTimeout(() => { videoItem.value?.pause(); __initVideo(); }, 60); } ); // 去云练习完整版 const gotoAccomany = (e: any) => { // 去云练习完整版 e.stopPropagation(); // 判断是否需要购买 if(item.value.isLock) { handleShowVip(item.value.materialMusicId, "MUSIC") onToggleAudio("pause") return } // const Authorization = sessionStorage.getItem('Authorization') || ''; // const origin = /(localhost|192)/.test(location.host) // ? 'https://test.gym.lexiaoya.cn' // : location.origin; // const src = `${origin}/${ // state.platformType === 'TEACHER' ? 'accompany-teacher' : 'accompany' // }/#/detail/${ // item.value.materialMusicId // }?Authorization=${Authorization}&isHideTitle=true`; const origin = /(localhost|192)/.test(location.host) ? 'https://test.gym.lexiaoya.cn/' : location.origin; const src = `${origin}/gym-music-score/?id=${item.value.materialMusicId}&isHideMusicList=true&systemType=${ state.platformType === 'TEACHER' ? 'teacher' : 'student'}` postMessage({ api: 'openAccompanyWebView', content: { url: src, orientation: 0, c_orientation: 0, isHideTitle: true, statusBarTextColor: false, isOpenLight: true } }); }; const getVideoRef = () => { return videoRef.value; }; const getPlyrRef = () => { return videoItem.value; }; expose({ changePlayBtn, toggleHideControl, getVideoRef, getPlyrRef }); watch( () => props.isActive, val => { if (!val) { videoItem.value?.pause(); } } ); return () => (