diff options
author | Sumit Khanna <notify@battlepenguin.com> | 2018-11-27 05:25:38 -0600 |
---|---|---|
committer | Eugen Rochko <eugen@zeonfederated.com> | 2018-11-27 12:25:38 +0100 |
commit | f978afa4871c22caecb625a0b3eba533edfa309b (patch) | |
tree | b44b42b0c7024622ec212b4fd48575d5ae381ec9 /app | |
parent | b6681d21ad8f8569af447bfe2106a04eab32d153 (diff) |
Volume sliders for videos #7429 (#9366)
* Volume sliders for videos #7429 * Code formatting updates * Code formatting updates
Diffstat (limited to 'app')
-rw-r--r-- | app/javascript/mastodon/features/video/index.js | 70 | ||||
-rw-r--r-- | app/javascript/styles/mastodon/components.scss | 57 |
2 files changed, 122 insertions, 5 deletions
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js index 67f7580b9..3650fddb6 100644 --- a/app/javascript/mastodon/features/video/index.js +++ b/app/javascript/mastodon/features/video/index.js @@ -105,6 +105,7 @@ class Video extends React.PureComponent { state = { currentTime: 0, duration: 0, + volume: 0.5, paused: true, dragging: false, containerWidth: false, @@ -114,6 +115,15 @@ class Video extends React.PureComponent { revealed: displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all', }; + // 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 => { this.player = c; @@ -132,6 +142,10 @@ class Video extends React.PureComponent { this.seek = c; } + setVolumeRef = c => { + this.volume = c; + } + handleClickRoot = e => e.stopPropagation(); handlePlay = () => { @@ -149,6 +163,43 @@ 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 => { document.addEventListener('mousemove', this.handleMouseMove, true); document.addEventListener('mouseup', this.handleMouseUp, true); @@ -273,8 +324,11 @@ class Video extends React.PureComponent { render () { const { preview, src, inline, startTime, onOpenVideo, onCloseVideo, intl, alt, 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 volumeWidth = (muted) ? 0 : volume * this.volWidth; + const volumeHandleLoc = (muted) ? this.volHandleOffset(0) : this.volHandleOffset(volume); const playerStyle = {}; let { width, height } = this.props; @@ -326,6 +380,7 @@ class Video extends React.PureComponent { title={alt} width={width} height={height} + volume={volume} onClick={this.togglePlay} onPlay={this.handlePlay} onPause={this.handlePause} @@ -354,9 +409,15 @@ class Video extends React.PureComponent { <div className='video-player__buttons-bar'> <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(muted ? messages.unmute : messages.mute)} onClick={this.toggleMute}><i className={classNames('fa fa-fw', { 'fa-volume-off': muted, 'fa-volume-up': !muted })} /></button> - - {!onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></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}> + <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) && <span> @@ -368,6 +429,7 @@ class Video extends React.PureComponent { </div> <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>} {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> diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index a636d18c4..5765829a4 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4658,6 +4658,19 @@ a.status-card.compact:hover { z-index: 5; } +.detailed, +.fullscreen { + .video-player__volume__current, + .video-player__volume::before { + bottom: 27px; + } + + .video-player__volume__handle { + bottom: 23px; + } + +} + .video-player { overflow: hidden; position: relative; @@ -4806,7 +4819,7 @@ a.status-card.compact:hover { &__time-current { color: $white; - margin-left: 10px; + margin-left: 60px; } &__time-sep { @@ -4819,6 +4832,48 @@ a.status-card.compact:hover { 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 { cursor: pointer; height: 24px; |