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,
height: 420,
theme: '#00D3FF',
isShowMultiple: false,
poster: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG',
}}
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 Progress from '../progress';
import Controls from '../controls';
import { FlowContext } from '@/core/context';
import { useVideo } from '@/core/useVideo';
import useWindowClient from '@/utils/useWindowClient';
import usePrevious from '@/utils/usePrevious';
import EndComponent from '@/components/end';
import Screenshot from '@/components/screenshot';
import './index.scss';
const Index = memo(function Index(props) {
const { clientX } = useWindowClient();
const Index = memo(function Index() {
const reviceProps = useContext(FlowContext);
const { dispatch, propsAttributes } = reviceProps;
@ -25,57 +22,72 @@ const Index = memo(function Index(props) {
const timer = useRef<NodeJS.Timeout | null>(null!);
const viewClientX = useRef<number>(null!);
const prevCalculation = useRef<number>(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) => {
dispatch!({ type: 'isControl', data: status === 'enter' && !isEndEd ? true : false });
};
/**
* @description
*/
const hiddleCursor = () => {
if (timer.current) {
clearInterval(timer.current);
}
const isControlsContainerMove = useRef<boolean>(false);
const [isScreenshot, setIsscreenshot] = useState<boolean>(false);
const [screenshotLoading, setScreenshotLoading] = useState<boolean>(false);
useEffect(() => {
timer.current = setInterval(() => {
setPre(viewClientX.current);
/**
* @description 1200ms没有任何操作的话
*/
if (viewClientX.current! !== prevCalculation.current) {
if (userActivity.current) {
/**
* @description
*/
userActivity.current = false;
dispatch!({ type: 'isControl', data: true });
controllerRef.current.style.cursor = 'pointer';
} else {
dispatch!({ type: 'isControl', data: false });
controllerRef.current.style.cursor = 'none';
inactivityTimeout.current && clearTimeout(inactivityTimeout.current);
inactivityTimeout.current = setTimeout(
() => {
/**
* @note Controls上时
*/
if (!userActivity.current && !isControlsContainerMove.current) {
dispatch!({ type: 'isControl', data: false });
controllerRef.current.style.cursor = 'none';
}
},
propsAttributes!.hideMouseTime ? propsAttributes!.hideMouseTime : 2000,
);
}
}, 1200);
};
const clearTimer = () => {
controllerRef.current.style.cursor = 'pointer';
if (timer.current) {
clearInterval(timer.current);
}
}, 200);
return () => {
timer.current && clearInterval(timer.current);
};
}, []);
/**
* @description
*/
const showControl = (status: string) => {
dispatch!({ type: 'isControl', data: status === 'enter' && !isEndEd ? true : false });
};
const handlePlay = () => {
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
*/
@ -93,18 +105,25 @@ const Index = memo(function Index(props) {
};
}
}, [propsAttributes!.pausePlacement]);
/**
* @param status
* @description
*/
const controlsContainerMove = (status: string) => {
isControlsContainerMove.current = status === 'move' ? true : false;
};
return (
<div
className="JoL-controller-container"
onMouseEnter={(e) => showControl(e, 'enter')}
onMouseLeave={(e) => showControl(e, 'leave')}
onMouseEnter={(e) => [showControl('enter')]}
onMouseLeave={(e) => [showControl('leave'), e.stopPropagation()]}
ref={controllerRef}
>
<div
id="play-or-pause-mask"
className="JoL-click-to-play-or-pause"
onMouseEnter={hiddleCursor}
onMouseLeave={clearTimer}
onMouseLeave={leaveMove}
onMouseMove={mouseMove}
onClick={handlePlay}
></div>
{!isPlay && !isEndEd && (
@ -116,15 +135,22 @@ const Index = memo(function Index(props) {
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 />
<Controls />
<Controls setIsscreenshot={setIsscreenshot} setScreenshotLoading={setScreenshotLoading} />
</div>
{isEndEd ? (
propsAttributes!.setEndPlayContent ? (
propsAttributes!.setEndPlayContent
) : (
<EndComponent handle={() => [handleChangePlayState(), hiddleCursor()]} />
<EndComponent handle={() => [handleChangePlayState(), showControl('enter')]} />
)
) : null}
</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 Tooltip from '@/components/tooltip';
import { FlowContext } from '@/core/context';
import { contextType, FlowContext } from '@/core/context';
import { useVideo } from '@/core/useVideo';
import { secondsToMinutesAndSecondes, createALabel } from '@/utils';
import { secondsToMinutesAndSecondes, capture } from '@/utils';
import { useControls } from './variable';
import useWindowClient from '@/utils/useWindowClient';
import screenfull, { Screenfull } from 'screenfull';
@ -14,286 +14,295 @@ import VolumeComponent from './volume';
import MonitorComponent from './monitor';
import './index.scss';
const Index = memo(function Index(props) {
/**
* @description
*/
const volumeSliderMirror = useRef<HTMLDivElement>(null!);
const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> = memo(
function Index({ setIsscreenshot, setScreenshotLoading }) {
/**
* @description
*/
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 } =
useVideo(
{
videoElement: reviceProps.videoRef,
},
[reviceProps.videoRef],
);
const revicePropsData = useRef<contextType>();
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(() => {
/**
* @description setVolume函数video的数据一致
*/
dispatch({ type: 'volume', data: Math.floor(volume * 100) });
}, [volume]);
revicePropsData.current = reviceProps;
useEffect(() => {
// 为了防止音量滑动元素计时器不暂停
window.addEventListener('mouseup', whenMouseUpDo);
return () => {
window.removeEventListener('mouseup', whenMouseUpDo);
};
}, []);
useEffect(() => {
/**
* @description setVolume函数video的数据一致
*/
dispatch({ type: 'volume', data: Math.floor(volume * 100) });
}, [volume]);
/**
* @description
*/
const updateCurrentVolume = (volumePercent: number) => {
const videoRef = reviceProps.videoRef!;
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);
};
useEffect(() => {
// 为了防止音量滑动元素计时器不暂停
window.addEventListener('mouseup', whenMouseUpDo);
return () => {
window.removeEventListener('mouseup', whenMouseUpDo);
};
}, []);
const slideCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
const volumeSliderMirrorElement = (
volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement }
).element;
// 获取音量区域高度
const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight;
// 防止点击的时候,再次出发计时器,重而造成点击卡顿
volumeInterval.current && clearInterval(volumeInterval.current);
volumeInterval.current = setInterval(() => {
/**
* @description
*/
const updateCurrentVolume = (volumePercent: number) => {
const videoRef = reviceProps.videoRef!;
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 -
(clientYdistance.current - volumeSliderMirrorElement.getBoundingClientRect().top) /
volumeAreaHeight;
1 - (e.clientY - volumeSliderMirrorElement.getBoundingClientRect().top) / volumeAreaHeight;
// 修改当前音量大小
updateCurrentVolume(volumePercent);
dispatch({ type: 'isSlideVolume', data: true });
}, 1);
};
// 当鼠标抬起时
const whenMouseUpDo = () => {
volumeInterval.current && clearInterval(volumeInterval.current);
dispatch({ type: 'isSlideVolume', data: false });
};
const clearVolumeInterval: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
whenMouseUpDo();
};
/**
* @description
*/
const requestFullScreen = () => {
if (screenfull.isEnabled) {
screenfull.toggle(reviceProps.videoContainerRef!);
screenfull.on('change', () =>
dispatch({ type: 'isScreentFull', data: (screenfull as Screenfull).isFullscreen }),
);
}
};
/**
* @description
*/
const pictureInPicture = () => {
if (isPictureinpicture) {
(document as any).exitPictureInPicture();
} else {
(reviceProps.videoRef! as any).requestPictureInPicture();
}
};
/**
* @description
*/
const selectPlayRate = (playbackRate: number) => {
reviceProps.videoRef!.playbackRate = playbackRate;
dispatch({ type: 'multiple', data: playbackRate });
};
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';
};
const slideCurrentVolume: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
const volumeSliderMirrorElement = (
volumeSliderMirror.current as HTMLDivElement & { element: HTMLDivElement }
).element;
// 获取音量区域高度
const volumeAreaHeight = volumeSliderMirrorElement.offsetHeight;
// 防止点击的时候,再次出发计时器,重而造成点击卡顿
volumeInterval.current && clearInterval(volumeInterval.current);
volumeInterval.current = setInterval(() => {
// 获取当前位置在整个音量区域高度的占比
const volumePercent =
1 -
(clientYdistance.current - volumeSliderMirrorElement.getBoundingClientRect().top) /
volumeAreaHeight;
// 修改当前音量大小
updateCurrentVolume(volumePercent);
dispatch({ type: 'isSlideVolume', data: true });
}, 1);
};
// 当鼠标抬起时
const whenMouseUpDo = () => {
volumeInterval.current && clearInterval(volumeInterval.current);
dispatch({ type: 'isSlideVolume', data: false });
};
const clearVolumeInterval: React.MouseEventHandler<HTMLDivElement> = (e) => {
e.stopPropagation();
whenMouseUpDo();
};
/**
* @description
*/
const requestFullScreen = () => {
if (screenfull.isEnabled) {
screenfull.toggle(reviceProps.videoContainerRef!);
screenfull.on('change', () =>
dispatch({ type: 'isScreentFull', data: (screenfull as Screenfull).isFullscreen }),
);
}
} 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;
};
};
/**
* @description
*/
const pictureInPicture = () => {
if (isPictureinpicture) {
(document as any).exitPictureInPicture();
} else {
(reviceProps.videoRef! as any).requestPictureInPicture();
}
};
/**
* @description
*/
const selectPlayRate = (playbackRate: number) => {
reviceProps.videoRef!.playbackRate = playbackRate;
dispatch({ type: 'multiple', data: playbackRate });
};
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 = 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 (
<div
className="JoL-controls-container"
style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
>
<MonitorComponent
isPlay={isPlay}
handleChangePlayState={handleChangePlayState}
currentTime={secondsToMinutesAndSecondes(currentTime)}
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}
/>
}
return (
<div
className="JoL-controls-container"
// style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
>
<MonitorComponent
isPlay={isPlay}
handleChangePlayState={handleChangePlayState}
currentTime={secondsToMinutesAndSecondes(currentTime)}
totalTime={secondsToMinutesAndSecondes(duration)}
/>
<Tooltip
styleCss={{ padding: '0 5px' }}
title={isPictureinpicture ? '关闭画中画' : '开启画中画'}
icon={
<Broadcast
iconClass="fullScreen"
fill="#fff"
className="hover-icon-animate"
fontSize={'20px'}
onClick={pictureInPicture}
<div className="JoL-multifunction">
{propsAttributes!.isShowMultiple && (
<MultipleComponent
multipleText={multipleText}
multiple={controlsState.multiple}
selectPlayRate={selectPlayRate}
/>
}
/>
<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"
/>
}
/>
)}
<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
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>
);
});
);
},
);
export default Index;

@ -175,5 +175,12 @@ const JoLPlayer = function JoLPlayer(props: videoparameter, ref: React.Ref<unkno
};
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;

@ -172,7 +172,7 @@ const Index = memo(function Index(props) {
return (
<div
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-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
*/
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> {
/**

@ -37,3 +37,14 @@ export const createALabel = (path: string, fileName: string = 'JoL-player.png')
link.remove();
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