react video

import React, { useRef, useState, useCallback } from 'react';

import './style.scss';
const typeCheckFactory = (typeName: string) => {
    return (arg: any) => {
        if (typeof arg == typeName) {
            return true;
        } else {
            return false;
        }
    };
};
export const isNumber = typeCheckFactory('number') as (
    arg: any,
) => arg is number;
function setUnit(value: string | number) {
    return isNumber(value)
        ? value + 'px'
        : ~value.indexOf('%') || !/^\d+$/.test(value)
        ? value
        : value + 'px';
}
function zeroFill(num: number) {
    if (num < 10) {
        return '0' + num;
    }
    return num;
}

const timerTarget: { bufferTimer: NodeJS.Timeout | null } = {
    bufferTimer: null,
};
export default function(props: {
    height: number | string;
    width: number | string;
    url: string;
    coverImage: string;
}) {
    const videoHeight = setUnit(props.height);
    const videoWidth = setUnit(props.width);
    const target = useRef<HTMLVideoElement>(null);
    // 0 play, 1 stop, 2 refresh
    const [videoState, setVideoState] = useState<0 | 1 | 2>(0);
    // 声音
    const [voice, setVoice] = useState<1 | 0>(0);
    // 当前时间
    const [currentTime, setCurrentTime] = useState<string | number>(`00:00`);
    // 进度
    const [progress, setProgress] = useState<string | number>(0);
    // 播放完成 1代表完成
    const [over, setOver] = useState<1 | 0>(0);
    function play() {
        target.current && target.current.play();
    }
    function pause() {
        target.current && target.current.pause();
    }
    const toggle = useCallback(
        function(jump = false) {
            if (
                (videoState == 0 || over == 1 || jump) &&
                target &&
                target.current
            ) {
                const current = target.current;
                if (over == 1 && !jump) {
                    setProgress(0);
                    setOver(0);
                    current.currentTime = 0;
                }
                play();
                setVideoState(1);
                timerTarget.bufferTimer = setInterval(function() {
                    const pgs = current.currentTime / current.duration;
                    if (pgs == 1) {
                        timerTarget.bufferTimer &&
                            clearInterval(timerTarget.bufferTimer);
                        setOver(1);
                    }
                    setProgress(pgs * 100 + '%');
                }, 1000 / 30);
            } else {
                timerTarget.bufferTimer &&
                    clearInterval(timerTarget.bufferTimer);
                pause();
                setVideoState(0);
            }
        },
        [over, videoState],
    );

    function toggleVoice(): void {
        setVoice((v) => (1 - v) as 1 | 0);
    }

    // 处理秒数为时分秒 h:m:s
    function getTime(num: number) {
        const m = zeroFill(Math.floor(num / 60)),
            remainder = zeroFill(Math.floor(num % 60)),
            time = m + ':' + remainder;
        return time;
    }
    function progressClick(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
        e.stopPropagation();
        const currentTarget = e.currentTarget;
        const barLength = e.pageX - currentTarget.offsetLeft;
        timerTarget.bufferTimer && clearInterval(timerTarget.bufferTimer);

        if (target.current) {
            target.current.currentTime =
                (barLength / currentTarget.clientWidth) *
                target.current.duration;
            setProgress((barLength / currentTarget.clientWidth) * 100 + '%');
            toggle(true);
        }
    }
    return (
        <div
            className="jdw-video"
            style={{ height: videoHeight, width: videoWidth }}
        >
            <div
                className="mask"
                onClick={() => {
                    toggle(false);
                }}
            >
                {videoState == 0 && <div className="play"></div>}
                {videoState == 1 && (
                    <div
                        className="toolbar"
                        onClick={(e) => e.stopPropagation()}
                    >
                        <span
                            className="pause"
                            onClick={() => toggle(false)}
                        ></span>
                        <div className="progress" onClick={progressClick}>
                            <div
                                className="bar"
                                style={{ width: progress }}
                            ></div>
                        </div>
                        <span className="timer">
                            {currentTime}/
                            {getTime(
                                target && target.current
                                    ? target.current.duration
                                    : 0,
                            )}
                        </span>
                        <span
                            className={voice ? 'mute' : 'speak'}
                            onClick={toggleVoice}
                        ></span>
                    </div>
                )}
            </div>
            <video
                poster={props.coverImage}
                ref={target}
                height={props.height}
                width={props.width}
                muted={!!voice}
                onTimeUpdate={() => {
                    setCurrentTime(
                        getTime(
                            target && target.current
                                ? target.current.currentTime
                                : 0,
                        ),
                    );
                }}
            >
                <source src={props.url} type="video/ogg" />
                Your browser does not support the video tag.
            </video>
        </div>
    );
}

 

.video {
    position: relative;
    video {
        position: relative;
        z-index: 0;
    }
    .mask {
        box-sizing: border-box;
        cursor: pointer;
        height: 100%;
        position: absolute;
        width: 100%;
        z-index: 11;

        .item {
            background-position: left top;
            background-repeat: no-repeat;
            background-size: cover;
            border-radius: 50%;
            height: 22px;
            left: 50%;
            position: absolute;
            top: 50%;
            transform: translate(-50%, -50%);
            width: 22px;
        }
        .play {
            @extend .item;
            background-image: url(./images/play.png);
        }
        .refresh {
            @extend .item;
            background-image: url(./images/refresh.png);
        }
        .toolbar {
            align-items: center;
            bottom: 0;
            display: flex;
            height: 48px;
            justify-content: space-between;
            left: 20px;
            position: absolute;
            right: 20px;
            .pause {
                background: url(./images/pause.png) no-repeat center center;
                background-size: 12px 12px;
                cursor: pointer;
                height: 48px;
                margin-right: 24px;
                width: 48px;
            }
            .progress {
                cursor: pointer;
                flex-grow: 1;
                flex-shrink: 1;
                height: 12px;
                margin-right: 24px;
                position: relative;
                &:before {
                    background-color: rgba(255, 255, 255, 0.3);
                    border-radius: 4px;
                    content: '';
                    height: 4px;
                    left: 0;
                    position: absolute;
                    top: 4px;
                    width: 100%;
                }
                .bar {
                    height: 12px;
                    min-width: 12px;
                    position: relative;
                    &:before {
                        background-color: #ffffff;
                        border-radius: 50%;
                        content: '';
                        height: 12px;
                        position: absolute;
                        right: 0;
                        top: 0;
                        width: 12px;
                        z-index: 2;
                    }
                    &:after {
                        background-color: rgba(242, 23, 12, 1);
                        border-radius: 4px;
                        content: '';
                        height: 4px;
                        left: 0;
                        position: absolute;
                        top: 4px;
                        width: 100%;
                    }
                }
            }
            .timer {
                color: rgba(255, 255, 255, 1);
                font-size: 20px;
                height: 48px;
                line-height: 48px;
                margin-right: 24px;
                min-width: 115px;
            }
            .voice {
                background-position: center center;
                background-repeat: no-repeat;
                background-size: 24px 24px;
                cursor: pointer;
                height: 48px;
                width: 48px;
            }
            .mute {
                @extend .voice;
                background-image: url(./images/mute.png);
            }
            .speak {
                @extend .voice;
                background-image: url(./images/voice.png);
            }
        }
    }
}

 

posted on 2021-08-06 14:18  KyleLjc  阅读(393)  评论(0编辑  收藏  举报

导航