master
lgf 3 years ago
parent 872af4b6d7
commit d421bb6559
  1. 1
      src/appComponent.tsx
  2. 36
      src/components/screenshot/index.module.scss
  3. 23
      src/components/screenshot/index.tsx
  4. 124
      src/core/controller/index.tsx
  5. 533
      src/core/controls/index.tsx
  6. 9
      src/core/index.tsx
  7. 2
      src/core/progress/index.tsx
  8. 1
      src/icons/svg/close.svg
  9. 24
      src/interface/index.ts
  10. 11
      src/utils/index.ts

@ -79,6 +79,7 @@ const AppComponent = memo(function AppComponent(props) {
width: 750, width: 750,
height: 420, height: 420,
theme: '#00D3FF', theme: '#00D3FF',
isShowMultiple: false,
poster: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG', poster: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG',
}} }}
onProgressMouseDown={onProgressMouseDown} onProgressMouseDown={onProgressMouseDown}

@ -0,0 +1,36 @@
@import 'src/assets/css/mixin.scss';
.screenshot {
width: 280px;
@include position(absolute, auto, 0, 55px, auto);
background: #fff;
padding: 3px;
overflow: hidden;
.img {
height: 155px;
overflow: hidden;
position: relative;
canvas {
@include position(absolute, 50%, auto, auto, 50%);
transform: translate(-50%, -50%);
}
}
.close {
@include position(absolute, -24px, -24px, auto, auto);
@include wh(0, 0);
z-index: 1;
border: 26px solid transparent;
border-left-color: #fff;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
border-radius: 100%;
.icon {
@include position(absolute, -8px, 5px, auto, auto);
transform: rotate(45deg);
cursor: pointer;
}
}
.save {
padding: 10px;
text-align: center;
}
}

@ -0,0 +1,23 @@
import React, { memo, FC } from 'react';
import style from './index.module.scss';
import Broadcast from '@/components/svgIcon';
const Index: FC<{
setIsscreenshot: Function;
screenshotLoading: boolean;
}> = memo(function Index({ setIsscreenshot, screenshotLoading }) {
return (
<div className={style.screenshot}>
<div className={style.close} onClick={() => setIsscreenshot(false)}>
<Broadcast iconClass="close" fill="red" className={style.icon} />
</div>
<div className={style.img} id="JoL-screenshotCanvas">
<Broadcast iconClass="loading" className="player-loading" fontSize="55px" />
</div>
<p className={style.save}>
{screenshotLoading ? '截图加载失败,在点击下' : '鼠标右键图片另存为'}
</p>
</div>
);
});
export default Index;

@ -1,17 +1,14 @@
import React, { memo, useContext, useRef, useState, useMemo } from 'react'; import React, { memo, useContext, useRef, useMemo, useEffect, useState } from 'react';
import Broadcast from '@/components/svgIcon'; import Broadcast from '@/components/svgIcon';
import Progress from '../progress'; import Progress from '../progress';
import Controls from '../controls'; import Controls from '../controls';
import { FlowContext } from '@/core/context'; import { FlowContext } from '@/core/context';
import { useVideo } from '@/core/useVideo'; import { useVideo } from '@/core/useVideo';
import useWindowClient from '@/utils/useWindowClient';
import usePrevious from '@/utils/usePrevious';
import EndComponent from '@/components/end'; import EndComponent from '@/components/end';
import Screenshot from '@/components/screenshot';
import './index.scss'; import './index.scss';
const Index = memo(function Index(props) { const Index = memo(function Index() {
const { clientX } = useWindowClient();
const reviceProps = useContext(FlowContext); const reviceProps = useContext(FlowContext);
const { dispatch, propsAttributes } = reviceProps; const { dispatch, propsAttributes } = reviceProps;
@ -25,57 +22,72 @@ const Index = memo(function Index(props) {
const timer = useRef<NodeJS.Timeout | null>(null!); const timer = useRef<NodeJS.Timeout | null>(null!);
const viewClientX = useRef<number>(null!);
const prevCalculation = useRef<number>(null!);
const controllerRef = useRef<HTMLDivElement>(null!); const controllerRef = useRef<HTMLDivElement>(null!);
const [pre, setPre] = useState<number>(0);
viewClientX.current = clientX;
/** /**
* @description * @description
*/ */
prevCalculation.current = usePrevious(pre) as number; const userActivity = useRef<boolean>(false);
const inactivityTimeout = useRef<NodeJS.Timeout | null>(null!);
/** /**
* @description * @description
*/ */
const showControl = (e: any, status: string) => { const isControlsContainerMove = useRef<boolean>(false);
dispatch!({ type: 'isControl', data: status === 'enter' && !isEndEd ? true : false });
}; const [isScreenshot, setIsscreenshot] = useState<boolean>(false);
/**
* @description const [screenshotLoading, setScreenshotLoading] = useState<boolean>(false);
*/
const hiddleCursor = () => { useEffect(() => {
if (timer.current) {
clearInterval(timer.current);
}
timer.current = setInterval(() => { timer.current = setInterval(() => {
setPre(viewClientX.current); if (userActivity.current) {
/** /**
* @description 1200ms没有任何操作的话 * @description
*/ */
if (viewClientX.current! !== prevCalculation.current) { userActivity.current = false;
dispatch!({ type: 'isControl', data: true }); dispatch!({ type: 'isControl', data: true });
controllerRef.current.style.cursor = 'pointer'; controllerRef.current.style.cursor = 'pointer';
} else { inactivityTimeout.current && clearTimeout(inactivityTimeout.current);
dispatch!({ type: 'isControl', data: false }); inactivityTimeout.current = setTimeout(
controllerRef.current.style.cursor = 'none'; () => {
/**
* @note Controls上时
*/
if (!userActivity.current && !isControlsContainerMove.current) {
dispatch!({ type: 'isControl', data: false });
controllerRef.current.style.cursor = 'none';
}
},
propsAttributes!.hideMouseTime ? propsAttributes!.hideMouseTime : 2000,
);
} }
}, 1200); }, 200);
}; return () => {
const clearTimer = () => { timer.current && clearInterval(timer.current);
controllerRef.current.style.cursor = 'pointer'; };
if (timer.current) { }, []);
clearInterval(timer.current);
} /**
* @description
*/
const showControl = (status: string) => {
dispatch!({ type: 'isControl', data: status === 'enter' && !isEndEd ? true : false });
}; };
const handlePlay = () => { const handlePlay = () => {
handleChangePlayState && handleChangePlayState(); handleChangePlayState && handleChangePlayState();
}; };
const mouseMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
userActivity.current = true;
/**
* @note false,falsebug
*/
isControlsContainerMove.current = false;
};
const leaveMove: React.MouseEventHandler<HTMLDivElement> = (e) => {
userActivity.current = false;
controllerRef.current.style.cursor = 'pointer';
};
/** /**
* @description * @description
*/ */
@ -93,18 +105,25 @@ const Index = memo(function Index(props) {
}; };
} }
}, [propsAttributes!.pausePlacement]); }, [propsAttributes!.pausePlacement]);
/**
* @param status
* @description
*/
const controlsContainerMove = (status: string) => {
isControlsContainerMove.current = status === 'move' ? true : false;
};
return ( return (
<div <div
className="JoL-controller-container" className="JoL-controller-container"
onMouseEnter={(e) => showControl(e, 'enter')} onMouseEnter={(e) => [showControl('enter')]}
onMouseLeave={(e) => showControl(e, 'leave')} onMouseLeave={(e) => [showControl('leave'), e.stopPropagation()]}
ref={controllerRef} ref={controllerRef}
> >
<div <div
id="play-or-pause-mask" id="play-or-pause-mask"
className="JoL-click-to-play-or-pause" className="JoL-click-to-play-or-pause"
onMouseEnter={hiddleCursor} onMouseLeave={leaveMove}
onMouseLeave={clearTimer} onMouseMove={mouseMove}
onClick={handlePlay} onClick={handlePlay}
></div> ></div>
{!isPlay && !isEndEd && ( {!isPlay && !isEndEd && (
@ -116,15 +135,22 @@ const Index = memo(function Index(props) {
style={pausePosition} style={pausePosition}
/> />
)} )}
<div className="JoL-progress-and-controls-wrap"> <div
className="JoL-progress-and-controls-wrap"
onMouseMove={(e) => [controlsContainerMove('move')]}
onMouseLeave={(e) => [controlsContainerMove('leave')]}
>
{isScreenshot ? (
<Screenshot setIsscreenshot={setIsscreenshot} screenshotLoading={screenshotLoading} />
) : null}
<Progress /> <Progress />
<Controls /> <Controls setIsscreenshot={setIsscreenshot} setScreenshotLoading={setScreenshotLoading} />
</div> </div>
{isEndEd ? ( {isEndEd ? (
propsAttributes!.setEndPlayContent ? ( propsAttributes!.setEndPlayContent ? (
propsAttributes!.setEndPlayContent propsAttributes!.setEndPlayContent
) : ( ) : (
<EndComponent handle={() => [handleChangePlayState(), hiddleCursor()]} /> <EndComponent handle={() => [handleChangePlayState(), showControl('enter')]} />
) )
) : null} ) : null}
</div> </div>

@ -1,9 +1,9 @@
import React, { memo, useContext, useRef, useEffect, useMemo, useCallback } from 'react'; import React, { memo, useContext, useRef, useEffect, useMemo, FC } from 'react';
import Broadcast from '@/components/svgIcon'; import Broadcast from '@/components/svgIcon';
import Tooltip from '@/components/tooltip'; import Tooltip from '@/components/tooltip';
import { FlowContext } from '@/core/context'; import { contextType, FlowContext } from '@/core/context';
import { useVideo } from '@/core/useVideo'; import { useVideo } from '@/core/useVideo';
import { secondsToMinutesAndSecondes, createALabel } from '@/utils'; import { secondsToMinutesAndSecondes, capture } from '@/utils';
import { useControls } from './variable'; import { useControls } from './variable';
import useWindowClient from '@/utils/useWindowClient'; import useWindowClient from '@/utils/useWindowClient';
import screenfull, { Screenfull } from 'screenfull'; import screenfull, { Screenfull } from 'screenfull';
@ -14,286 +14,295 @@ import VolumeComponent from './volume';
import MonitorComponent from './monitor'; import MonitorComponent from './monitor';
import './index.scss'; import './index.scss';
const Index = memo(function Index(props) { const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> = memo(
/** function Index({ setIsscreenshot, setScreenshotLoading }) {
* @description /**
*/ * @description
const volumeSliderMirror = useRef<HTMLDivElement>(null!); */
const volumeSliderMirror = useRef<HTMLDivElement>(null!);
const clientYdistance = useRef<number>(0); const clientYdistance = useRef<number>(0);
const volumeInterval = useRef<NodeJS.Timeout | null>(null); const volumeInterval = useRef<NodeJS.Timeout | null>(null);
const reviceProps = useContext(FlowContext); const reviceProps = useContext(FlowContext);
const revicePropsData = useRef<any>(); const { propsAttributes } = reviceProps!;
const { isPlay, handleChangePlayState, currentTime, duration, isPictureinpicture, volume } = const revicePropsData = useRef<contextType>();
useVideo(
{
videoElement: reviceProps.videoRef,
},
[reviceProps.videoRef],
);
const { controlsState, dispatch } = useControls(); const { isPlay, handleChangePlayState, currentTime, duration, isPictureinpicture, volume } =
useVideo(
{
videoElement: reviceProps.videoRef,
},
[reviceProps.videoRef],
);
const { clientY } = useWindowClient(); const { controlsState, dispatch } = useControls();
clientYdistance.current = clientY; const { clientY } = useWindowClient();
revicePropsData.current = reviceProps; clientYdistance.current = clientY;
useEffect(() => { revicePropsData.current = reviceProps;
/**
* @description setVolume函数video的数据一致
*/
dispatch({ type: 'volume', data: Math.floor(volume * 100) });
}, [volume]);
useEffect(() => { useEffect(() => {
// 为了防止音量滑动元素计时器不暂停 /**
window.addEventListener('mouseup', whenMouseUpDo); * @description setVolume函数video的数据一致
return () => { */
window.removeEventListener('mouseup', whenMouseUpDo); dispatch({ type: 'volume', data: Math.floor(volume * 100) });
}; }, [volume]);
}, []);
/** useEffect(() => {
* @description // 为了防止音量滑动元素计时器不暂停
*/ window.addEventListener('mouseup', whenMouseUpDo);
const updateCurrentVolume = (volumePercent: number) => { return () => {
const videoRef = reviceProps.videoRef!; window.removeEventListener('mouseup', whenMouseUpDo);
if (volumePercent >= 0 && volumePercent <= 1) { };
videoRef.volume = volumePercent; }, []);
videoRef.muted = false;
dispatch({ type: 'volume', data: volumePercent * 100 });
dispatch({ type: 'isMuted', data: Math.floor(volumePercent * 100) === 0 ? true : false });
}
if (volumePercent < 0) {
videoRef.volume = 0;
videoRef.muted = true;
dispatch({ type: 'volume', data: 0 });
dispatch({ type: 'isMuted', data: true });
}
if (volumePercent > 1) {
videoRef.volume = 1;
dispatch({ type: 'volume', data: 100 });
}
};
/**
*
* @param e event对象
* @description
*/
const changeCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
const volumeSliderMirrorElement = (
volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement }
).element;
// 获取音量区域高度
const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight;
// 获取当前位置在整个音量区域高度的占比
const volumePercent =
1 - (e.clientY - volumeSliderMirrorElement.getBoundingClientRect().top) / volumeAreaHeight;
// 修改当前音量大小
updateCurrentVolume(volumePercent);
};
const slideCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => { /**
e.stopPropagation(); * @description
const volumeSliderMirrorElement = ( */
volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement } const updateCurrentVolume = (volumePercent: number) => {
).element; const videoRef = reviceProps.videoRef!;
// 获取音量区域高度 if (volumePercent >= 0 && volumePercent <= 1) {
const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight; videoRef.volume = volumePercent;
// 防止点击的时候,再次出发计时器,重而造成点击卡顿 videoRef.muted = false;
volumeInterval.current && clearInterval(volumeInterval.current); dispatch({ type: 'volume', data: volumePercent * 100 });
volumeInterval.current = setInterval(() => { dispatch({ type: 'isMuted', data: Math.floor(volumePercent * 100) === 0 ? true : false });
}
if (volumePercent < 0) {
videoRef.volume = 0;
videoRef.muted = true;
dispatch({ type: 'volume', data: 0 });
dispatch({ type: 'isMuted', data: true });
}
if (volumePercent > 1) {
videoRef.volume = 1;
dispatch({ type: 'volume', data: 100 });
}
};
/**
*
* @param e event对象
* @description
*/
const changeCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
const volumeSliderMirrorElement = (
volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement }
).element;
// 获取音量区域高度
const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight;
// 获取当前位置在整个音量区域高度的占比 // 获取当前位置在整个音量区域高度的占比
const volumePercent = const volumePercent =
1 - 1 - (e.clientY - volumeSliderMirrorElement.getBoundingClientRect().top) / volumeAreaHeight;
(clientYdistance.current - volumeSliderMirrorElement.getBoundingClientRect().top) /
volumeAreaHeight;
// 修改当前音量大小 // 修改当前音量大小
updateCurrentVolume(volumePercent); updateCurrentVolume(volumePercent);
dispatch({ type: 'isSlideVolume', data: true }); };
}, 1);
}; const slideCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => {
// 当鼠标抬起时 e.stopPropagation();
const whenMouseUpDo = () => { const volumeSliderMirrorElement = (
volumeInterval.current && clearInterval(volumeInterval.current); volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement }
dispatch({ type: 'isSlideVolume', data: false }); ).element;
}; // 获取音量区域高度
const clearVolumeInterval: React.MouseEventHandler<HTMLDivElement> = (e) => { const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight;
e.stopPropagation(); // 防止点击的时候,再次出发计时器,重而造成点击卡顿
whenMouseUpDo(); volumeInterval.current && clearInterval(volumeInterval.current);
}; volumeInterval.current = setInterval(() => {
/** // 获取当前位置在整个音量区域高度的占比
* @description const volumePercent =
*/ 1 -
const requestFullScreen = () => { (clientYdistance.current - volumeSliderMirrorElement.getBoundingClientRect().top) /
if (screenfull.isEnabled) { volumeAreaHeight;
screenfull.toggle(reviceProps.videoContainerRef!); // 修改当前音量大小
screenfull.on('change', () => updateCurrentVolume(volumePercent);
dispatch({ type: 'isScreentFull', data: (screenfull as Screenfull).isFullscreen }), dispatch({ type: 'isSlideVolume', data: true });
); }, 1);
} };
}; // 当鼠标抬起时
/** const whenMouseUpDo = () => {
* @description volumeInterval.current && clearInterval(volumeInterval.current);
*/ dispatch({ type: 'isSlideVolume', data: false });
const pictureInPicture = () => { };
if (isPictureinpicture) { const clearVolumeInterval: React.MouseEventHandler<HTMLDivElement> = (e) => {
(document as any).exitPictureInPicture(); e.stopPropagation();
} else { whenMouseUpDo();
(reviceProps.videoRef! as any).requestPictureInPicture(); };
} /**
}; * @description
/** */
* @description const requestFullScreen = () => {
*/ if (screenfull.isEnabled) {
const selectPlayRate = (playbackRate: number) => { screenfull.toggle(reviceProps.videoContainerRef!);
reviceProps.videoRef!.playbackRate = playbackRate; screenfull.on('change', () =>
dispatch({ type: 'multiple', data: playbackRate }); dispatch({ type: 'isScreentFull', data: (screenfull as Screenfull).isFullscreen }),
}; );
const multipleText = useMemo(() => {
if (controlsState.multiple === 1.0) {
return '倍数';
} else {
return multipleList.filter((item) => item.id === controlsState.multiple)[0].name;
}
}, [controlsState.multiple]);
/**
* @description 线
*/
const screenshot = () => {
const canvas = document.createElement('canvas') as HTMLCanvasElement;
canvas.width = reviceProps.videoRef!.offsetWidth;
canvas.height = reviceProps.videoRef!.offsetHeight;
const context = canvas.getContext('2d')!;
context.drawImage(reviceProps.videoRef!, 0, 0, canvas.width, canvas.height);
try {
createALabel(canvas.toDataURL('image/png'));
} catch (error) {}
};
/**
* @description
*/
const switchChange = (e: string, flag: string) => {
const { videoRef, lightOffMaskRef } = revicePropsData.current;
if (flag === 'lights') {
if (lightOffMaskRef) {
lightOffMaskRef.style.display = e === 'yes' ? 'block' : 'none';
} }
} else { };
const loop = videoRef.loop; /**
videoRef.loop = loop ? false : true; * @description
} */
}; const pictureInPicture = () => {
/** if (isPictureinpicture) {
* @description (document as any).exitPictureInPicture();
*/ } else {
const clientFullScreen = () => { (reviceProps.videoRef! as any).requestPictureInPicture();
const videoContainerRef = reviceProps.videoContainerRef!; }
if (videoContainerRef.classList.contains('clientFullScreen')) { };
videoContainerRef.classList.remove('clientFullScreen'); /**
dispatch({ type: 'isWebPageFullScreen', data: false }); * @description
} else { */
videoContainerRef.classList.add('clientFullScreen'); const selectPlayRate = (playbackRate: number) => {
dispatch({ type: 'isWebPageFullScreen', data: true }); reviceProps.videoRef!.playbackRate = playbackRate;
} dispatch({ type: 'multiple', data: playbackRate });
}; };
/** const multipleText = useMemo(() => {
* @description if (controlsState.multiple === 1.0) {
*/ return '倍数';
const toggleVolume = () => { } else {
reviceProps.videoRef!.volume = controlsState.isMuted ? defaultVolume / 100 : 0; return multipleList.filter((item) => item.id === controlsState.multiple)[0].name;
dispatch({ type: 'isMuted', data: controlsState.isMuted ? false : true }); }
reviceProps.videoRef!.muted = controlsState.isMuted ? false : true; }, [controlsState.multiple]);
}; /**
* @description 线
*/
const screenshot = async () => {
const output = document.querySelector('#JoL-screenshotCanvas')!;
const canvas = capture(reviceProps.videoRef!, 0.45);
setIsscreenshot(true);
if (output) {
setScreenshotLoading(false);
output.innerHTML = '';
output.appendChild(canvas);
} else {
setScreenshotLoading(true);
}
};
/**
* @description
*/
const switchChange = (e: string, flag: string) => {
const { videoRef, lightOffMaskRef } = revicePropsData.current!;
if (flag === 'lights') {
if (lightOffMaskRef) {
lightOffMaskRef.style.display = e === 'yes' ? 'block' : 'none';
}
} else {
const loop = videoRef!.loop;
videoRef!.loop = loop ? false : true;
}
};
/**
* @description
*/
const clientFullScreen = () => {
const videoContainerRef = reviceProps.videoContainerRef!;
if (videoContainerRef.classList.contains('clientFullScreen')) {
videoContainerRef.classList.remove('clientFullScreen');
dispatch({ type: 'isWebPageFullScreen', data: false });
} else {
videoContainerRef.classList.add('clientFullScreen');
dispatch({ type: 'isWebPageFullScreen', data: true });
}
};
/**
* @description
*/
const toggleVolume = () => {
reviceProps.videoRef!.volume = controlsState.isMuted ? defaultVolume / 100 : 0;
dispatch({ type: 'isMuted', data: controlsState.isMuted ? false : true });
reviceProps.videoRef!.muted = controlsState.isMuted ? false : true;
};
return ( return (
<div <div
className="JoL-controls-container" className="JoL-controls-container"
style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }} // style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
> >
<MonitorComponent <MonitorComponent
isPlay={isPlay} isPlay={isPlay}
handleChangePlayState={handleChangePlayState} handleChangePlayState={handleChangePlayState}
currentTime={secondsToMinutesAndSecondes(currentTime)} currentTime={secondsToMinutesAndSecondes(currentTime)}
totalTime={secondsToMinutesAndSecondes(duration)} totalTime={secondsToMinutesAndSecondes(duration)}
/>
<div className="JoL-multifunction">
<MultipleComponent
multipleText={multipleText}
multiple={controlsState.multiple}
selectPlayRate={selectPlayRate}
/>
<VolumeComponent
ref={volumeSliderMirror}
volume={controlsState.volume}
changeCurrentVolume={changeCurrentVolume}
slideCurrentVolume={slideCurrentVolume}
clearVolumeInterval={clearVolumeInterval}
isMuted={controlsState.isMuted}
toggleVolume={toggleVolume}
/>
<SetComponent switchChange={switchChange} />
<Tooltip
styleCss={{ padding: '0 5px' }}
title="截图"
icon={
<Broadcast
iconClass="screenshot"
className="hover-icon-animate"
fill="#fff"
onClick={screenshot}
/>
}
/> />
<Tooltip <div className="JoL-multifunction">
styleCss={{ padding: '0 5px' }} {propsAttributes!.isShowMultiple && (
title={isPictureinpicture ? '关闭画中画' : '开启画中画'} <MultipleComponent
icon={ multipleText={multipleText}
<Broadcast multiple={controlsState.multiple}
iconClass="fullScreen" selectPlayRate={selectPlayRate}
fill="#fff"
className="hover-icon-animate"
fontSize={'20px'}
onClick={pictureInPicture}
/> />
} )}
/>
<Tooltip <VolumeComponent
styleCss={{ padding: '0 5px' }} ref={volumeSliderMirror}
title="网页全屏" volume={controlsState.volume}
icon={ changeCurrentVolume={changeCurrentVolume}
<Broadcast slideCurrentVolume={slideCurrentVolume}
iconClass="fullScreen" clearVolumeInterval={clearVolumeInterval}
fill="#fff" isMuted={controlsState.isMuted}
className="hover-icon-animate" toggleVolume={toggleVolume}
fontSize={'20px'} />
onClick={clientFullScreen} <SetComponent switchChange={switchChange} />
/> <Tooltip
} styleCss={{ padding: '0 5px' }}
/> title="截图"
<Tooltip icon={
styleCss={{ padding: '0 5px' }} <Broadcast
title={controlsState.isScreentFull ? '退出全屏' : '全屏'} iconClass="screenshot"
icon={ className="hover-icon-animate"
<Broadcast fill="#fff"
iconClass="fullScreen" onClick={screenshot}
fill="#fff" />
fontSize={'20px'} }
onClick={requestFullScreen} />
className="hover-icon-animate" <Tooltip
/> styleCss={{ padding: '0 5px' }}
} title={isPictureinpicture ? '关闭画中画' : '开启画中画'}
/> icon={
<Broadcast
iconClass="fullScreen"
fill="#fff"
className="hover-icon-animate"
fontSize={'20px'}
onClick={pictureInPicture}
/>
}
/>
<Tooltip
styleCss={{ padding: '0 5px' }}
title="网页全屏"
icon={
<Broadcast
iconClass="fullScreen"
fill="#fff"
className="hover-icon-animate"
fontSize={'20px'}
onClick={clientFullScreen}
/>
}
/>
<Tooltip
styleCss={{ padding: '0 5px' }}
title={controlsState.isScreentFull ? '退出全屏' : '全屏'}
icon={
<Broadcast
iconClass="fullScreen"
fill="#fff"
fontSize={'20px'}
onClick={requestFullScreen}
className="hover-icon-animate"
/>
}
/>
</div>
</div> </div>
</div> );
); },
}); );
export default Index; export default Index;

@ -175,5 +175,12 @@ const JoLPlayer = function JoLPlayer(props: videoparameter, ref: React.Ref<unkno
}; };
const JoLPlayerComponent = forwardRef<JoLPlayerRef, videoparameter>(JoLPlayer); const JoLPlayerComponent = forwardRef<JoLPlayerRef, videoparameter>(JoLPlayer);
JoLPlayerComponent.defaultProps = {
option: {
width: 750,
height: 420,
videoSrc: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG',
isShowMultiple: true,
},
};
export default JoLPlayerComponent; export default JoLPlayerComponent;

@ -172,7 +172,7 @@ const Index = memo(function Index(props) {
return ( return (
<div <div
className="JoL-progress-container" className="JoL-progress-container"
style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }} // style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
> >
<div className="progress-bg" ref={progressBgRef}> <div className="progress-bg" ref={progressBgRef}>
<div className="progress-buffered" style={{ width: `${calculateBufferedPercent}%` }}></div> <div className="progress-buffered" style={{ width: `${calculateBufferedPercent}%` }}></div>

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1629344784407" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6951" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M783.36 195.2L512 466.56 240.64 195.2a32 32 0 0 0-45.44 45.44L466.56 512l-271.36 271.36a32 32 0 0 0 45.44 45.44L512 557.44l271.36 271.36a32 32 0 0 0 45.44-45.44L557.44 512l271.36-271.36a32 32 0 0 0-45.44-45.44z" p-id="6952"></path></svg>

After

Width:  |  Height:  |  Size: 614 B

@ -40,6 +40,30 @@ export interface videoOption<T = string, K = boolean, U = number> {
* @description * @description
*/ */
pausePlacement?: pausePlacement; pausePlacement?: pausePlacement;
/**
* @description /ms
*/
hideMouseTime?: U;
/**
* @description
*/
isShowMultiple?: K;
/**
* @description
*/
isShowSet?: K;
/**
* @description
*/
isShowScreenshot?: K;
/**
* @description
*/
isShowPicture?: K;
/**
* @description
*/
isShowWebFullScreen?: K;
} }
export interface videoAttributes<T = number, K = boolean> { export interface videoAttributes<T = number, K = boolean> {
/** /**

@ -37,3 +37,14 @@ export const createALabel = (path: string, fileName: string = 'JoL-player.png')
link.remove(); link.remove();
window.URL.revokeObjectURL(path); window.URL.revokeObjectURL(path);
}; };
export const capture = (video: HTMLVideoElement, scaleFactor: number = 0.25) => {
var w = video.videoWidth * scaleFactor;
var h = video.videoHeight * scaleFactor;
var canvas = document.createElement('canvas') as HTMLCanvasElement;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext('2d');
ctx!.drawImage(video, 0, 0, w, h);
return canvas;
};

Loading…
Cancel
Save