DocSpace-client/packages/asc-web-common/components/MediaViewer/sub-components/video-viewer.js

508 lines
12 KiB
JavaScript
Raw Normal View History

import React, { Component } from "react";
2020-04-30 07:35:02 +00:00
import PropTypes from "prop-types";
import styled from "styled-components";
import { findDOMNode } from "react-dom";
import screenfull from "screenfull";
import ReactPlayer from "react-player";
import Duration from "./duration";
import Progress from "./progress";
2021-02-25 15:41:06 +00:00
import MediaPauseIcon from "../../../../../public/images/media.pause.react.svg";
import MediaPlayIcon from "../../../../../public/images/media.play.react.svg";
import MediaFullScreenIcon from "../../../../../public/images/media.fullscreen.react.svg";
import MediaMuteIcon from "../../../../../public/images/media.mute.react.svg";
import MediaMuteOffIcon from "../../../../../public/images/media.muteoff.react.svg";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
2020-04-19 20:09:12 +00:00
2020-05-05 13:15:33 +00:00
const controlsHeight = 40;
2020-04-19 20:09:12 +00:00
const StyledControls = styled.div`
height: ${(props) => props.height}px;
display: block;
position: fixed;
2021-05-06 09:52:19 +00:00
z-index: 301;
${(props) => !props.isVideo && "background-color: rgba(11,11,11,0.7);"}
top: calc(50% + ${(props) => props.top}px);
left: ${(props) => props.left}px;
2020-04-19 20:09:12 +00:00
`;
const StyledVideoControlBtn = styled.div`
display: inline-block;
height: 30px;
line-height: 30px;
margin: 5px;
width: 40px;
border-radius: 2px;
cursor: pointer;
text-align: center;
vertical-align: top;
&:hover {
background-color: rgba(200, 200, 200, 0.2);
}
2020-05-05 13:15:33 +00:00
.playBtnContainer {
width: 23px;
line-height: 0;
margin: 3px auto;
}
.pauseBtnContainer {
display: block;
width: 19px;
margin: 3px 10px;
line-height: 19px;
}
.muteBtnContainer {
display: block;
width: 26px;
margin: 3px 7px;
line-height: 19px;
}
.fullscreenBtnContainer {
display: block;
width: 20px;
margin: 3px 10px;
line-height: 19px;
}
2020-04-19 20:09:12 +00:00
`;
const StyledMediaPauseIcon = styled(MediaPauseIcon)`
${commonIconsStyles}
`;
const StyledMediaPlayIcon = styled(MediaPlayIcon)`
${commonIconsStyles}
`;
const StyledMediaFullScreenIcon = styled(MediaFullScreenIcon)`
${commonIconsStyles}
`;
const StyledMediaMuteIcon = styled(MediaMuteIcon)`
${commonIconsStyles}
`;
const StyledMediaMuteOffIcon = styled(MediaMuteOffIcon)`
${commonIconsStyles}
`;
const VideoControlBtn = (props) => {
return (
<StyledVideoControlBtn {...props}>{props.children}</StyledVideoControlBtn>
);
};
VideoControlBtn.propTypes = {
children: PropTypes.any,
};
const Controls = (props) => {
return <StyledControls {...props}>{props.children}</StyledControls>;
};
Controls.propTypes = {
children: PropTypes.any,
};
const PlayBtn = (props) => {
return (
<VideoControlBtn onClick={props.onClick}>
{props.playing ? (
2020-05-05 13:15:33 +00:00
<div className="pauseBtnContainer">
<StyledMediaPauseIcon size="scale" />
2020-05-05 13:15:33 +00:00
</div>
) : (
2020-05-05 13:15:33 +00:00
<div className="playBtnContainer">
<StyledMediaPlayIcon size="scale" />
2020-05-05 13:15:33 +00:00
</div>
)}
</VideoControlBtn>
);
};
PlayBtn.propTypes = {
playing: PropTypes.bool,
onClick: PropTypes.func,
};
const FullScreenBtn = (props) => {
return (
<VideoControlBtn onClick={props.onClick}>
2020-05-17 17:00:45 +00:00
<div className="fullscreenBtnContainer">
<StyledMediaFullScreenIcon size="scale" />
2020-05-05 13:15:33 +00:00
</div>
</VideoControlBtn>
);
};
FullScreenBtn.propTypes = {
onClick: PropTypes.func,
};
2020-04-19 20:09:12 +00:00
const StyledValumeContainer = styled.div`
display: inline-block;
vertical-align: top;
line-height: 39px;
position: relative;
.muteConteiner {
display: none;
position: absolute;
width: 40px;
height: 80px;
border-radius: 5px;
top: -76px;
left: 5px;
background: black;
}
&:hover {
.muteConteiner {
2020-04-19 20:09:12 +00:00
display: inline-block;
}
}
.mute {
display: inline-block;
transform: rotate(-90deg);
margin: 22px -14px;
}
2020-04-19 20:09:12 +00:00
`;
2020-04-21 10:49:23 +00:00
const StyledDuration = styled.div`
display: inline-block;
height: 30px;
line-height: 30px;
margin: 5px;
width: 60px;
text-align: center;
vertical-align: top;
border-radius: 2px;
cursor: pointer;
&:hover {
background-color: rgba(200, 200, 200, 0.2);
}
2020-04-21 10:49:23 +00:00
`;
2020-04-19 20:09:12 +00:00
const StyledVideoViewer = styled.div`
color: #d1d1d1;
.playerWrapper {
display: ${(props) => (props.isVideo ? "block" : "none")};
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
left: ${(props) => props.left}px;
top: calc(50% - ${(props) => props.top / 2}px);
2021-05-06 09:52:19 +00:00
z-index: 301;
position: fixed;
padding-bottom: 40px;
background-color: rgba(11, 11, 11, 0.7);
2020-04-21 10:49:23 +00:00
video {
2021-05-06 09:52:19 +00:00
z-index: 300;
}
}
2020-04-19 20:09:12 +00:00
`;
class ValumeBtn extends Component {
2020-04-19 20:09:12 +00:00
constructor(props) {
super(props);
}
render() {
return (
<StyledValumeContainer>
<div className="muteConteiner">
<Progress
className="mute"
width={this.props.width}
value={this.props.volume}
onMouseDown={this.props.onMouseDown}
2020-05-09 18:11:54 +00:00
handleSeekChange={this.props.onChange}
onMouseUp={this.props.handleSeekMouseUp}
/>
</div>
<VideoControlBtn onClick={this.props.onChangeMute}>
2020-05-17 17:00:45 +00:00
<div className="muteBtnContainer">
{this.props.muted ? (
<StyledMediaMuteOffIcon size="scale" />
) : (
<StyledMediaMuteIcon size="scale" />
)}
2020-05-05 13:15:33 +00:00
</div>
</VideoControlBtn>
</StyledValumeContainer>
);
2020-04-19 20:09:12 +00:00
}
}
ValumeBtn.propTypes = {
width: PropTypes.number,
volume: PropTypes.number,
muted: PropTypes.bool,
onMouseDown: PropTypes.func,
onChange: PropTypes.func,
handleSeekMouseUp: PropTypes.func,
onChangeMute: PropTypes.func,
};
2020-04-19 20:09:12 +00:00
class VideoViewer extends Component {
state = {
url: this.props.url,
2020-04-19 20:09:12 +00:00
pip: false,
playing: this.props.playing,
2020-04-19 20:09:12 +00:00
controls: false,
light: false,
2020-04-25 18:03:13 +00:00
volume: 0.3,
2020-04-19 20:09:12 +00:00
muted: false,
played: 0,
loaded: 0,
duration: 0,
playbackRate: 1.0,
loop: false,
};
2020-04-19 20:09:12 +00:00
2021-08-04 09:50:44 +00:00
componentDidMount() {
document.addEventListener("keydown", this.onKeydown, false);
}
componentWillUnmount() {
document.removeEventListener("keydown", this.onKeydown, false);
}
componentDidUpdate(prevProps, prevState) {
let newUrl = prevState.url;
let newPlaying = prevState.playing;
if (
this.props.url !== prevProps.url ||
this.props.playing !== prevProps.playing
) {
if (this.props.url !== prevProps.url) {
newUrl = this.props.url;
}
if (this.props.playing !== prevProps.playing) {
newPlaying = this.props.playing;
}
this.setState({
url: newUrl,
playing: newPlaying,
});
}
}
2020-05-05 13:15:33 +00:00
2021-08-04 09:50:44 +00:00
onKeydown = (e) => {
if (e.keyCode === 32) this.handlePlayPause();
2021-08-05 09:38:38 +00:00
if (e.keyCode === 37 || e.keyCode === 39) this.setState({ playing: false });
2021-08-04 09:50:44 +00:00
};
2020-04-19 20:09:12 +00:00
handlePlayPause = () => {
this.setState({ playing: !this.state.playing });
};
2020-04-19 20:09:12 +00:00
handleStop = () => {
this.setState({ url: null, playing: false });
};
2020-04-19 20:09:12 +00:00
handleVolumeChange = (e) => {
this.setState({
2020-04-19 20:09:12 +00:00
volume: parseFloat(e.target.value),
muted: false,
});
};
2020-04-19 20:09:12 +00:00
handleToggleMuted = () => {
this.setState({ muted: !this.state.muted });
};
2020-04-19 20:09:12 +00:00
handlePlay = () => {
this.setState({ playing: true });
};
2020-04-19 20:09:12 +00:00
handleEnablePIP = () => {
this.setState({ pip: true });
};
2020-04-19 20:09:12 +00:00
handleDisablePIP = () => {
this.setState({ pip: false });
};
2020-04-19 20:09:12 +00:00
handlePause = () => {
this.setState({ playing: false });
};
2020-04-19 20:09:12 +00:00
handleSeekMouseDown = (e) => {
this.setState({ seeking: true });
};
2020-04-19 20:09:12 +00:00
handleSeekChange = (e) => {
this.setState({ played: parseFloat(e.target.value) });
};
2020-04-19 20:09:12 +00:00
handleSeekMouseUp = (e) => {
console.log(!isNaN(parseFloat(e.target.value)), parseFloat(e.target.value));
2020-05-17 17:00:45 +00:00
if (!isNaN(parseFloat(e.target.value))) {
this.setState({ seeking: false });
this.player.seekTo(parseFloat(e.target.value));
}
};
2020-04-19 20:09:12 +00:00
handleProgress = (state) => {
2020-04-19 20:09:12 +00:00
if (!this.state.seeking) {
this.setState(state);
2020-04-19 20:09:12 +00:00
}
};
2020-04-19 20:09:12 +00:00
handleEnded = () => {
this.setState({ playing: this.state.loop });
};
2020-04-19 20:09:12 +00:00
handleDuration = (duration) => {
this.setState({ duration });
};
2020-04-19 20:09:12 +00:00
handleClickFullscreen = () => {
screenfull.request(findDOMNode(this.player));
};
2020-04-19 20:09:12 +00:00
ref = (player) => {
this.player = player;
};
2020-04-19 20:09:12 +00:00
resizePlayer = (videoSize, screenSize) => {
var ratio = videoSize.h / videoSize.w;
if (videoSize.h > screenSize.h) {
videoSize.h = screenSize.h;
videoSize.w = videoSize.h / ratio;
}
if (videoSize.w > screenSize.w) {
videoSize.w = screenSize.w;
videoSize.h = videoSize.w * ratio;
}
return {
width: videoSize.w,
height: videoSize.h,
};
};
onError = (e) => {
console.log("onError", e);
};
render() {
const {
url,
playing,
controls,
light,
volume,
muted,
loop,
played,
loaded,
duration,
playbackRate,
pip,
} = this.state;
2020-05-05 13:15:33 +00:00
const parentOffset = this.props.getOffset() || 0;
var screenSize = {
w: window.innerWidth,
h: window.innerHeight,
};
2020-05-05 13:15:33 +00:00
screenSize.h -= parentOffset + controlsHeight;
let width = screenSize.w;
let height = screenSize.h;
let centerAreaOx = screenSize.w / 2 + document.documentElement.scrollLeft;
let centerAreaOy = screenSize.h / 2 + document.documentElement.scrollTop;
let videoElement = document.getElementsByTagName("video")[0];
if (videoElement) {
width = this.props.isVideo
? videoElement.videoWidth || 480
: screenSize.w - 150;
height = this.props.isVideo ? videoElement.videoHeight || 270 : 0;
2020-05-05 13:15:33 +00:00
let resize = this.resizePlayer(
{
w: width,
h: height,
},
screenSize
);
width = resize.width;
height = resize.height;
}
let left = this.props.isVideo
? centerAreaOx - width / 2
: centerAreaOx - width / 2;
2020-05-17 17:00:45 +00:00
const videoControlBtnWidth = 220;
const audioControlBtnWidth = 170;
let progressWidth = this.props.isVideo
? width - videoControlBtnWidth
: width - audioControlBtnWidth;
2020-05-17 17:00:45 +00:00
2020-04-19 20:09:12 +00:00
return (
<StyledVideoViewer
isVideo={this.props.isVideo}
width={width}
height={height}
left={left}
2020-05-17 17:00:45 +00:00
top={height + controlsHeight}
2021-08-04 09:35:24 +00:00
onClick={this.handlePlayPause}
>
<div>
<div className="playerWrapper">
<ReactPlayer
ref={this.ref}
className="react-player"
width="100%"
height="100%"
url={url}
pip={pip}
playing={playing}
controls={controls}
light={light}
loop={loop}
playbackRate={playbackRate}
volume={volume}
muted={muted}
onPlay={this.handlePlay}
onEnablePIP={this.handleEnablePIP}
onDisablePIP={this.handleDisablePIP}
onPause={this.handlePause}
onEnded={this.handleEnded}
onError={this.onError}
onProgress={this.handleProgress}
onDuration={this.handleDuration}
/>
2020-04-19 20:09:12 +00:00
</div>
<Controls
2020-05-05 13:15:33 +00:00
height={controlsHeight}
left={left}
2020-05-17 17:00:45 +00:00
top={height / 2 - controlsHeight / 2}
isVideo={this.props.isVideo}
>
<PlayBtn onClick={this.handlePlayPause} playing={playing} />
<Progress
value={played}
width={progressWidth}
onMouseDown={this.handleSeekMouseDown}
2020-05-05 13:15:33 +00:00
handleSeekChange={this.handleSeekChange}
onMouseUp={this.handleSeekMouseUp}
onTouchEnd={this.handleSeekMouseUp}
/>
<StyledDuration>
-<Duration seconds={duration * (1 - played)} />
</StyledDuration>
<ValumeBtn
width={64}
muted={muted}
volume={muted ? 0 : volume}
onChangeMute={this.handleToggleMuted}
onChange={this.handleVolumeChange}
/>
{this.props.isVideo && (
<FullScreenBtn onClick={this.handleClickFullscreen} />
)}
</Controls>
</div>
</StyledVideoViewer>
);
2020-04-19 20:09:12 +00:00
}
}
2020-04-30 07:35:02 +00:00
VideoViewer.propTypes = {
isVideo: PropTypes.bool,
url: PropTypes.string,
2020-05-05 13:15:33 +00:00
playing: PropTypes.bool,
getOffset: PropTypes.func,
};
2020-04-19 20:09:12 +00:00
export default VideoViewer;