Updated Video component
• Updated: - Video component - Dismiss bar when not hovering and playing - Controls organization - Seek handle size • Added: - Video stats popover - Picture in picture controls - Button titles - Loading icon when buffering - New styles
This commit is contained in:
parent
4e72076b98
commit
2b69ddc849
@ -1,15 +1,25 @@
|
|||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||||
import { defineMessages, injectIntl } from 'react-intl'
|
import { defineMessages, injectIntl } from 'react-intl'
|
||||||
import { is } from 'immutable'
|
import { is } from 'immutable'
|
||||||
import throttle from 'lodash.throttle'
|
import throttle from 'lodash.throttle'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import { decode } from 'blurhash'
|
import { decode } from 'blurhash'
|
||||||
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen'
|
import { isFullscreen, requestFullscreen, exitFullscreen } from '../utils/fullscreen'
|
||||||
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'
|
import { isPanoramic, isPortrait, minimumAspectRatio, maximumAspectRatio } from '../utils/media_aspect_ratio'
|
||||||
|
import {
|
||||||
|
openPopover,
|
||||||
|
} from '../actions/popover'
|
||||||
import { displayMedia } from '../initial_state'
|
import { displayMedia } from '../initial_state'
|
||||||
|
import {
|
||||||
|
CX,
|
||||||
|
POPOVER_VIDEO_STATS,
|
||||||
|
} from '../constants'
|
||||||
import Button from './button'
|
import Button from './button'
|
||||||
|
import Icon from './icon'
|
||||||
import Text from './text'
|
import Text from './text'
|
||||||
|
|
||||||
const cx = classNames.bind(_s)
|
// check every 50 ms (do not use lower values)
|
||||||
|
const checkInterval = 50.0
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
play: { id: 'video.play', defaultMessage: 'Play' },
|
play: { id: 'video.play', defaultMessage: 'Play' },
|
||||||
@ -21,9 +31,10 @@ const messages = defineMessages({
|
|||||||
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' },
|
||||||
sensitive: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },
|
sensitive: { id: 'status.sensitive_warning', defaultMessage: 'Sensitive content' },
|
||||||
hidden: { id: 'status.media_hidden', defaultMessage: 'Media hidden' },
|
hidden: { id: 'status.media_hidden', defaultMessage: 'Media hidden' },
|
||||||
|
video_stats: { id: 'video.stats_label', defaultMessage: 'Video meta stats' },
|
||||||
})
|
})
|
||||||
|
|
||||||
const formatTime = secondsNum => {
|
const formatTime = (secondsNum) => {
|
||||||
let hours = Math.floor(secondsNum / 3600)
|
let hours = Math.floor(secondsNum / 3600)
|
||||||
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60)
|
let minutes = Math.floor((secondsNum - (hours * 3600)) / 60)
|
||||||
let seconds = secondsNum - (hours * 3600) - (minutes * 60)
|
let seconds = secondsNum - (hours * 3600) - (minutes * 60)
|
||||||
@ -35,7 +46,7 @@ const formatTime = secondsNum => {
|
|||||||
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`
|
return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const findElementPosition = el => {
|
export const findElementPosition = (el) => {
|
||||||
let box
|
let box
|
||||||
|
|
||||||
if (el.getBoundingClientRect && el.parentNode) {
|
if (el.getBoundingClientRect && el.parentNode) {
|
||||||
@ -88,9 +99,20 @@ export const getPointerPosition = (el, event) => {
|
|||||||
return position
|
return position
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
onOpenVideoStatsPopover(targetRef, meta) {
|
||||||
|
dispatch(openPopover(POPOVER_VIDEO_STATS, {
|
||||||
|
targetRef,
|
||||||
|
meta,
|
||||||
|
position: 'top',
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export default
|
export default
|
||||||
@injectIntl
|
@injectIntl
|
||||||
class Video extends PureComponent {
|
@connect(null, mapDispatchToProps)
|
||||||
|
class Video extends ImmutablePureComponent {
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
preview: PropTypes.string,
|
preview: PropTypes.string,
|
||||||
@ -108,6 +130,8 @@ class Video extends PureComponent {
|
|||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
blurhash: PropTypes.string,
|
blurhash: PropTypes.string,
|
||||||
aspectRatio: PropTypes.number,
|
aspectRatio: PropTypes.number,
|
||||||
|
meta: ImmutablePropTypes.map,
|
||||||
|
onOpenVideoStatsPopover: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -116,6 +140,7 @@ class Video extends PureComponent {
|
|||||||
volume: 0.5,
|
volume: 0.5,
|
||||||
paused: true,
|
paused: true,
|
||||||
dragging: false,
|
dragging: false,
|
||||||
|
draggingVolume: false,
|
||||||
containerWidth: this.props.width,
|
containerWidth: this.props.width,
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
hovered: false,
|
hovered: false,
|
||||||
@ -123,29 +148,102 @@ class Video extends PureComponent {
|
|||||||
hoveringVolumeButton: false,
|
hoveringVolumeButton: false,
|
||||||
hoveringVolumeControl: false,
|
hoveringVolumeControl: false,
|
||||||
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
||||||
|
pipAvailable: true,
|
||||||
|
isBuffering: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bufferCheckInterval = null
|
||||||
|
lastPlayPos = 0
|
||||||
volHeight = 100
|
volHeight = 100
|
||||||
volOffset = 13
|
volOffset = 13
|
||||||
|
|
||||||
volHandleOffset = v => {
|
componentDidMount() {
|
||||||
|
const { meta, blurhash } = this.props
|
||||||
|
|
||||||
|
document.addEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
||||||
|
|
||||||
|
if (blurhash) {
|
||||||
|
this._decode()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta) {
|
||||||
|
this.setState({ duration: parseInt(meta.get('duration')) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('pictureInPictureEnabled' in document) {
|
||||||
|
this.setState({ pipAvailable: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
||||||
|
document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
||||||
|
|
||||||
|
clearInterval(this.bufferCheckInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
|
||||||
|
this.setState({ revealed: nextProps.visible })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevState.revealed && !this.state.revealed && this.video) {
|
||||||
|
this.video.pause()
|
||||||
|
}
|
||||||
|
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
|
||||||
|
this._decode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkBuffering = () => {
|
||||||
|
const { isBuffering, paused } = this.state
|
||||||
|
const { currentTime } = this.video
|
||||||
|
|
||||||
|
// checking offset should be at most the check interval
|
||||||
|
// but allow for some margin
|
||||||
|
let offset = (checkInterval - 20) / 1000
|
||||||
|
|
||||||
|
// if no buffering is currently detected,
|
||||||
|
// and the position does not seem to increase
|
||||||
|
// and the player isn't manually paused...
|
||||||
|
if (!isBuffering && currentTime < (this.lastPlayPos + offset) && !paused) {
|
||||||
|
this.setState({ isBuffering: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we were buffering but the player has advanced,
|
||||||
|
// then there is no buffering
|
||||||
|
if (isBuffering && currentTime > (this.lastPlayPos + offset) && !paused) {
|
||||||
|
this.setState({ isBuffering: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastPlayPos = currentTime
|
||||||
|
}
|
||||||
|
|
||||||
|
volHandleOffset = (v) => {
|
||||||
const offset = v * this.volHeight + this.volOffset
|
const offset = v * this.volHeight + this.volOffset
|
||||||
return (offset > 110) ? 110 : offset
|
return (offset > 110) ? 110 : offset
|
||||||
}
|
}
|
||||||
|
|
||||||
setPlayerRef = c => {
|
setPlayerRef = (n) => {
|
||||||
this.player = c
|
this.player = n
|
||||||
|
|
||||||
if (c) {
|
if (n) {
|
||||||
if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth)
|
if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth)
|
||||||
this.setState({
|
this.setState({
|
||||||
containerWidth: c.offsetWidth,
|
containerWidth: n.offsetWidth,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setVideoRef = c => {
|
setVideoRef = (n) => {
|
||||||
this.video = c
|
this.video = n
|
||||||
|
|
||||||
if (this.video) {
|
if (this.video) {
|
||||||
const { volume, muted } = this.video
|
const { volume, muted } = this.video
|
||||||
@ -156,26 +254,34 @@ class Video extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setSeekRef = c => {
|
setSeekRef = (n) => {
|
||||||
this.seek = c
|
this.seek = n
|
||||||
}
|
}
|
||||||
|
|
||||||
setVolumeRef = c => {
|
setVolumeRef = (n) => {
|
||||||
this.volume = c
|
this.volume = n
|
||||||
}
|
}
|
||||||
|
|
||||||
setCanvasRef = c => {
|
setCanvasRef = (n) => {
|
||||||
this.canvas = c
|
this.canvas = n
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClickRoot = e => e.stopPropagation()
|
setSettingsBtnRef = (n) => {
|
||||||
|
this.settingsBtn = n
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClickRoot = (e) => e.stopPropagation()
|
||||||
|
|
||||||
handlePlay = () => {
|
handlePlay = () => {
|
||||||
this.setState({ paused: false })
|
this.setState({ paused: false })
|
||||||
|
|
||||||
|
this.bufferCheckInterval = setInterval(this.checkBuffering, checkInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePause = () => {
|
handlePause = () => {
|
||||||
this.setState({ paused: true })
|
this.setState({ paused: true })
|
||||||
|
|
||||||
|
clearInterval(this.bufferCheckInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTimeUpdate = () => {
|
handleTimeUpdate = () => {
|
||||||
@ -186,7 +292,7 @@ class Video extends PureComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVolumeMouseDown = e => {
|
handleVolumeMouseDown = (e) => {
|
||||||
document.addEventListener('mousemove', this.handleMouseVolSlide, true)
|
document.addEventListener('mousemove', this.handleMouseVolSlide, true)
|
||||||
document.addEventListener('mouseup', this.handleVolumeMouseUp, true)
|
document.addEventListener('mouseup', this.handleVolumeMouseUp, true)
|
||||||
document.addEventListener('touchmove', this.handleMouseVolSlide, true)
|
document.addEventListener('touchmove', this.handleMouseVolSlide, true)
|
||||||
@ -196,16 +302,22 @@ class Video extends PureComponent {
|
|||||||
|
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
this.setState({ draggingVolume: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVolumeMouseUp = () => {
|
handleVolumeMouseUp = () => {
|
||||||
|
this.handleMouseLeaveVolumeControl()
|
||||||
|
|
||||||
document.removeEventListener('mousemove', this.handleMouseVolSlide, true)
|
document.removeEventListener('mousemove', this.handleMouseVolSlide, true)
|
||||||
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true)
|
document.removeEventListener('mouseup', this.handleVolumeMouseUp, true)
|
||||||
document.removeEventListener('touchmove', this.handleMouseVolSlide, true)
|
document.removeEventListener('touchmove', this.handleMouseVolSlide, true)
|
||||||
document.removeEventListener('touchend', this.handleVolumeMouseUp, true)
|
document.removeEventListener('touchend', this.handleVolumeMouseUp, true)
|
||||||
|
|
||||||
|
this.setState({ draggingVolume: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseVolSlide = throttle(e => {
|
handleMouseVolSlide = throttle((e) => {
|
||||||
const rect = this.volume.getBoundingClientRect()
|
const rect = this.volume.getBoundingClientRect()
|
||||||
const y = 1 - ((e.clientY - rect.top) / this.volHeight)
|
const y = 1 - ((e.clientY - rect.top) / this.volHeight)
|
||||||
|
|
||||||
@ -221,7 +333,7 @@ class Video extends PureComponent {
|
|||||||
}
|
}
|
||||||
}, 60)
|
}, 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)
|
||||||
document.addEventListener('touchmove', this.handleMouseMove, true)
|
document.addEventListener('touchmove', this.handleMouseMove, true)
|
||||||
@ -271,36 +383,20 @@ class Video extends PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
togglePip = () => {
|
||||||
document.addEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
try {
|
||||||
document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
if (this.video !== document.pictureInPictureElement) {
|
||||||
document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
if (this.state.paused) {
|
||||||
document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
this.video.play()
|
||||||
|
|
||||||
if (this.props.blurhash) {
|
|
||||||
this._decode()
|
|
||||||
}
|
}
|
||||||
|
setTimeout(() => { // : hack :
|
||||||
|
this.video.requestPictureInPicture()
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
document.exitPictureInPicture()
|
||||||
}
|
}
|
||||||
|
} catch(e) {
|
||||||
componentWillUnmount() {
|
//
|
||||||
document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true)
|
|
||||||
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true)
|
|
||||||
document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true)
|
|
||||||
document.removeEventListener('MSFullscreenChange', this.handleFullscreenChange, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
|
||||||
if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
|
|
||||||
this.setState({ revealed: nextProps.visible })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevProps, prevState) {
|
|
||||||
if (prevState.revealed && !this.state.revealed && this.video) {
|
|
||||||
this.video.pause()
|
|
||||||
}
|
|
||||||
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
|
|
||||||
this._decode()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,17 +428,19 @@ class Video extends PureComponent {
|
|||||||
this.setState({ hoveringVolumeButton: true })
|
this.setState({ hoveringVolumeButton: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseLeaveAudio = throttle(e => {
|
handleMouseLeaveAudio = throttle(() => {
|
||||||
this.setState({ hoveringVolumeButton: false })
|
this.setState({ hoveringVolumeButton: false })
|
||||||
}, 2000)
|
}, 2500)
|
||||||
|
|
||||||
handleMouseEnterVolumeControl = () => {
|
handleMouseEnterVolumeControl = () => {
|
||||||
this.setState({ hoveringVolumeControl: true })
|
this.setState({ hoveringVolumeControl: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseLeaveVolumeControl = throttle(e => {
|
handleMouseLeaveVolumeControl = throttle(() => {
|
||||||
|
if (!this.state.draggingVolume) {
|
||||||
this.setState({ hoveringVolumeControl: false })
|
this.setState({ hoveringVolumeControl: false })
|
||||||
}, 2000)
|
}
|
||||||
|
}, 2500)
|
||||||
|
|
||||||
toggleMute = () => {
|
toggleMute = () => {
|
||||||
this.video.muted = !this.video.muted
|
this.video.muted = !this.video.muted
|
||||||
@ -383,6 +481,10 @@ class Video extends PureComponent {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOnClickSettings = () => {
|
||||||
|
this.props.onOpenVideoStatsPopover(this.settingsBtn, this.props.meta)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
preview,
|
preview,
|
||||||
@ -409,7 +511,9 @@ class Video extends PureComponent {
|
|||||||
muted,
|
muted,
|
||||||
revealed,
|
revealed,
|
||||||
hoveringVolumeButton,
|
hoveringVolumeButton,
|
||||||
hoveringVolumeControl
|
hoveringVolumeControl,
|
||||||
|
pipAvailable,
|
||||||
|
isBuffering,
|
||||||
} = this.state
|
} = this.state
|
||||||
|
|
||||||
const progress = (currentTime / duration) * 100
|
const progress = (currentTime / duration) * 100
|
||||||
@ -454,21 +558,38 @@ class Video extends PureComponent {
|
|||||||
|
|
||||||
// : todo spoiler :
|
// : todo spoiler :
|
||||||
|
|
||||||
const seekHandleClasses = cx({
|
const mainContainerClasses = CX({
|
||||||
|
default: 1,
|
||||||
|
mt10: 1,
|
||||||
|
outlineNone: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const seekHandleClasses = CX({
|
||||||
default: 1,
|
default: 1,
|
||||||
posAbs: 1,
|
posAbs: 1,
|
||||||
circle: 1,
|
circle: 1,
|
||||||
px10: 1,
|
height20PX: 1,
|
||||||
py10: 1,
|
width20PX: 1,
|
||||||
bgBrand: 1,
|
bgTransparent: 1,
|
||||||
mlNeg5PX: 1,
|
mlNeg5PX: 1,
|
||||||
|
mr5: 1,
|
||||||
z3: 1,
|
z3: 1,
|
||||||
boxShadow1: 1,
|
alignItemsCenter: 1,
|
||||||
|
justifyContentCenter: 1,
|
||||||
opacity0: !dragging,
|
opacity0: !dragging,
|
||||||
opacity1: dragging || hovered,
|
opacity1: dragging || hovered,
|
||||||
})
|
})
|
||||||
|
|
||||||
const progressClasses = cx({
|
const seekInnerHandleClasses = CX({
|
||||||
|
default: 1,
|
||||||
|
circle: 1,
|
||||||
|
height14PX: 1,
|
||||||
|
width14PX: 1,
|
||||||
|
bgBrand: 1,
|
||||||
|
boxShadow1: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
const progressClasses = CX({
|
||||||
default: 1,
|
default: 1,
|
||||||
radiusSmall: 1,
|
radiusSmall: 1,
|
||||||
mt10: 1,
|
mt10: 1,
|
||||||
@ -476,7 +597,7 @@ class Video extends PureComponent {
|
|||||||
height4PX: 1,
|
height4PX: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const volumeControlClasses = cx({
|
const volumeControlClasses = CX({
|
||||||
default: 1,
|
default: 1,
|
||||||
posAbs: 1,
|
posAbs: 1,
|
||||||
bgBlackOpaque: 1,
|
bgBlackOpaque: 1,
|
||||||
@ -486,9 +607,32 @@ class Video extends PureComponent {
|
|||||||
displayNone: !hoveringVolumeButton && !hoveringVolumeControl || !hovered,
|
displayNone: !hoveringVolumeButton && !hoveringVolumeControl || !hovered,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const videoControlsBackgroundClasses = CX({
|
||||||
|
default: 1,
|
||||||
|
z2: 1,
|
||||||
|
px15: 1,
|
||||||
|
videoPlayerControlsBackground: 1,
|
||||||
|
posAbs: 1,
|
||||||
|
bottom0: 1,
|
||||||
|
right0: 1,
|
||||||
|
left0: 1,
|
||||||
|
displayNone: !hovered && !paused,
|
||||||
|
})
|
||||||
|
|
||||||
|
const overlayClasses = CX({
|
||||||
|
default: 1,
|
||||||
|
top50PC: 1,
|
||||||
|
left50PC: 1,
|
||||||
|
posAbs: 1,
|
||||||
|
z2: 1,
|
||||||
|
alignItemsCenter: 1,
|
||||||
|
justifyContentCenter: 1,
|
||||||
|
displayNone: !paused,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={[_s.default].join(' ')}
|
className={mainContainerClasses}
|
||||||
style={playerStyle}
|
style={playerStyle}
|
||||||
ref={this.setPlayerRef}
|
ref={this.setPlayerRef}
|
||||||
onMouseEnter={this.handleMouseEnter}
|
onMouseEnter={this.handleMouseEnter}
|
||||||
@ -507,6 +651,22 @@ class Video extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div className={overlayClasses}>
|
||||||
|
{
|
||||||
|
paused && !isBuffering &&
|
||||||
|
<button
|
||||||
|
onClick={this.togglePlay}
|
||||||
|
className={[_s.default, _s.outlineNone, _s.cursorPointer, _s.alignItemsCenter, _s.justifyContentCenter, _s.posAbs, _s.bgBlackOpaque, _s.circle, _s.height60PX, _s.width60PX].join(' ')}
|
||||||
|
>
|
||||||
|
<Icon id='play' size='24px' className={_s.fillWhite} />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
{
|
||||||
|
isBuffering &&
|
||||||
|
<Icon id='loading' size='40px' className={[_s.default, _s.posAbs].join(' ')} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
{
|
{
|
||||||
revealed &&
|
revealed &&
|
||||||
<video
|
<video
|
||||||
@ -571,7 +731,7 @@ class Video extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.default, _s.z2, _s.px15, _s.videoPlayerControlsBackground, _s.posAbs, _s.bottom0, _s.right0, _s.left0].join(' ')}>
|
<div className={videoControlsBackgroundClasses}>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={[_s.default, _s.cursorPointer, _s.height22PX, _s.videoPlayerSeek].join(' ')}
|
className={[_s.default, _s.cursorPointer, _s.height22PX, _s.videoPlayerSeek].join(' ')}
|
||||||
@ -589,7 +749,9 @@ class Video extends PureComponent {
|
|||||||
style={{
|
style={{
|
||||||
left: `${progress}%`
|
left: `${progress}%`
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<span className={seekInnerHandleClasses} />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.pb5, _s.noSelect].join(' ')}>
|
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.pb5, _s.noSelect].join(' ')}>
|
||||||
@ -599,18 +761,12 @@ class Video extends PureComponent {
|
|||||||
aria-label={intl.formatMessage(paused ? messages.play : messages.pause)}
|
aria-label={intl.formatMessage(paused ? messages.play : messages.pause)}
|
||||||
onClick={this.togglePlay}
|
onClick={this.togglePlay}
|
||||||
icon={paused ? 'play' : 'pause'}
|
icon={paused ? 'play' : 'pause'}
|
||||||
|
title={paused ? 'Play' : 'Pause'}
|
||||||
iconSize='16px'
|
iconSize='16px'
|
||||||
iconClassName={_s.fillWhite}
|
iconClassName={_s.fillWhite}
|
||||||
className={_s.pl0}
|
className={_s.pl0}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={[_s.default, _s.mlAuto, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
|
||||||
<Text color='white' size='small'>
|
|
||||||
{formatTime(currentTime)}
|
|
||||||
/
|
|
||||||
{formatTime(duration)}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
isNarrow
|
isNarrow
|
||||||
backgroundColor='none'
|
backgroundColor='none'
|
||||||
@ -620,17 +776,52 @@ class Video extends PureComponent {
|
|||||||
icon={muted ? 'audio-mute' : 'audio'}
|
icon={muted ? 'audio-mute' : 'audio'}
|
||||||
iconSize='24px'
|
iconSize='24px'
|
||||||
iconClassName={_s.fillWhite}
|
iconClassName={_s.fillWhite}
|
||||||
className={[_s.px10, _s.ml10].join(' ')}
|
className={[_s.px10, _s.mr10].join(' ')}
|
||||||
|
title='Volume'
|
||||||
onMouseEnter={this.handleMouseEnterAudio}
|
onMouseEnter={this.handleMouseEnterAudio}
|
||||||
onMouseLeave={this.handleMouseLeaveAudio}
|
onMouseLeave={this.handleMouseLeaveAudio}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Text color='white' size='small'>
|
||||||
|
{formatTime(currentTime)}
|
||||||
|
/
|
||||||
|
{formatTime(duration)}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<div className={[_s.default, _s.mlAuto, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||||
|
<Button
|
||||||
|
isNarrow
|
||||||
|
backgroundColor='none'
|
||||||
|
aria-label={intl.formatMessage(messages.video_stats)}
|
||||||
|
onClick={this.handleOnClickSettings}
|
||||||
|
icon='cog'
|
||||||
|
iconSize='20px'
|
||||||
|
iconClassName={_s.fillWhite}
|
||||||
|
className={[_s.px10, _s.pr0].join(' ')}
|
||||||
|
buttonRef={this.setSettingsBtnRef}
|
||||||
|
title='Video stats'
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
pipAvailable &&
|
||||||
|
<Button
|
||||||
|
isNarrow
|
||||||
|
backgroundColor='none'
|
||||||
|
aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)}
|
||||||
|
onClick={this.togglePip}
|
||||||
|
icon='pip'
|
||||||
|
iconSize='20px'
|
||||||
|
iconClassName={_s.fillWhite}
|
||||||
|
className={[_s.px10, _s.pr0].join(' ')}
|
||||||
|
title='Picture in Picture'
|
||||||
|
/>
|
||||||
|
}
|
||||||
<Button
|
<Button
|
||||||
isNarrow
|
isNarrow
|
||||||
backgroundColor='none'
|
backgroundColor='none'
|
||||||
aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)}
|
aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)}
|
||||||
onClick={this.toggleFullscreen}
|
onClick={this.toggleFullscreen}
|
||||||
icon={fullscreen ? 'minimize-fullscreen' : 'fullscreen'}
|
icon={fullscreen ? 'minimize-fullscreen' : 'fullscreen'}
|
||||||
|
title={fullscreen ? 'Minimize fullscreen' : 'Fullscreen'}
|
||||||
iconSize='20px'
|
iconSize='20px'
|
||||||
iconClassName={_s.fillWhite}
|
iconClassName={_s.fillWhite}
|
||||||
className={[_s.px10, _s.pr0].join(' ')}
|
className={[_s.px10, _s.pr0].join(' ')}
|
||||||
|
@ -431,6 +431,7 @@ body {
|
|||||||
.bottomAuto { bottom: auto; }
|
.bottomAuto { bottom: auto; }
|
||||||
|
|
||||||
.left0 { left: 0px; }
|
.left0 { left: 0px; }
|
||||||
|
.left50PC { left: 50%; }
|
||||||
|
|
||||||
.right0 { right: 0px; }
|
.right0 { right: 0px; }
|
||||||
.rightAuto { right: auto; }
|
.rightAuto { right: auto; }
|
||||||
@ -483,6 +484,7 @@ body {
|
|||||||
.height24PX { height: 24px; }
|
.height24PX { height: 24px; }
|
||||||
.height22PX { height: 22px; }
|
.height22PX { height: 22px; }
|
||||||
.height20PX { height: 20px; }
|
.height20PX { height: 20px; }
|
||||||
|
.height14PX { height: 14px; }
|
||||||
.height10PX { height: 10px; }
|
.height10PX { height: 10px; }
|
||||||
.height4PX { height: 4px; }
|
.height4PX { height: 4px; }
|
||||||
.height1PX { height: 1px; }
|
.height1PX { height: 1px; }
|
||||||
@ -514,6 +516,7 @@ body {
|
|||||||
.width60PX { width: 60px; }
|
.width60PX { width: 60px; }
|
||||||
.width50PX { width: 50px; }
|
.width50PX { width: 50px; }
|
||||||
.width20PX { width: 20px; }
|
.width20PX { width: 20px; }
|
||||||
|
.width14PX { width: 14px; }
|
||||||
.width10PX { width: 10px; }
|
.width10PX { width: 10px; }
|
||||||
.width4PX { width: 4px; }
|
.width4PX { width: 4px; }
|
||||||
.width1PX { width: 1px; }
|
.width1PX { width: 1px; }
|
||||||
@ -840,7 +843,7 @@ body {
|
|||||||
|
|
||||||
.videoPlayerVolume {
|
.videoPlayerVolume {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
right: 65px;
|
left: 68px;
|
||||||
bottom: 60px;
|
bottom: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user