import { defineComponent, nextTick, onMounted, reactive, toRefs, watch } from 'vue'; import { ref } from 'vue'; import styles from './video.module.less'; // 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 { Slider } from 'vant'; // 秒转分 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); // 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); } else { videoItem.value.loop(true); } 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) { nextTick(() => { videoItem.value?.currentTime(0); }); videoItem.value.poster(props.item.coverImg); // 封面 videoItem.value.src(props.item.content); // url 播放地址 videoItem.value.playbackRate(data.defaultSpeed); // 初步加载时 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(() => { 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); videoItem.value?.pause(); setTimeout(() => { __initVideo(); }, 60); } ); 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 () => (
{ data.speedControl = false; }}>
{ e.stopPropagation(); }} // onTouchmove={(e: TouchEvent) => { // emit('close') // }} >
{getSecondRPM(data.currentTime)}
{getSecondRPM(data.duration)}
{ handleChangeTime(val); }} />
emit('close')}>
{ e.stopPropagation(); onToggleAudio(data.playState === 'pause' ? 'play' : 'pause'); }}>
{item.value.name}
{ e.stopPropagation(); }}> { if (data.defaultSpeed >= 1.5) { return; } if (videoItem.value) { data.defaultSpeed = (data.defaultSpeed * 10 + 1) / 10; videoItem.value.playbackRate(data.defaultSpeed); } }}> { if (videoItem.value) { videoItem.value.playbackRate(data.defaultSpeed); } }}> {{ button: () => (
{data.defaultSpeed} x
) }}
{ if (data.defaultSpeed <= 0.5) { return; } if (videoItem.value) { data.defaultSpeed = (data.defaultSpeed * 10 - 1) / 10; videoItem.value.playbackRate(data.defaultSpeed); } }}>
); } });