Web: Component: update video player component

This commit is contained in:
Dmitry Sychugov 2022-10-14 19:00:03 +05:00
parent 203196f883
commit b53b4e07b2

View File

@ -9,18 +9,27 @@ import IconMuted from "../../../../public/images/videoplayer.mute.react.svg";
import IconFullScreen from "../../../../public/images/videoplayer.full.react.svg";
import IconExitFullScreen from "../../../../public/images/videoplayer.exit.react.svg";
import IconSpeed from "../../../../public/images/videoplayer.speed.react.svg";
import MediaContextMenu from "../../../../public/images/vertical-dots.react.svg";
import BigIconPlay from "../../../../public/images/videoplayer.bgplay.react.svg";
import Slider from "@docspace/components/slider";
let iconWidth = 80;
let iconHeight = 60;
const StyledVideoPlayer = styled.div`
${(props) =>
props.isFullScreen ? "background: #000" : "background: transparent"};
const ACTION_TYPES = {
setActiveIndex: "setActiveIndex",
update: "update",
};
function createAction(type, payload) {
return {
type,
payload: payload || {},
};
}
const StyledVideoPlayer = styled.div`
.video-wrapper {
position: fixed;
z-index: 1005;
@ -28,6 +37,37 @@ const StyledVideoPlayer = styled.div`
bottom: 0;
right: 0;
left: 0;
${(props) =>
props.isFullScreen ? "background: #000" : "background: transparent"};
}
.dropdown-speed {
position: relative;
display: inline-block;
}
.dropdown-item {
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px 3px 0px 0px;
height: 30px;
width: 40px;
&:hover {
cursor: pointer;
background: #222;
}
}
.dropdown-content {
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
bottom: 52px;
color: #fff;
background: #000;
text-align: center;
border-radius: 3px 3px 0px 0px;
}
.bg-play {
@ -53,6 +93,7 @@ const StyledVideoActions = styled.div`
height: 48px;
&:hover {
cursor: pointer;
background: rgb(77, 77, 77);
}
}
`;
@ -67,25 +108,25 @@ const StyledVideoControls = styled.div`
background: rgba(17, 17, 17, 0.867);
input[type="range"] {
//-webkit-appearance: none !important;
-webkit-appearance: none;
margin-right: 15px;
width: 80%;
height: 7px;
background: #4d4d4d;
border: 1px solid rgba(0, 0, 0, 0.4);
border-radius: 2px;
transition: all 0.26s ease-out;
height: 8px;
width: 70%;
border-radius: 5px;
background-image: linear-gradient(#d1d1d1, #d1d1d1);
background-repeat: no-repeat;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none !important;
cursor: pointer;
// height: 6px;
border: 1px solid rgba(0, 0, 0, 0.4);
-webkit-appearance: none;
height: 14px;
width: 14px;
border-radius: 50%;
background: #fff;
border: 1px solid rgba(0, 0, 0);
// transition: background 0.3s ease-in-out;
}
/* input[type="range"]::-moz-range-progress {
background: white;
} */
`;
const getDuration = (time) => {
@ -110,64 +151,130 @@ const getDuration = (time) => {
};
export default function ViewerPlayer(props) {
const { setOverlay } = props;
const { setIsFullScreen, videoRef } = props;
const initialState = {
width: 0,
height: 0,
left: 0,
top: 0,
activeIndex: props.activeIndex,
isPlaying: false,
isMuted: false,
isFullScreen: false,
speedSelection: false,
progress: 0,
duration: 0,
size: "0%",
};
function reducer(state, action) {
switch (action.type) {
case ACTION_TYPES.setActiveIndex:
return {
...state,
activeIndex: action.payload.index,
startLoading: true,
};
case ACTION_TYPES.update:
return {
...state,
...action.payload,
};
default:
break;
}
return state;
}
const inputRef = React.useRef(null);
const [state, dispatch] = React.useReducer(reducer, initialState);
const footerHeight = 48;
const titleHeight = 48;
const [style, setStyle] = React.useState(null);
const [isPlaying, setIsPlaying] = React.useState(false);
const [progress, setProgress] = React.useState(0);
const togglePlay = () =>
dispatch(
createAction(ACTION_TYPES.update, {
isPlaying: !state.isPlaying,
})
);
const toggleMute = () =>
dispatch(
createAction(ACTION_TYPES.update, {
isMuted: !state.isMuted,
})
);
const toggleScreen = () => {
handleFullScreen(!state.isFullScreen);
setIsFullScreen(!state.isFullScreen);
const [duration, setDuration] = React.useState(0);
const [isMuted, setIsMuted] = React.useState(false);
const [isFullScreen, setIsFullScreen] = React.useState(false);
dispatch(
createAction(ACTION_TYPES.update, {
isFullScreen: !state.isFullScreen,
})
);
};
const [width, setWidth] = React.useState(0);
const [height, setHeight] = React.useState(0);
const [left, setLeft] = React.useState(0);
const [top, setTop] = React.useState(0);
const togglePlay = () => setIsPlaying((playing) => !playing);
const toggleMute = () => setIsMuted((muted) => !muted);
const toggleScreen = () => setIsFullScreen((screen) => !screen);
const toggleSpeedSelectionMenu = () =>
dispatch(
createAction(ACTION_TYPES.update, {
speedSelection: !state.speedSelection,
})
);
const elem = document.documentElement;
function openFullscreen() {
if (elem.requestFullscreen) {
elem.requestFullscreen();
}
}
function closeFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
const handleFullScreen = (isFull) => {
if (elem.requestFullscreen && isFull) return elem.requestFullscreen();
return document.exitFullscreen();
};
const handleVideoProgress = (e) => {
const manualChange = Number(e.target.value);
props.forwardedRef.current.currentTime =
(props.forwardedRef.current.duration / 100) * manualChange;
setProgress(manualChange);
videoRef.current.currentTime =
(videoRef.current.duration / 100) * manualChange;
dispatch(
createAction(ACTION_TYPES.update, {
progress: manualChange,
})
);
};
const getCurrentStyle = (video) => {
const handleVideoSpeed = (speed) => {
const currentSpeeed = Number(speed);
videoRef.current.playbackRate = currentSpeeed;
};
const SpeedButtonComponent = () => {
const speed = ["0.5", "1", "1.5", "2"];
const items = speed.map((speed) => (
<div
className="dropdown-item"
onClick={() => {
dispatch(
createAction(ACTION_TYPES.update, {
speedSelection: false,
})
);
return handleVideoSpeed(speed);
}}
>
{speed}
</div>
));
return items;
};
const getVideoPosition = (video) => {
const [width, height] = getVideoWidthHeight(video);
let left = (window.innerWidth - width) / 2;
let top = (window.innerHeight - height - (footerHeight - 48)) / 2;
let top = !state.isFullScreen
? (window.innerHeight - height - (footerHeight - 48)) / 2
: 0;
let imgStyle = {
width: `${width}px`,
height: `${height}px`,
transition: "all .26s ease-out",
transform: `translateX(${
left !== null ? left + "px" : "auto"
}) translateY(${top}px)`,
};
return imgStyle;
return [width, height, left, top];
};
const getVideoWidthHeight = (video) => {
@ -175,9 +282,14 @@ export default function ViewerPlayer(props) {
let height = 0;
let maxWidth = window.innerWidth;
let maxHeight = window.innerHeight - (footerHeight + titleHeight);
let maxHeight = !state.isFullScreen
? window.innerHeight - (footerHeight + titleHeight)
: window.innerHeight - footerHeight;
width = Math.min(maxWidth, video.videoWidth);
width =
video.videoWidth > maxWidth
? maxWidth
: Math.max(maxWidth, video.videoWidth);
height = (width / video.videoWidth) * video.videoHeight;
if (height > maxHeight) {
@ -185,93 +297,111 @@ export default function ViewerPlayer(props) {
width = (height / video.videoHeight) * video.videoWidth;
}
const videoplayer = document.getElementById("video-playerId");
if (isFullScreen) {
height = videoplayer.offsetHeight;
width = window.innerWidth;
}
return [width, height];
};
const handleOnTimeUpdate = () => {
const progress =
(props.forwardedRef.current.currentTime /
props.forwardedRef.current.duration) *
100;
setProgress(progress);
(videoRef.current.currentTime / videoRef.current.duration) * 100;
const currentTime = getDuration(props.forwardedRef.current.currentTime);
const duration = getDuration(props.forwardedRef.current.duration);
const currentTime = getDuration(videoRef.current.currentTime);
const duration = getDuration(videoRef.current.duration);
const lasting = `${currentTime} / ${duration}`;
setDuration(lasting);
dispatch(
createAction(ACTION_TYPES.update, {
duration: lasting,
progress: progress,
})
);
};
React.useEffect(() => {
// let video = props.forwardedRef.current;
setOverlay((overlay) => !overlay);
if (isFullScreen) {
openFullscreen();
} else {
closeFullscreen();
}
}, [isFullScreen]);
const handleResize = () => {
let video = props.forwardedRef.current;
let video = videoRef.current;
const [width, height, left, top] = getVideoPosition(video);
const imgStyle = getCurrentStyle(video);
setStyle(imgStyle);
dispatch(
createAction(ACTION_TYPES.update, {
width: width,
height: height,
left: left,
top: top,
})
);
};
React.useEffect(() => {
isMuted
? (props.forwardedRef.current.muted = true)
: (props.forwardedRef.current.muted = false);
}, [isMuted, props.forwardedRef.current]);
state.isMuted
? (videoRef.current.muted = true)
: (videoRef.current.muted = false);
}, [state.isMuted, videoRef.current]);
React.useEffect(() => {
inputRef.current.style.backgroundSize =
((state.progress - inputRef.current.min) * 100) /
(inputRef.current.max - inputRef.current.min) +
"% 100%";
}, [inputRef.current, state.progress]);
React.useEffect(() => {
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, [props.forwardedRef.current]);
}, [videoRef.current, state.isFullScreen]);
React.useEffect(() => {
isPlaying
? props.forwardedRef.current.play()
: props.forwardedRef.current.pause();
}, [isPlaying, props.forwardedRef.current]);
state.isPlaying ? videoRef.current.play() : videoRef.current.pause();
}, [state.isPlaying, videoRef.current]);
function loadVideo(video) {
const currentTime = getDuration(props.forwardedRef.current.currentTime);
const duration = getDuration(props.forwardedRef.current.duration);
const currentTime = getDuration(video.currentTime);
const duration = getDuration(video.duration);
const lasting = `${currentTime} / ${duration}`;
setDuration(lasting);
const imgStyle = getCurrentStyle(video);
setStyle(imgStyle);
const [width, height, left, top] = getVideoPosition(video);
dispatch(
createAction(ACTION_TYPES.update, {
width: width,
height: height,
left: left,
top: top,
duration: lasting,
progress: 0,
isPlaying: false,
isMuted: false,
isFullScreen: state.isFullScreen,
speedSelection: false,
})
);
}
React.useEffect(() => {
props.forwardedRef.current.addEventListener("loadedmetadata", function (e) {
loadVideo(props.forwardedRef.current);
videoRef.current.addEventListener("loadedmetadata", function (e) {
loadVideo(videoRef.current);
});
}, []);
}, [props.activeIndex]);
let imgStyle = {
width: `${state.width}px`,
height: `${state.height}px`,
transition: "all .26s ease-out",
transform: `
translateX(${state.left !== null ? state.left + "px" : "auto"}) translateY(${
state.top
}px)`,
};
return (
<StyledVideoPlayer id="video-playerId" isFullScreen={isFullScreen}>
<StyledVideoPlayer id="video-playerId" isFullScreen={state.isFullScreen}>
<div className="video-wrapper">
<video
onClick={togglePlay}
id="videoPlayer"
ref={props.forwardedRef}
ref={videoRef}
src={props.video.src}
style={style}
style={imgStyle}
onTimeUpdate={handleOnTimeUpdate}
></video>
{/* {!isPlaying && (
@ -283,29 +413,44 @@ export default function ViewerPlayer(props) {
<StyledVideoControls>
<StyledVideoActions>
<div className="controller" onClick={togglePlay}>
{!isPlaying ? <IconPlay /> : <IconStop />}
{!state.isPlaying ? <IconPlay /> : <IconStop />}
</div>
<input
ref={inputRef}
type="range"
withPouring={true}
min="0"
max="100"
value={progress}
value={state.progress}
onChange={(e) => handleVideoProgress(e)}
/>
<div
style={{
paddingLeft: "10px",
paddingRight: "14px",
width: "102px",
color: "#DDDDDD",
}}
>
{duration}
{state.duration}
</div>
<div className="controller" onClick={toggleMute}>
{!isMuted ? <IconSound /> : <IconMuted />}
{!state.isMuted ? <IconSound /> : <IconMuted />}
</div>
<div className="controller" onClick={toggleScreen}>
{!isFullScreen ? <IconFullScreen /> : <IconExitFullScreen />}
{!state.isFullScreen ? <IconFullScreen /> : <IconExitFullScreen />}
</div>
<div
className="controller dropdown-speed"
onClick={toggleSpeedSelectionMenu}
>
<IconSpeed />
{state.speedSelection && (
<div className="dropdown-content">{SpeedButtonComponent()}</div>
)}
</div>
<div className="controller">
<MediaContextMenu />
</div>
</StyledVideoActions>
</StyledVideoControls>