parent
6b6e633c09
commit
3b707bdc12
2 changed files with 122 additions and 5 deletions
|
@ -108,6 +108,7 @@ export default class Video extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
currentTime: 0,
|
currentTime: 0,
|
||||||
duration: 0,
|
duration: 0,
|
||||||
|
volume: 0.5,
|
||||||
paused: true,
|
paused: true,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
containerWidth: false,
|
containerWidth: false,
|
||||||
|
@ -117,6 +118,15 @@ export default class Video extends React.PureComponent {
|
||||||
revealed: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed,
|
revealed: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// hard coded in components.scss
|
||||||
|
// any way to get ::before values programatically?
|
||||||
|
volWidth = 50;
|
||||||
|
volOffset = 70;
|
||||||
|
volHandleOffset = v => {
|
||||||
|
const offset = v * this.volWidth + this.volOffset;
|
||||||
|
return (offset > 110) ? 110 : offset;
|
||||||
|
}
|
||||||
|
|
||||||
setPlayerRef = c => {
|
setPlayerRef = c => {
|
||||||
this.player = c;
|
this.player = c;
|
||||||
|
|
||||||
|
@ -135,6 +145,10 @@ export default class Video extends React.PureComponent {
|
||||||
this.seek = c;
|
this.seek = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setVolumeRef = c => {
|
||||||
|
this.volume = c;
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseDownRoot = e => {
|
handleMouseDownRoot = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@ -155,6 +169,43 @@ export default class Video extends React.PureComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleVolumeMouseDown = e => {
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||||
|
document.addEventListener('mouseup', this.handleVolumeMouseUp, true);
|
||||||
|
document.addEventListener('touchmove', this.handleMouseVolSlide, true);
|
||||||
|
document.addEventListener('touchend', this.handleVolumeMouseUp, true);
|
||||||
|
|
||||||
|
this.handleMouseVolSlide(e);
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVolumeMouseUp = () => {
|
||||||
|
document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
|
||||||
|
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
|
||||||
|
document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
|
||||||
|
document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseVolSlide = throttle(e => {
|
||||||
|
|
||||||
|
const rect = this.volume.getBoundingClientRect();
|
||||||
|
const x = (e.clientX - rect.left) / this.volWidth; //x position within the element.
|
||||||
|
|
||||||
|
if(!isNaN(x)) {
|
||||||
|
var slideamt = x;
|
||||||
|
if(x > 1) {
|
||||||
|
slideamt = 1;
|
||||||
|
} else if(x < 0) {
|
||||||
|
slideamt = 0;
|
||||||
|
}
|
||||||
|
this.video.volume = slideamt;
|
||||||
|
this.setState({ volume: slideamt });
|
||||||
|
}
|
||||||
|
}, 60);
|
||||||
|
|
||||||
handleMouseDown = e => {
|
handleMouseDown = e => {
|
||||||
document.addEventListener('mousemove', this.handleMouseMove, true);
|
document.addEventListener('mousemove', this.handleMouseMove, true);
|
||||||
document.addEventListener('mouseup', this.handleMouseUp, true);
|
document.addEventListener('mouseup', this.handleMouseUp, true);
|
||||||
|
@ -290,10 +341,13 @@ export default class Video extends React.PureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive } = this.props;
|
const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive } = this.props;
|
||||||
const { containerWidth, currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
||||||
const progress = (currentTime / duration) * 100;
|
const progress = (currentTime / duration) * 100;
|
||||||
const playerStyle = {};
|
const playerStyle = {};
|
||||||
|
|
||||||
|
const volumeWidth = (muted) ? 0 : volume * this.volWidth;
|
||||||
|
const volumeHandleLoc = (muted) ? this.volHandleOffset(0) : this.volHandleOffset(volume);
|
||||||
|
|
||||||
const computedClass = classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth });
|
const computedClass = classNames('video-player', { inactive: !revealed, detailed, inline: inline && !fullscreen, fullscreen, letterbox, 'full-width': fullwidth });
|
||||||
|
|
||||||
let { width, height } = this.props;
|
let { width, height } = this.props;
|
||||||
|
@ -346,6 +400,7 @@ export default class Video extends React.PureComponent {
|
||||||
title={alt}
|
title={alt}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
|
volume={volume}
|
||||||
onClick={this.togglePlay}
|
onClick={this.togglePlay}
|
||||||
onPlay={this.handlePlay}
|
onPlay={this.handlePlay}
|
||||||
onPause={this.handlePause}
|
onPause={this.handlePause}
|
||||||
|
@ -374,9 +429,15 @@ export default class Video extends React.PureComponent {
|
||||||
<div className='video-player__buttons-bar'>
|
<div className='video-player__buttons-bar'>
|
||||||
<div className='video-player__buttons left'>
|
<div className='video-player__buttons left'>
|
||||||
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
|
<button type='button' aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} onClick={this.togglePlay}><i className={classNames('fa fa-fw', { 'fa-play': paused, 'fa-pause': !paused })} /></button>
|
||||||
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
|
<button type='button' aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} onMouseEnter={this.volumeSlider} onMouseLeave={this.volumeSlider} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button>
|
||||||
|
<div className='video-player__volume' onMouseDown={this.handleVolumeMouseDown} ref={this.setVolumeRef}>
|
||||||
{!onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
|
<div className='video-player__volume__current' style={{ width: `${volumeWidth}px` }} />
|
||||||
|
<span
|
||||||
|
className={classNames('video-player__volume__handle')}
|
||||||
|
tabIndex='0'
|
||||||
|
style={{ left: `${volumeHandleLoc}px` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{(detailed || fullscreen) &&
|
{(detailed || fullscreen) &&
|
||||||
<span>
|
<span>
|
||||||
|
@ -388,6 +449,7 @@ export default class Video extends React.PureComponent {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='video-player__buttons right'>
|
<div className='video-player__buttons right'>
|
||||||
|
{!onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>}
|
||||||
{(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
|
{(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>}
|
||||||
{onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-compress' /></button>}
|
{onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-compress' /></button>}
|
||||||
<button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
|
<button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><i className={classNames('fa fa-fw', { 'fa-arrows-alt': !fullscreen, 'fa-compress': fullscreen })} /></button>
|
||||||
|
|
|
@ -277,6 +277,19 @@
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detailed,
|
||||||
|
.fullscreen {
|
||||||
|
.video-player__volume__current,
|
||||||
|
.video-player__volume::before {
|
||||||
|
bottom: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player__volume__handle {
|
||||||
|
bottom: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.video-player {
|
.video-player {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -432,7 +445,7 @@
|
||||||
|
|
||||||
&__time-current {
|
&__time-current {
|
||||||
color: $white;
|
color: $white;
|
||||||
margin-left: 10px;
|
margin-left: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__time-sep {
|
&__time-sep {
|
||||||
|
@ -445,6 +458,48 @@
|
||||||
color: $white;
|
color: $white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__volume {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 24px;
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
width: 50px;
|
||||||
|
background: rgba($white, 0.35);
|
||||||
|
border-radius: 4px;
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 4px;
|
||||||
|
left: 70px;
|
||||||
|
bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__current {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
left: 70px;
|
||||||
|
bottom: 20px;
|
||||||
|
background: lighten($ui-highlight-color, 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__handle {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
bottom: 16px;
|
||||||
|
left: 70px;
|
||||||
|
transition: opacity .1s ease;
|
||||||
|
background: lighten($ui-highlight-color, 8%);
|
||||||
|
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__seek {
|
&__seek {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
|
Loading…
Reference in a new issue