useVideo

作者: skoll | 来源:发表于2020-10-13 16:31 被阅读0次

视频组件

1 .后续可以加上支持流式访问,现在应该只能传入MP4格式
2 .可以封装下flv.js,西瓜视频那个

import * as React from 'react'
import {useEffect,useRef,useState} from 'react'
import parseTimeRanges from './parseTimeRanges'
import useFullScreen from './useFullscreen'
// 这个全屏的应该放在外面,因为hook是不能嵌套的

interface HTMLMediaProps extends React.AudioHTMLAttributes<any>,React.VideoHTMLAttributes<any>{
    src:string,
}

// xgplayer

interface HTMLMediaState{
    buffered:any[],
    duration:number,
    paused:boolean,
    muted:boolean,
    time:number,
    volume:number,
    isFullScreen:boolean,
}

interface MediaProps{
    type:"audio|video",
    // 多媒体类型
    src:string,
    // 媒体资源的地址
    autoPlay:boolean,
    // 是否自动播放
    controls:boolean,
    // 是否显示媒体资源的组件
    loop:boolean,
    // 是否循环
    muted:boolean,
    // 是否静音
    preload:string,
    // 预加载模式
    poster?:string,
    // 预览海报
}

interface HTMLMediaControls{
    play:()=>Promise<void>|void,
    pause:()=>void,
    mute:()=>void,
    unmute:()=>void,
    volume:(volume:number)=>void,
    seek:(time:number)=>void,
    pip:()=>void,
    speed:(value:number)=>void,
    setFull:()=>void,
}

type createHTMLMediaHookReturn=[
    React.ReactElement<HTMLMediaProps>,
    HTMLMediaState,
    HTMLMediaControls,
    {current:HTMLMediaElement|null}
]

function useHTMLMedia(mediaConfig:any):createHTMLMediaHookReturn{
    let el:React.ReactElement<any>|undefined
    let props:HTMLMediaProps

    const [state,setState]=useState<HTMLMediaState>({
        buffered:[],
        time:0,
        duration:0,
        paused:true,
        muted:false,
        volume:1,
        isFullScreen:false
    })

    const ref=useRef<HTMLMediaElement|null>(null)
    const [isFullScreen,{setFull,exitFull,toggleFull}]=useFullScreen(ref)
    
    function onPlay(){
        console.log('play')
        setState(Object.assign({},state,{paused:false}))
    }

    function onPause(){
        setState(Object.assign({},state,{paused:true}))
        // 对象的时候都必须这样写
        // 数组则是array.slice()

    }

    function onVolumeChange(){
        const el=ref.current

        if(!el)return
        setState(Object.assign({},state,{muted:el.muted,volume:el.volume}))
    }

    function onDurationChange(){
        const el=ref.current
        if(!el)return
        const {duration,buffered}=el
        setState(Object.assign({},state,{
            duration,
            buffered:parseTimeRanges(buffered)
        }))
    }

    // 这些只是用来更新状态的,具体的操作逻辑应该在她之前,这些只是作为钩子函数来记录变化的数值

    function onTimeUpdate(){
        const el=ref.current
        if(!el)return

        setState(Object.assign({},state,{time:el.currentTime}))
    }

    function onProgress(){
        const el=ref.current
        if(!el)return

        setState(Object.assign({},state,{buffered:parseTimeRanges(el.buffered)}))
    }

    let type=mediaConfig.type

    el=React.createElement(type,{
            ref:ref,
            ...mediaConfig,
            onPlay,
            onPause,
            onVolumeChange,
            onDurationChange,
            onTimeUpdate,
            onProgress,
    },'对不起,你的浏览器不支持播放 video !')

    let loclPlay:boolean=false
    const controls={
        play(){
            const el:any=ref.current
            if(!el)return

            if(!loclPlay){
                // 都需要强制转换
                const promise=el.play()
                const isPromise=typeof promise==='object'
                if(isPromise){
                    loclPlay=true
                    const resetLock=()=>{
                        loclPlay=false
                    }
                    promise.then(resetLock,resetLock)
                }
                return promise
            }
            return undefined
        },
        pause(){
            const el=ref.current
            if(el&&!loclPlay){
                return el.pause()
            }
        },
        seek(time:number){
            const el=ref.current
            if(!el||state.duration===undefined)return

            time=Math.min(state.duration,Math.max(0,time))
            el.currentTime=time
        },
        volume(volume:number){
            const el=ref.current
            if(!el)return
            volume=Math.min(1,Math.max(0,volume))
            el.volume=volume
            setState(Object.assign({},state,{volume:volume}))
        },
        mute(){
            const el=ref.current
            if(!el)return
            el.muted=true
        },
        unmute(){
            const el=ref.current
            if(!el)return
            el.muted=false
        },
        pip(){
            let el:any=ref.current
            let doc:any=document
            // 用any承接,不然会提示没有这个属性
            if(el!==doc.pictureInPictureElement){
                el.requestPictureInPicture()
                .catch((error:any)=>{
                    console.log('视频无法进入画中画模式!')
                })
            }else{
                doc.exitPictureInPicture()
                .catch((error:any)=>{
                    console.log('视频无法退出画中画模式!')
                })
            }
        },
        speed(value:number){
            const el=ref.current
            if(!el)return
            let speeds=[0.5,1,2,3]
            if(speeds.includes(value)){
                el.playbackRate=value
            }else{
                console.log('不能修改限定之外的速度')
            }
        },
        setFull,
    }

    useEffect(()=>{
        const el=ref.current!;

        if(!el){
            return
        }

       setState(Object.assign({},state,{
           volume:el.volume,
           muted:el.muted,
           paused:el.paused,
       }))

       if(mediaConfig.autoPlay&&el.paused){
           controls.play()
       }
       
    },[mediaConfig.src,])
    // 这里提示的补全有错误吧,还是按照自己的想法来,明确自己想要的效果是什么
    return [el,state,controls,ref]    
}
export default useHTMLMedia;

useVideo
import useHtmlMedia from './useHTMLMedia'

interface MediaProps{
    type:"audio|video",
    // 多媒体类型
    src:string,
    // 媒体资源的地址
    autoPlay:boolean,
    // 是否自动播放
    controls:boolean,
    // 是否显示媒体资源的组件
    loop:boolean,
    // 是否循环
    muted:boolean,
    // 是否静音
    preload:string,
    // 预加载模式
    poster?:string,
    // 预览海报
}

const initialMediaProps={
        controls:false,
        autoPlay:false,
        loop:false,
        muted:true,
        preload:"metadata",
        type:'video',
        poster:'',
}

function useVideo(mediaConfig:any){
    const mediaProps:MediaProps=Object.assign({},initialMediaProps,mediaConfig)
    return useHtmlMedia(mediaProps)
}


export default useVideo;

//parseTime
//当前已经缓存的数据
function parseTimeRanges(ranges:any){
    const result:{start:number,end:number}[]=[]
    for(let i=0 ; i<ranges.length; i++){
        result.push({
            start:ranges.start(i),
            end:ranges.end(i),
        })
    }
    return result
}
export default parseTimeRanges

使用

import useVideo from '../useVideo'
export default function(){
  const [video,state,controls,ref]=useVideo({
    src:'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
    autoPlay:false,
    controls:false,
  })
  return (
    <>
        {video}
        <hr/>
        <pre>{JSON.stringify(state,null,2)}</pre>
        <button onClick={controls.pause}>Pause</button>
        <button onClick={controls.play}>Play</button>
        <button onClick={controls.mute}>Mute</button>
        <button onClick={controls.unmute}>unMute</button>
        <br/>
        {/* 传值的函数需要这么写 */}
        <button onClick={() => controls.volume(.1)}>Volume: 10%</button>
        <button onClick={() => controls.volume(.5)}>Volume: 50%</button>
        <button onClick={() => controls.volume(1)}>Volume: 100%</button>
        <br/>
        <button onClick={() => controls.seek(state.time - 5)}>-5 sec</button>
        <button onClick={() => controls.seek(state.time + 5)}>+5 sec</button>
        <br/>
        <button onClick={controls.pip}>pip</button>
        <br/>
        <button onClick={()=>controls.speed(0.5)}>speed:0.5</button>
        <button onClick={()=>controls.speed(1)}>speed:1</button>
        <button onClick={()=>controls.speed(2)}>speed:2</button>
        <button onClick={()=>controls.speed(3)}>speed:3</button>
        <br/>
        <button onClick={controls.setFull}>fullScreen</button>
    </>
  );
}

1 .ondurationchange 事件在视频/音频(audio/video)的时长发生变化时触发
2 . 当视频/音频(audio/video)已经加载后,视频/音频(audio/video)的时长从 "NaN" 修改为正在的时长

相关文章

  • useVideo

    视频组件 1 .后续可以加上支持流式访问,现在应该只能传入MP4格式2 .可以封装下flv.js,西瓜视频那个 使...

网友评论

      本文标题:useVideo

      本文链接:https://www.haomeiwen.com/subject/fgivpktx.html