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. 114
      src/core/controller/index.tsx
  5. 47
      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
*/
const showControl = (e: any, status: string) => {
dispatch!({ type: 'isControl', data: status === 'enter' && !isEndEd ? true : false });
};
/**
* @description
* @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);
if (userActivity.current) {
/**
* @description 1200ms没有任何操作的话
* @description
*/
if (viewClientX.current! !== prevCalculation.current) {
userActivity.current = false;
dispatch!({ type: 'isControl', data: true });
controllerRef.current.style.cursor = 'pointer';
} else {
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';
}
}, 1200);
};
const clearTimer = () => {
controllerRef.current.style.cursor = 'pointer';
if (timer.current) {
clearInterval(timer.current);
},
propsAttributes!.hideMouseTime ? propsAttributes!.hideMouseTime : 2000,
);
}
}, 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,7 +14,8 @@ import VolumeComponent from './volume';
import MonitorComponent from './monitor';
import './index.scss';
const Index = memo(function Index(props) {
const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> = memo(
function Index({ setIsscreenshot, setScreenshotLoading }) {
/**
* @description
*/
@ -26,7 +27,9 @@ const Index = memo(function Index(props) {
const reviceProps = useContext(FlowContext);
const revicePropsData = useRef<any>();
const { propsAttributes } = reviceProps!;
const revicePropsData = useRef<contextType>();
const { isPlay, handleChangePlayState, currentTime, duration, isPictureinpicture, volume } =
useVideo(
@ -167,28 +170,30 @@ const Index = memo(function Index(props) {
/**
* @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) {}
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;
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;
const loop = videoRef!.loop;
videoRef!.loop = loop ? false : true;
}
};
/**
@ -216,7 +221,7 @@ const Index = memo(function Index(props) {
return (
<div
className="JoL-controls-container"
style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
// style={{ opacity: reviceProps.videoFlow!.isControl ? '1' : '0' }}
>
<MonitorComponent
isPlay={isPlay}
@ -225,11 +230,14 @@ const Index = memo(function Index(props) {
totalTime={secondsToMinutesAndSecondes(duration)}
/>
<div className="JoL-multifunction">
{propsAttributes!.isShowMultiple && (
<MultipleComponent
multipleText={multipleText}
multiple={controlsState.multiple}
selectPlayRate={selectPlayRate}
/>
)}
<VolumeComponent
ref={volumeSliderMirror}
volume={controlsState.volume}
@ -294,6 +302,7 @@ const Index = memo(function Index(props) {
</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