Polish video player CSS, add timer on fullscreen/modal/public pages (#5928)
This commit is contained in:
		
							parent
							
								
									b0db4dad79
								
							
						
					
					
						commit
						70ce2a2095
					
				| @ -23,6 +23,7 @@ export default class VideoModal extends ImmutablePureComponent { | ||||
|             src={media.get('url')} | ||||
|             startTime={time} | ||||
|             onCloseVideo={onClose} | ||||
|             detailed | ||||
|             description={media.get('description')} | ||||
|           /> | ||||
|         </div> | ||||
|  | ||||
| @ -17,6 +17,18 @@ const messages = defineMessages({ | ||||
|   exit_fullscreen: { id: 'video.exit_fullscreen', defaultMessage: 'Exit full screen' }, | ||||
| }); | ||||
| 
 | ||||
| const formatTime = secondsNum => { | ||||
|   let hours   = Math.floor(secondsNum / 3600); | ||||
|   let minutes = Math.floor((secondsNum - (hours * 3600)) / 60); | ||||
|   let seconds = secondsNum - (hours * 3600) - (minutes * 60); | ||||
| 
 | ||||
|   if (hours   < 10) hours   = '0' + hours; | ||||
|   if (minutes < 10) minutes = '0' + minutes; | ||||
|   if (seconds < 10) seconds = '0' + seconds; | ||||
| 
 | ||||
|   return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`; | ||||
| }; | ||||
| 
 | ||||
| const findElementPosition = el => { | ||||
|   let box; | ||||
| 
 | ||||
| @ -83,11 +95,13 @@ export default class Video extends React.PureComponent { | ||||
|     startTime: PropTypes.number, | ||||
|     onOpenVideo: PropTypes.func, | ||||
|     onCloseVideo: PropTypes.func, | ||||
|     detailed: PropTypes.bool, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|   state = { | ||||
|     progress: 0, | ||||
|     currentTime: 0, | ||||
|     duration: 0, | ||||
|     paused: true, | ||||
|     dragging: false, | ||||
|     fullscreen: false, | ||||
| @ -117,7 +131,10 @@ export default class Video extends React.PureComponent { | ||||
|   } | ||||
| 
 | ||||
|   handleTimeUpdate = () => { | ||||
|     this.setState({ progress: 100 * (this.video.currentTime / this.video.duration) }); | ||||
|     this.setState({ | ||||
|       currentTime: Math.floor(this.video.currentTime), | ||||
|       duration: Math.floor(this.video.duration), | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   handleMouseDown = e => { | ||||
| @ -143,8 +160,10 @@ export default class Video extends React.PureComponent { | ||||
| 
 | ||||
|   handleMouseMove = throttle(e => { | ||||
|     const { x } = getPointerPosition(this.seek, e); | ||||
|     this.video.currentTime = this.video.duration * x; | ||||
|     this.setState({ progress: x * 100 }); | ||||
|     const currentTime = Math.floor(this.video.duration * x); | ||||
| 
 | ||||
|     this.video.currentTime = currentTime; | ||||
|     this.setState({ currentTime }); | ||||
|   }, 60); | ||||
| 
 | ||||
|   togglePlay = () => { | ||||
| @ -226,11 +245,12 @@ export default class Video extends React.PureComponent { | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt } = this.props; | ||||
|     const { progress, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; | ||||
|     const { preview, src, width, height, startTime, onOpenVideo, onCloseVideo, intl, alt, detailed } = this.props; | ||||
|     const { currentTime, duration, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state; | ||||
|     const progress = (currentTime / duration) * 100; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={classNames('video-player', { inactive: !revealed, inline: width && height && !fullscreen, fullscreen })} style={{ width, height }} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> | ||||
|       <div className={classNames('video-player', { inactive: !revealed, detailed, inline: width && height && !fullscreen, fullscreen })} style={{ width, height }} ref={this.setPlayerRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> | ||||
|         <video | ||||
|           ref={this.setVideoRef} | ||||
|           src={src} | ||||
| @ -267,16 +287,27 @@ export default class Video extends React.PureComponent { | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|           <div className='video-player__buttons left'> | ||||
|             <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 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 aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>} | ||||
|           </div> | ||||
|           <div className='video-player__buttons-bar'> | ||||
|             <div className='video-player__buttons left'> | ||||
|               <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 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> | ||||
| 
 | ||||
|           <div className='video-player__buttons right'> | ||||
|             {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>} | ||||
|             {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-times' /></button>} | ||||
|             <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> | ||||
|               {!onCloseVideo && <button aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><i className='fa fa-fw fa-eye' /></button>} | ||||
| 
 | ||||
|               {(detailed || fullscreen) && | ||||
|                 <span> | ||||
|                   <span className='video-player__time-current'>{formatTime(currentTime)}</span> | ||||
|                   <span className='video-player__time-sep'>/</span> | ||||
|                   <span className='video-player__time-total'>{formatTime(duration)}</span> | ||||
|                 </span> | ||||
|               } | ||||
|             </div> | ||||
| 
 | ||||
|             <div className='video-player__buttons right'> | ||||
|               {(!fullscreen && onOpenVideo) && <button aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><i className='fa fa-fw fa-expand' /></button>} | ||||
|               {onCloseVideo && <button aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><i className='fa fa-fw fa-compress' /></button>} | ||||
|               <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> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -3998,6 +3998,7 @@ button.icon-button.active i.fa-retweet { | ||||
|   position: relative; | ||||
|   background: $base-shadow-color; | ||||
|   max-width: 100%; | ||||
|   border-radius: 4px; | ||||
| 
 | ||||
|   video { | ||||
|     height: 100%; | ||||
| @ -4032,8 +4033,8 @@ button.icon-button.active i.fa-retweet { | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     box-sizing: border-box; | ||||
|     background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 60%, transparent); | ||||
|     padding: 0 10px; | ||||
|     background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent); | ||||
|     padding: 0 15px; | ||||
|     opacity: 0; | ||||
|     transition: opacity .1s ease; | ||||
| 
 | ||||
| @ -4086,40 +4087,67 @@ button.icon-button.active i.fa-retweet { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__buttons { | ||||
|   &__buttons-bar { | ||||
|     display: flex; | ||||
|     justify-content: space-between; | ||||
|     padding-bottom: 10px; | ||||
|   } | ||||
| 
 | ||||
|   &__buttons { | ||||
|     font-size: 16px; | ||||
|     white-space: nowrap; | ||||
|     overflow: hidden; | ||||
|     text-overflow: ellipsis; | ||||
| 
 | ||||
|     &.left { | ||||
|       float: left; | ||||
| 
 | ||||
|       button { | ||||
|         padding-right: 10px; | ||||
|         padding-left: 0; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     &.right { | ||||
|       float: right; | ||||
| 
 | ||||
|       button { | ||||
|         padding-left: 10px; | ||||
|         padding-right: 0; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     button { | ||||
|       background: transparent; | ||||
|       padding: 0; | ||||
|       padding: 2px 10px; | ||||
|       font-size: 16px; | ||||
|       border: 0; | ||||
|       color: $white; | ||||
|       color: rgba($white, 0.75); | ||||
| 
 | ||||
|       &:active, | ||||
|       &:hover, | ||||
|       &:focus { | ||||
|         color: $ui-highlight-color; | ||||
|         color: $white; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__time-sep, | ||||
|   &__time-total, | ||||
|   &__time-current { | ||||
|     font-size: 14px; | ||||
|     font-weight: 500; | ||||
|   } | ||||
| 
 | ||||
|   &__time-current { | ||||
|     color: $white; | ||||
|     margin-left: 10px; | ||||
|   } | ||||
| 
 | ||||
|   &__time-sep { | ||||
|     display: inline-block; | ||||
|     margin: 0 6px; | ||||
|   } | ||||
| 
 | ||||
|   &__time-sep, | ||||
|   &__time-total { | ||||
|     color: $white; | ||||
|   } | ||||
| 
 | ||||
|   &__seek { | ||||
|     cursor: pointer; | ||||
|     height: 24px; | ||||
| @ -4129,6 +4157,7 @@ button.icon-button.active i.fa-retweet { | ||||
|       content: ""; | ||||
|       width: 100%; | ||||
|       background: rgba($white, 0.35); | ||||
|       border-radius: 4px; | ||||
|       display: block; | ||||
|       position: absolute; | ||||
|       height: 4px; | ||||
| @ -4140,8 +4169,9 @@ button.icon-button.active i.fa-retweet { | ||||
|       display: block; | ||||
|       position: absolute; | ||||
|       height: 4px; | ||||
|       border-radius: 4px; | ||||
|       top: 10px; | ||||
|       background: $ui-highlight-color; | ||||
|       background: lighten($ui-highlight-color, 8%); | ||||
|     } | ||||
| 
 | ||||
|     &__buffer { | ||||
| @ -4158,7 +4188,8 @@ button.icon-button.active i.fa-retweet { | ||||
|       top: 6px; | ||||
|       margin-left: -6px; | ||||
|       transition: opacity .1s ease; | ||||
|       background: $ui-highlight-color; | ||||
|       background: lighten($ui-highlight-color, 8%); | ||||
|       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2); | ||||
|       pointer-events: none; | ||||
| 
 | ||||
|       &.active { | ||||
| @ -4172,6 +4203,16 @@ button.icon-button.active i.fa-retweet { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &.detailed, | ||||
|   &.fullscreen { | ||||
|     .video-player__buttons { | ||||
|       button { | ||||
|         padding-top: 10px; | ||||
|         padding-bottom: 10px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .media-spoiler-video { | ||||
|  | ||||
| @ -22,7 +22,7 @@ | ||||
|   - if !status.media_attachments.empty? | ||||
|     - if status.media_attachments.first.video? | ||||
|       - video = status.media_attachments.first | ||||
|       %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive?, width: 670, height: 380) }} | ||||
|       %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive?, width: 670, height: 380, detailed: true) }} | ||||
|     - else | ||||
|       %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 380, sensitive: status.sensitive?, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }} | ||||
|   - elsif status.preview_cards.first | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user