add video clarity, fix the black border bug of the video

master
lgf 3 years ago
parent 4d81bc138e
commit 6a8566374d
  1. 35
      example/src/app.tsx
  2. 2
      package.json
  3. 15
      src/core/config/index.ts
  4. 16
      src/core/context/index.ts
  5. 22
      src/core/controller/index.tsx
  6. 11
      src/core/controls/index.scss
  7. 46
      src/core/controls/index.tsx
  8. 1
      src/core/controls/monitor.tsx
  9. 3
      src/core/controls/multiple.tsx
  10. 102
      src/core/controls/quality.tsx
  11. 3
      src/core/index.scss
  12. 10
      src/core/index.tsx
  13. 28
      src/core/useVideo.ts
  14. 9
      src/core/useVideoCallback.ts
  15. 30
      src/index.d.ts
  16. 30
      src/interface/index.ts

@ -1,4 +1,4 @@
import { callBackType, JoLPlayerRef } from '@/interface';
import { callBackType, JoLPlayerRef, qualityKey } from '@/interface';
import React, { useRef, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import JoLPlayer from '../../src/index';
@ -22,7 +22,7 @@ const AppCompent = () => {
console.log(`onPlay`, val);
};
const onTimeChange: callBackType = (val) => {
console.log(`onTimeChange`, val);
// console.log(`onTimeChange`, val);
};
const onvolumechange: callBackType = (val) => {
console.log(`onvolumechange`, val);
@ -30,9 +30,10 @@ const AppCompent = () => {
const onError = () => {
console.log(`onError`);
};
useEffect(() => {
console.log(`videoRef.current`, videoRef.current);
}, [videoRef.current]);
const onQualityChange: callBackType<qualityKey> = (val) => {
console.log(`onQualityChange`, val);
};
useEffect(() => {}, [videoRef.current]);
const videoMethod = (status: string) => {
if (status === 'play') {
@ -64,14 +65,33 @@ const AppCompent = () => {
onTimeChange={onTimeChange}
onvolumechange={onvolumechange}
onError={onError}
onQualityChange={onQualityChange}
option={{
videoSrc: 'https://qiniu.qyhever.com/162962488432086ba29652658echrome.mp4',
videoSrc:
'https://gs-files.oss-cn-hongkong.aliyuncs.com/okr/prod/file/2021/08/31/540p.mp4',
width: 750,
height: 420,
theme,
poster: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG',
poster:
'https://gs-files.oss-cn-hongkong.aliyuncs.com/okr/prod/file/2021/08/31/1080pp.png',
language: 'en',
isShowMultiple,
pausePlacement: 'center',
isShowPauseButton: false,
quality: [
{
name: 'FHD',
url: 'https://gs-files.oss-cn-hongkong.aliyuncs.com/okr/prod/file/2021/08/31/720p.mp4',
},
{
name: 'HD',
url: 'https://gs-files.oss-cn-hongkong.aliyuncs.com/okr/prod/file/2021/08/31/540p.mp4',
},
{
name: 'SD',
url: 'https://gs-files.oss-accelerate.aliyuncs.com/okr/prod/file/2021/08/31/1630377480138360p.mp4',
},
],
}}
/>
<JoLPlayer
@ -80,6 +100,7 @@ const AppCompent = () => {
width: 750,
height: 420,
poster: 'https://cdn.gudsen.com/2021/06/28/f81356b08b4842d7a3719499f557c8e4.JPG',
quality: [],
}}
/>
<button onClick={() => videoMethod('play')}>play</button>

@ -1,6 +1,6 @@
{
"name": "jol-player",
"version": "2.1.2",
"version": "2.2.0",
"description": "简洁,美观,功能强大的react播放器,simple and beautiful, powerful react player",
"main": "index.js",
"scripts": {

@ -1,3 +1,5 @@
import { qualityName, qualityKey } from '@/interface';
export const multipleList = [
{ id: 0.5, name: '0.5x' },
{ id: 0.75, name: '0.75x' },
@ -12,3 +14,16 @@ export const defaultTheme: string = '#ffb821';
export const defaultVolume: number = 60;
export const defaultLanguage = 'zh';
export interface qualityListType {
key: qualityKey;
enName: qualityName;
name: string;
id: number;
}
export const qualityList: qualityListType[] = [
{ name: '标清', key: '360P', enName: 'SD', id: 1 },
{ name: '高清', key: '540P', enName: 'HD', id: 2 },
{ name: '超清', key: '720P', enName: 'FHD', id: 3 },
{ name: '蓝光', key: '1080P', enName: 'BD', id: 4 },
];

@ -1,5 +1,5 @@
import React, { useReducer } from 'react';
import { videoOption } from '@/interface';
import { videoOption, qualityKey } from '@/interface';
export interface VideoStateType<K = boolean, T = number> {
/**
* @description
@ -13,6 +13,10 @@ export interface VideoStateType<K = boolean, T = number> {
* @description onProgressMouseUp事件的变化数据onProgressMouseUp事件触发的
*/
progressMouseUpChangeVal: T;
/**
* @description
*/
quality: qualityKey | undefined;
}
/**
* @description
@ -32,6 +36,7 @@ export const initialState = {
isControl: false,
progressSliderChangeVal: 0,
progressMouseUpChangeVal: 0,
quality: undefined,
};
export const defaultValue = {
@ -54,10 +59,15 @@ export interface progressMouseUpChangeValValActionType {
type: 'progressMouseUpChangeVal';
data: VideoStateType['progressMouseUpChangeVal'];
}
export interface qualityActionType {
type: 'quality';
data: VideoStateType['quality'];
}
export type mergeAction =
| isControlActionType
| progressSliderChangeValActionType
| progressMouseUpChangeValValActionType;
| progressMouseUpChangeValValActionType
| qualityActionType;
export const useVideoFlow = () => {
const reducer = (state: VideoStateType, action: mergeAction) => {
@ -68,6 +78,8 @@ export const useVideoFlow = () => {
return { ...state, progressSliderChangeVal: action.data };
case 'progressMouseUpChangeVal':
return { ...state, progressMouseUpChangeVal: action.data };
case 'quality':
return { ...state, quality: action.data };
default:
return state;
}

@ -6,6 +6,7 @@ import { FlowContext } from '@/core/context';
import { useVideo } from '@/core/useVideo';
import EndComponent from '@/components/end';
import Screenshot from '@/components/screenshot';
import { filterDefaults } from '@/utils';
import './index.scss';
const Index = memo(function Index() {
@ -126,15 +127,18 @@ const Index = memo(function Index() {
onMouseMove={mouseMove}
onClick={handlePlay}
></div>
{!isPlay && !isEndEd && (
<Broadcast
iconClass="player"
fill="#fff"
fontSize={'47px'}
className="iconfont play-icon"
style={pausePosition}
/>
)}
{filterDefaults(propsAttributes!.isShowPauseButton)
? !isPlay &&
!isEndEd && (
<Broadcast
iconClass="player"
fill="#fff"
fontSize={'55px'}
className="iconfont play-icon"
style={pausePosition}
/>
)
: null}
<div
className="JoL-progress-and-controls-wrap"
onMouseMove={(e) => [controlsContainerMove('move')]}

@ -28,6 +28,9 @@
.play-pause-timeline {
display: flex;
align-items: center;
.icon {
cursor: pointer;
}
.time-wrap {
font-size: 13px;
vertical-align: middle;
@ -62,8 +65,14 @@
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.7);
li {
padding: 12px 15px;
padding: 10px 22px;
cursor: pointer;
font-size: 14px;
white-space: nowrap;
display: flex;
p {
padding: 0 3px;
}
&:hover {
background: rgba(255, 255, 255, 0.2);
}

@ -7,11 +7,12 @@ import { secondsToMinutesAndSecondes, capture, filterDefaults } from '@/utils';
import { useControls } from './variable';
import useWindowClient from '@/utils/useWindowClient';
import screenfull, { Screenfull } from 'screenfull';
import { multipleList, defaultVolume, defaultLanguage } from '@/core/config';
import { multipleList, defaultLanguage } from '@/core/config';
import SetComponent from './set';
import MultipleComponent from './multiple';
import VolumeComponent from './volume';
import MonitorComponent from './monitor';
import QualityComponent, { qualityToggleType } from './quality';
import { il8n } from '@/language';
import './index.scss';
@ -28,17 +29,24 @@ const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> =
const reviceProps = useContext(FlowContext);
const { propsAttributes } = reviceProps!;
const { propsAttributes, dispatch: contentDispatch } = reviceProps!;
const revicePropsData = useRef<contextType>(null!);
const { isPlay, handleChangePlayState, currentTime, duration, isPictureinpicture, volume } =
useVideo(
{
videoElement: reviceProps.videoRef,
},
[reviceProps.videoRef],
);
const {
isPlay,
handleChangePlayState,
currentTime,
duration,
isPictureinpicture,
volume,
videoMethod,
} = useVideo(
{
videoElement: reviceProps.videoRef,
},
[reviceProps.videoRef],
);
const { controlsState, dispatch } = useControls();
@ -229,6 +237,15 @@ const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> =
return controlsState.isScreentFull || controlsState.isWebPageFullScreen ? '13px' : '8px';
}, [controlsState.isWebPageFullScreen, controlsState.isScreentFull]);
/**
* @description
*/
const qualityToggle: qualityToggleType = (url, key) => {
contentDispatch!({ type: 'quality', data: key });
videoMethod.setVideoSrc(url);
videoMethod.seek(currentTime);
videoMethod.play();
};
return (
<div
className="JoL-controls-container"
@ -241,6 +258,16 @@ const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> =
totalTime={secondsToMinutesAndSecondes(duration)}
/>
<div className="JoL-multifunction">
{propsAttributes!.quality && propsAttributes!.quality.length ? (
<QualityComponent
videoSrc={
revicePropsData.current && revicePropsData.current.videoRef
? revicePropsData.current.videoRef!.src
: undefined
}
qualityToggle={qualityToggle}
/>
) : null}
{filterDefaults(propsAttributes!.isShowMultiple) && (
<MultipleComponent
multipleText={multipleText}
@ -258,7 +285,6 @@ const Index: FC<{ setIsscreenshot: Function; setScreenshotLoading: Function }> =
slideCurrentVolume={slideCurrentVolume}
clearVolumeInterval={clearVolumeInterval}
isMuted={controlsState.isMuted}
// toggleVolume={toggleVolume}
toggleVolume={() => dispatch({ type: 'isMuted', data: !controlsState.isMuted })}
/>
{filterDefaults(propsAttributes!.isShowSet) && (

@ -22,6 +22,7 @@ const Monitor: FC<MonitorType> = memo(function Monitor({
fill="#fff"
fontSize="20px"
onClick={handleChangePlayState}
className="icon"
/>
<span className="time-wrap">
<span className="current-time">{currentTime}</span>

@ -1,6 +1,5 @@
import React, { memo, FC, useState, useContext } from 'react';
import { multipleList } from '@/core/config';
import { defaultTheme } from '@/core/config';
import { multipleList, defaultTheme } from '@/core/config';
import { FlowContext } from '@/core/context';
import './index.scss';
export interface MultipleType {

@ -0,0 +1,102 @@
import React, { memo, FC, useState, useContext, useMemo } from 'react';
import { defaultTheme, qualityList, qualityListType } from '@/core/config';
import { FlowContext } from '@/core/context';
import { qualityKey } from '@/interface';
import './index.scss';
export type qualityToggleType = (par: string, key: qualityKey) => void;
export interface QualityType {
style?: React.CSSProperties;
videoSrc?: string;
qualityToggle: qualityToggleType;
}
export type qualityMerge = qualityListType & { url: string };
const Quality: FC<QualityType> = memo(function Quality({ style, videoSrc, qualityToggle }) {
const reviceProps = useContext(FlowContext);
const { theme, quality, language } = reviceProps.propsAttributes!;
const [isShow, setIsShow] = useState<boolean>(false);
const qualitySelectList = useMemo(() => {
if (quality) {
return quality
.map(
(item) =>
qualityList
.map((items) => {
return item.name === items.enName && Object.assign({}, items, { url: item.url });
})
.filter((item) => item)[0],
)
.sort((a, b) => (b && b.id ? b.id : 1) - (a && a.id ? a.id : 1));
}
return [];
}, [quality]);
const qualityText = useMemo<Partial<qualityMerge>>(() => {
let text: qualityMerge[] = [];
if (videoSrc && qualitySelectList) {
text = (qualitySelectList as qualityMerge[]).filter((item) => item.url === videoSrc);
}
return text.length ? text[0] : { key: '360P', enName: 'SD', name: '标清', id: 1 };
}, [videoSrc, qualitySelectList]);
const toggle = (url: string, key: qualityKey) => {
qualityToggle(url, key);
};
return (
<div
className="JoL-multifunction-multiple"
onClick={(e) => setIsShow((pre) => !pre)}
onMouseLeave={(e) => [setIsShow(false)]}
style={style}
>
<p>
{qualityText
? language && language === 'en'
? qualityText.enName
: qualityText.name
: null}
</p>
<div
className="JoL-multifunction-multiple-container"
style={{ display: isShow ? 'block' : 'none' }}
>
{qualitySelectList && qualitySelectList.length ? (
<ul className="JoL-multifunction-multiple-layer">
{qualitySelectList.map((item, index) => (
<li
key={index}
onClick={() =>
toggle(item && item.url ? item.url : '', item && item.key ? item.key : '360P')
}
style={{
color:
qualityText.key === (item && item.key ? item.key : null)
? theme
? theme
: defaultTheme
: '#fff',
}}
>
<p>
{item && item.name
? language && language === 'en'
? item.enName
: item.name
: null}
</p>
<p>{item && item.key ? item.key : null}</p>
</li>
))}
</ul>
) : null}
</div>
</div>
);
});
export default Quality;

@ -8,9 +8,12 @@
box-sizing: border-box;
position: relative;
z-index: 100;
// background-color: #000;
.JoL-player {
@include wh(100%, 100%);
background-color: #000;
// 去除视频黑边
object-fit: cover;
@include position(absolute, 0, auto, auto, 0);
z-index: 1;
}

@ -30,6 +30,7 @@ const JoLPlayer = function JoLPlayer(props: videoparameter, ref: React.Ref<unkno
onProgressMouseUp,
onError,
onvolumechange,
onQualityChange,
} = props;
const { videoSrc, width, height, theme, poster, setBufferContent } = option;
/**
@ -85,6 +86,7 @@ const JoLPlayer = function JoLPlayer(props: videoparameter, ref: React.Ref<unkno
onEndEd,
onError,
onvolumechange,
onQualityChange,
});
useEffect(() => {
@ -152,7 +154,13 @@ const JoLPlayer = function JoLPlayer(props: videoparameter, ref: React.Ref<unkno
return (
<figure className={`JoL-player-container ${className}`} ref={videoContainerRef} style={style}>
<div className="JoL-light-off-mask" ref={lightOffMaskRef}></div>
<video className="JoL-player" ref={videoRef} poster={poster ? poster : undefined}>
<video
className="JoL-player"
ref={videoRef}
src={videoSrc}
poster={poster ? poster : undefined}
id="JoL-player"
>
{returnVideoSource}
</video>
{!isVideoUseful && <p className="video-no-useful-tip"> ( ́︿ ̀)</p>}

@ -1,7 +1,7 @@
import { useRef, useMemo, useEffect, DependencyList } from "react";
import useMandatoryUpdate from "@/utils/useMandatoryUpdate";
import { videoAttributes, videoMethod, parVoid } from "@/interface";
import { defaultVolume } from "@/core/config";
import { useRef, useMemo, useEffect, DependencyList } from 'react';
import useMandatoryUpdate from '@/utils/useMandatoryUpdate';
import { videoAttributes, videoMethod, parVoid } from '@/interface';
import { defaultVolume } from '@/core/config';
export interface useVideoType extends videoAttributes {
handleChangePlayState: () => void;
videoAttributes: videoAttributes;
@ -37,13 +37,13 @@ export const useVideo = (props: any, dep: DependencyList = []) => {
/**
* @description
*/
videoRef.current.addEventListener("canplay", () => {
videoRef.current.addEventListener('canplay', () => {
torture({ duration: videoRef.current.duration });
});
/**
* @description
*/
videoRef.current.addEventListener("progress", () => {
videoRef.current.addEventListener('progress', () => {
if (videoRef.current.buffered.length >= 1) {
torture({ bufferedTime: videoRef.current.buffered.end(0) });
}
@ -51,13 +51,13 @@ export const useVideo = (props: any, dep: DependencyList = []) => {
/**
* @description
*/
videoRef.current.addEventListener("enterpictureinpicture", () => {
videoRef.current.addEventListener('enterpictureinpicture', () => {
torture({ isPictureinpicture: true });
});
/**
* @description 退
*/
videoRef.current.addEventListener("leavepictureinpicture", () => {
videoRef.current.addEventListener('leavepictureinpicture', () => {
torture({ isPictureinpicture: false });
});
// 定时器用于更新当前视频时间
@ -74,11 +74,11 @@ export const useVideo = (props: any, dep: DependencyList = []) => {
isEndEd: videoRef.current.ended ? true : false,
});
}, 1);
videoRef.current.addEventListener("pause", pauseChange);
videoRef.current.addEventListener("play", playChange);
videoRef.current.addEventListener("timeupdate", timeupdate);
videoRef.current.addEventListener("ended", endedChange);
videoRef.current.addEventListener("error", errorChange);
videoRef.current.addEventListener('pause', pauseChange);
videoRef.current.addEventListener('play', playChange);
videoRef.current.addEventListener('timeupdate', timeupdate);
videoRef.current.addEventListener('ended', endedChange);
videoRef.current.addEventListener('error', errorChange);
}
return () => {
interval.current && clearInterval(interval.current);
@ -135,6 +135,6 @@ export const useVideo = (props: any, dep: DependencyList = []) => {
videoAttributes: videoParameter.current,
videoMethod,
}),
[videoParameter.current]
[videoParameter.current],
);
};

@ -8,7 +8,7 @@ const useVideoCallback = (
handle: Partial<videoCallback>,
) => {
const { isPlay, currentTime, isEndEd, error, volume } = videoPr;
const { progressSliderChangeVal, progressMouseUpChangeVal } = videoFlow;
const { progressSliderChangeVal, progressMouseUpChangeVal, quality } = videoFlow;
const {
onProgressMouseDown,
onPlay,
@ -18,6 +18,7 @@ const useVideoCallback = (
onProgressMouseUp,
onError,
onvolumechange,
onQualityChange,
} = handle;
useEffect(() => {
@ -63,6 +64,12 @@ const useVideoCallback = (
onvolumechange && onvolumechange(videoPr);
}
}, [volume]);
useEffect(() => {
if (quality) {
onQualityChange && onQualityChange(quality);
}
}, [quality]);
};
export default useVideoCallback;

30
src/index.d.ts vendored

@ -9,6 +9,20 @@ export type pausePlacement = 'bottomRight' | 'center';
export type languageType = 'zh' | 'en';
/**
* 360P SD
* 540P HD
* 720P FHD
* 1080P BD
*/
export type qualityName = 'SD' | 'HD' | 'FHD' | 'BD';
export type qualityKey = '360P' | '540P' | '720P' | '1080P';
export interface qualityAttributes<T = qualityName> {
name: T;
url: string;
}
export interface videoOption<T = string, K = boolean, U = number> {
/**
* @description width
@ -70,6 +84,14 @@ export interface videoOption<T = string, K = boolean, U = number> {
* @description ,
*/
language?: languageType;
/**
* @description
*/
isShowPauseButton?: K;
/**
* @description
*/
quality?: qualityAttributes[];
}
export interface videoAttributes<T = number, K = boolean> {
/**
@ -139,8 +161,8 @@ export interface videoMethod<T = noParVoid> {
*/
setVideoSrc: parVoid<string>;
}
export type callBackType = (e: videoAttributes) => void;
export interface videoCallback<T = callBackType> {
export type callBackType<T = videoAttributes> = (e: T) => void;
export interface videoCallback<T = callBackType, U = callBackType<qualityKey>> {
/**
* @description
*/
@ -173,6 +195,10 @@ export interface videoCallback<T = callBackType> {
* @description
*/
onvolumechange: T;
/**
* @description
*/
onQualityChange: U;
}
export interface videoparameter extends Partial<videoCallback> {
style?: React.CSSProperties;

@ -9,6 +9,20 @@ export interface hoverShowStyleType {
export type pausePlacement = 'bottomRight' | 'center';
export type languageType = 'zh' | 'en';
/**
* 360P SD
* 540P HD
* 720P FHD
* 1080P BD
*/
export type qualityName = 'SD' | 'HD' | 'FHD' | 'BD';
export type qualityKey = '360P' | '540P' | '720P' | '1080P';
export interface qualityAttributes<T = qualityName> {
name: T;
url: string;
}
export interface videoOption<T = string, K = boolean, U = number> {
/**
* @description width
@ -70,6 +84,14 @@ export interface videoOption<T = string, K = boolean, U = number> {
* @description ,
*/
language?: languageType;
/**
* @description
*/
isShowPauseButton?: K;
/**
* @description
*/
quality?: qualityAttributes[];
}
export interface videoAttributes<T = number, K = boolean> {
/**
@ -139,8 +161,8 @@ export interface videoMethod<T = noParVoid> {
*/
setVideoSrc: parVoid<string>;
}
export type callBackType = (e: videoAttributes) => void;
export interface videoCallback<T = callBackType> {
export type callBackType<T = videoAttributes> = (e: T) => void;
export interface videoCallback<T = callBackType, U = callBackType<qualityKey>> {
/**
* @description
*/
@ -173,6 +195,10 @@ export interface videoCallback<T = callBackType> {
* @description
*/
onvolumechange: T;
/**
* @description
*/
onQualityChange: U;
}
export interface videoparameter extends Partial<videoCallback> {
style?: React.CSSProperties;

Loading…
Cancel
Save