2020-04-19 20:09:12 +00:00
|
|
|
import React, { Component } from 'react'
|
2020-04-30 07:35:02 +00:00
|
|
|
import PropTypes from "prop-types";
|
|
|
|
|
2020-04-19 20:09:12 +00:00
|
|
|
import { findDOMNode } from 'react-dom'
|
|
|
|
import screenfull from 'screenfull'
|
|
|
|
|
|
|
|
import ReactPlayer from 'react-player'
|
|
|
|
import Duration from './duration'
|
2020-04-25 18:03:13 +00:00
|
|
|
import Progress from './progress'
|
|
|
|
|
2020-04-19 20:09:12 +00:00
|
|
|
import styled from "styled-components"
|
|
|
|
import { Icons } from "asc-web-components";
|
|
|
|
|
2020-05-05 13:15:33 +00:00
|
|
|
const controlsHeight = 40;
|
2020-04-19 20:09:12 +00:00
|
|
|
const StyledControls = styled.div`
|
2020-05-05 13:15:33 +00:00
|
|
|
height: ${props => props.height}px;
|
2020-04-20 06:32:27 +00:00
|
|
|
display: block;
|
2020-05-08 07:18:50 +00:00
|
|
|
position: fixed;
|
2020-04-22 17:44:09 +00:00
|
|
|
z-index: 4001;
|
2020-04-24 12:28:17 +00:00
|
|
|
${props => !props.isVideo && "background-color: rgba(11,11,11,0.7);"}
|
|
|
|
top: ${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;
|
2020-05-05 13:15:33 +00:00
|
|
|
vertical-align: top;
|
2020-04-19 20:09:12 +00:00
|
|
|
&: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 VideoControlBtn = props => {
|
2020-04-30 08:20:30 +00:00
|
|
|
return (
|
|
|
|
<StyledVideoControlBtn {...props} >
|
|
|
|
{props.children}
|
|
|
|
</StyledVideoControlBtn>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
VideoControlBtn.propTypes = {
|
|
|
|
children: PropTypes.any
|
2020-04-19 20:09:12 +00:00
|
|
|
}
|
|
|
|
const Controls = props => {
|
|
|
|
return (
|
|
|
|
<StyledControls {...props} >
|
2020-04-30 08:20:30 +00:00
|
|
|
{props.children}
|
2020-04-19 20:09:12 +00:00
|
|
|
</StyledControls>
|
|
|
|
);
|
|
|
|
}
|
2020-04-30 08:20:30 +00:00
|
|
|
Controls.propTypes = {
|
|
|
|
children: PropTypes.any
|
2020-04-19 20:09:12 +00:00
|
|
|
}
|
2020-04-30 08:20:30 +00:00
|
|
|
const PlayBtn = props => {
|
2020-05-05 13:15:33 +00:00
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
return (
|
|
|
|
<VideoControlBtn onClick={props.onClick}>
|
2020-05-05 13:15:33 +00:00
|
|
|
{props.playing ?
|
|
|
|
<div className="pauseBtnContainer">
|
|
|
|
<Icons.MediaPauseIcon size="scale" />
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
<div className="playBtnContainer">
|
|
|
|
<Icons.MediaPlayIcon size="scale" />
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
</VideoControlBtn>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
PlayBtn.propTypes = {
|
|
|
|
playing: PropTypes.bool,
|
|
|
|
onClick: PropTypes.func
|
|
|
|
}
|
|
|
|
const FullScreenBtn = props => {
|
|
|
|
return (
|
|
|
|
<VideoControlBtn onClick={props.onClick}>
|
2020-05-05 13:15:33 +00:00
|
|
|
<div className = "fullscreenBtnContainer">
|
|
|
|
<Icons.MediaFullScreenIcon size="scale" />
|
|
|
|
</div>
|
2020-04-30 08:20:30 +00:00
|
|
|
</VideoControlBtn>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
FullScreenBtn.propTypes = {
|
|
|
|
onClick: PropTypes.func
|
2020-04-19 20:09:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const StyledValumeContainer = styled.div`
|
|
|
|
display: inline-block;
|
2020-05-05 13:15:33 +00:00
|
|
|
line-height: 39px;
|
2020-04-19 20:09:12 +00:00
|
|
|
position: relative;
|
|
|
|
|
|
|
|
.muteConteiner{
|
|
|
|
display: none;
|
|
|
|
position: absolute;
|
|
|
|
width: 40px;
|
|
|
|
height: 80px;
|
|
|
|
border-radius: 5px;
|
|
|
|
top: -76px;
|
|
|
|
left: 5px;
|
|
|
|
background: black;
|
|
|
|
}
|
|
|
|
&:hover{
|
|
|
|
.muteConteiner{
|
|
|
|
display: inline-block;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.mute{
|
|
|
|
display: inline-block;
|
|
|
|
transform: rotate(-90deg);
|
2020-05-08 07:18:50 +00:00
|
|
|
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;
|
|
|
|
border-radius: 2px;
|
|
|
|
cursor: pointer;
|
2020-04-19 20:09:12 +00:00
|
|
|
|
2020-04-21 10:49:23 +00:00
|
|
|
&:hover{
|
|
|
|
background-color: rgba(200,200,200,0.2);
|
|
|
|
}
|
|
|
|
`;
|
2020-04-19 20:09:12 +00:00
|
|
|
const StyledVideoViewer = styled.div`
|
2020-04-21 10:49:23 +00:00
|
|
|
|
|
|
|
color: #d1d1d1;
|
|
|
|
|
2020-04-20 06:32:27 +00:00
|
|
|
.playerWrapper{
|
2020-04-24 12:28:17 +00:00
|
|
|
display: ${props => props.isVideo ? "block" : "none"};
|
|
|
|
width: ${props => props.width}px;
|
|
|
|
height: ${props => props.height}px;
|
|
|
|
left: ${props => props.left}px;
|
|
|
|
top: ${props => props.top}px;
|
2020-04-22 17:44:09 +00:00
|
|
|
z-index: 4001;
|
2020-05-08 07:18:50 +00:00
|
|
|
position: fixed;
|
2020-04-20 06:32:27 +00:00
|
|
|
padding-bottom: 40px;
|
|
|
|
background-color: rgba(11,11,11,0.7);
|
2020-04-25 18:03:13 +00:00
|
|
|
|
|
|
|
video{
|
|
|
|
z-index: 4000;
|
|
|
|
}
|
2020-04-20 06:32:27 +00:00
|
|
|
}
|
2020-04-19 20:09:12 +00:00
|
|
|
`;
|
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
class ValumeBtn extends Component {
|
2020-04-30 08:37:16 +00:00
|
|
|
|
2020-04-19 20:09:12 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
}
|
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
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}
|
2020-04-30 08:20:30 +00:00
|
|
|
onMouseUp={this.props.handleSeekMouseUp}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<VideoControlBtn onClick={this.props.onChangeMute}>
|
2020-05-05 13:15:33 +00:00
|
|
|
<div className = "muteBtnContainer">
|
|
|
|
{this.props.muted ? <Icons.MediaMuteOffIcon size="scale" /> : <Icons.MediaMuteIcon size="scale" />}
|
|
|
|
</div>
|
2020-04-30 08:20:30 +00:00
|
|
|
</VideoControlBtn>
|
|
|
|
</StyledValumeContainer>
|
|
|
|
);
|
2020-04-19 20:09:12 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-30 08:20:30 +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 = {
|
2020-04-23 21:44:47 +00:00
|
|
|
url: this.props.url,
|
2020-04-19 20:09:12 +00:00
|
|
|
pip: false,
|
2020-04-30 08:37:16 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
load = url => {
|
|
|
|
this.setState({
|
|
|
|
url,
|
|
|
|
played: 0,
|
|
|
|
loaded: 0,
|
|
|
|
pip: false
|
|
|
|
})
|
|
|
|
}
|
2020-04-30 08:37:16 +00:00
|
|
|
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
|
|
|
|
}
|
2020-04-24 12:28:17 +00:00
|
|
|
this.setState(
|
|
|
|
{
|
2020-04-30 08:37:16 +00:00
|
|
|
url: newUrl,
|
|
|
|
playing: newPlaying
|
2020-04-24 12:28:17 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-05-05 13:15:33 +00:00
|
|
|
|
2020-04-19 20:09:12 +00:00
|
|
|
handlePlayPause = () => {
|
|
|
|
this.setState({ playing: !this.state.playing })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleStop = () => {
|
|
|
|
this.setState({ url: null, playing: false })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleVolumeChange = e => {
|
2020-04-30 08:20:30 +00:00
|
|
|
this.setState({
|
2020-04-19 20:09:12 +00:00
|
|
|
volume: parseFloat(e.target.value),
|
|
|
|
muted: false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
handleToggleMuted = () => {
|
|
|
|
this.setState({ muted: !this.state.muted })
|
|
|
|
}
|
|
|
|
|
|
|
|
handlePlay = () => {
|
|
|
|
this.setState({ playing: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleEnablePIP = () => {
|
|
|
|
this.setState({ pip: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDisablePIP = () => {
|
|
|
|
this.setState({ pip: false })
|
|
|
|
}
|
|
|
|
|
|
|
|
handlePause = () => {
|
|
|
|
this.setState({ playing: false })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSeekMouseDown = e => {
|
|
|
|
this.setState({ seeking: true })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSeekChange = e => {
|
|
|
|
this.setState({ played: parseFloat(e.target.value) })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSeekMouseUp = e => {
|
|
|
|
this.setState({ seeking: false })
|
|
|
|
this.player.seekTo(parseFloat(e.target.value))
|
|
|
|
}
|
|
|
|
|
|
|
|
handleProgress = state => {
|
|
|
|
if (!this.state.seeking) {
|
|
|
|
this.setState(state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleEnded = () => {
|
|
|
|
this.setState({ playing: this.state.loop })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleDuration = (duration) => {
|
|
|
|
this.setState({ duration })
|
|
|
|
}
|
|
|
|
|
|
|
|
handleClickFullscreen = () => {
|
|
|
|
screenfull.request(findDOMNode(this.player))
|
|
|
|
}
|
|
|
|
|
|
|
|
renderLoadButton = (url, label) => {
|
|
|
|
return (
|
|
|
|
<button onClick={() => this.load(url)}>
|
|
|
|
{label}
|
|
|
|
</button>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ref = player => {
|
|
|
|
this.player = player
|
|
|
|
}
|
|
|
|
|
2020-04-25 16:49:00 +00:00
|
|
|
resizePlayer = (videoSize, screenSize) => {
|
|
|
|
var ratio = videoSize.h / videoSize.w;
|
2020-04-30 08:20:30 +00:00
|
|
|
|
2020-04-25 16:49:00 +00:00
|
|
|
if (videoSize.h > screenSize.h) {
|
2020-04-30 08:20:30 +00:00
|
|
|
videoSize.h = screenSize.h;
|
|
|
|
videoSize.w = videoSize.h / ratio;
|
2020-04-25 16:49:00 +00:00
|
|
|
}
|
|
|
|
if (videoSize.w > screenSize.w) {
|
2020-04-30 08:20:30 +00:00
|
|
|
videoSize.w = screenSize.w;
|
|
|
|
videoSize.h = videoSize.w * ratio;
|
2020-04-25 16:49:00 +00:00
|
|
|
}
|
2020-04-30 08:20:30 +00:00
|
|
|
|
2020-04-25 16:49:00 +00:00
|
|
|
return {
|
2020-04-30 08:20:30 +00:00
|
|
|
width: videoSize.w,
|
|
|
|
height: videoSize.h
|
2020-04-25 16:49:00 +00:00
|
|
|
};
|
2020-04-30 08:20:30 +00:00
|
|
|
|
2020-04-25 16:49:00 +00:00
|
|
|
};
|
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
render() {
|
2020-04-19 20:09:12 +00:00
|
|
|
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;
|
2020-04-24 12:28:17 +00:00
|
|
|
var screenSize = {
|
2020-04-30 08:20:30 +00:00
|
|
|
w: window.innerWidth,
|
|
|
|
h: window.innerHeight
|
2020-04-24 12:28:17 +00:00
|
|
|
};
|
2020-05-05 13:15:33 +00:00
|
|
|
screenSize.h -= parentOffset + controlsHeight;
|
2020-04-24 12:28:17 +00:00
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
let width = screenSize.w;
|
|
|
|
let height = screenSize.h;
|
2020-04-24 12:28:17 +00:00
|
|
|
|
|
|
|
var centerAreaOx = screenSize.w / 2 + document.documentElement.scrollLeft;
|
|
|
|
var centerAreaOy = screenSize.h / 2 + document.documentElement.scrollTop;
|
|
|
|
|
2020-04-30 08:20:30 +00:00
|
|
|
if (document.getElementsByTagName('video')[0]) {
|
|
|
|
width = this.props.isVideo ? document.getElementsByTagName('video')[0].videoWidth || 480 : screenSize.w - 300;
|
2020-04-25 16:49:00 +00:00
|
|
|
height = this.props.isVideo ? document.getElementsByTagName('video')[0].videoHeight || 270 : 0;
|
2020-05-05 13:15:33 +00:00
|
|
|
|
2020-04-25 16:49:00 +00:00
|
|
|
let resize = this.resizePlayer(
|
|
|
|
{
|
|
|
|
w: width,
|
|
|
|
h: height
|
|
|
|
},
|
|
|
|
screenSize
|
|
|
|
)
|
|
|
|
width = resize.width;
|
|
|
|
height = resize.height;
|
2020-04-24 12:28:17 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-05 13:15:33 +00:00
|
|
|
let left = this.props.isVideo ? centerAreaOx - width / 2 : centerAreaOx + parentOffset / 4 - width / 2;
|
|
|
|
let top = this.props.isVideo ? centerAreaOy - height / 2 + parentOffset / 2 : centerAreaOy + parentOffset / 2;
|
2020-04-24 12:28:17 +00:00
|
|
|
|
2020-04-19 20:09:12 +00:00
|
|
|
return (
|
2020-04-30 08:20:30 +00:00
|
|
|
<StyledVideoViewer
|
|
|
|
isVideo={this.props.isVideo}
|
|
|
|
width={width}
|
|
|
|
height={height}
|
|
|
|
left={left}
|
|
|
|
top={top}
|
|
|
|
>
|
|
|
|
<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={e => console.log('onError', e)}
|
|
|
|
onProgress={this.handleProgress}
|
|
|
|
onDuration={this.handleDuration}
|
|
|
|
/>
|
2020-04-19 20:09:12 +00:00
|
|
|
</div>
|
2020-04-30 08:20:30 +00:00
|
|
|
<Controls
|
2020-05-05 13:15:33 +00:00
|
|
|
height={controlsHeight}
|
2020-04-30 08:20:30 +00:00
|
|
|
left={left}
|
|
|
|
top={top + height}
|
|
|
|
isVideo={this.props.isVideo}
|
|
|
|
>
|
|
|
|
<PlayBtn onClick={this.handlePlayPause} playing={playing} />
|
|
|
|
<Progress
|
|
|
|
value={played}
|
|
|
|
width={width - 220}
|
|
|
|
onMouseDown={this.handleSeekMouseDown}
|
2020-05-05 13:15:33 +00:00
|
|
|
handleSeekChange={this.handleSeekChange}
|
2020-04-30 08:20:30 +00:00
|
|
|
onMouseUp={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,
|
2020-04-30 08:37:16 +00:00
|
|
|
url: PropTypes.string,
|
2020-05-05 13:15:33 +00:00
|
|
|
playing: PropTypes.bool,
|
|
|
|
getOffset :PropTypes.func
|
2020-04-30 07:35:02 +00:00
|
|
|
}
|
2020-04-19 20:09:12 +00:00
|
|
|
|
|
|
|
export default VideoViewer;
|