Add hotkeys for audio/video control (#15158)

Fix #14515
lolsob-rspec
Eugen Rochko 2020-11-15 14:24:54 +01:00 committed by GitHub
parent e3b118cbeb
commit e8facfb2e9
2 changed files with 128 additions and 1 deletions

View File

@ -386,13 +386,59 @@ class Audio extends React.PureComponent {
return this.props.foregroundColor || '#ffffff'; return this.props.foregroundColor || '#ffffff';
} }
seekBy (time) {
const currentTime = this.audio.currentTime + time;
if (!isNaN(currentTime)) {
this.setState({ currentTime }, () => {
this.audio.currentTime = currentTime;
});
}
}
handleAudioKeyDown = e => {
// On the audio element or the seek bar, we can safely use the space bar
// for playback control because there are no buttons to press
if (e.key === ' ') {
e.preventDefault();
e.stopPropagation();
this.togglePlay();
}
}
handleKeyDown = e => {
switch(e.key) {
case 'k':
e.preventDefault();
e.stopPropagation();
this.togglePlay();
break;
case 'm':
e.preventDefault();
e.stopPropagation();
this.toggleMute();
break;
case 'j':
e.preventDefault();
e.stopPropagation();
this.seekBy(-10);
break;
case 'l':
e.preventDefault();
e.stopPropagation();
this.seekBy(10);
break;
}
}
render () { render () {
const { src, intl, alt, editable, autoPlay } = this.props; const { src, intl, alt, editable, autoPlay } = this.props;
const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state; const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100); const progress = Math.min((currentTime / duration) * 100, 100);
return ( return (
<div className={classNames('audio-player', { editable })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <div className={classNames('audio-player', { editable })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex='0' onKeyDown={this.handleKeyDown}>
<audio <audio
src={src} src={src}
ref={this.setAudioRef} ref={this.setAudioRef}
@ -406,12 +452,14 @@ class Audio extends React.PureComponent {
<canvas <canvas
role='button' role='button'
tabIndex='0'
className='audio-player__canvas' className='audio-player__canvas'
width={this.state.width} width={this.state.width}
height={this.state.height} height={this.state.height}
style={{ width: '100%', position: 'absolute', top: 0, left: 0 }} style={{ width: '100%', position: 'absolute', top: 0, left: 0 }}
ref={this.setCanvasRef} ref={this.setCanvasRef}
onClick={this.togglePlay} onClick={this.togglePlay}
onKeyDown={this.handleAudioKeyDown}
title={alt} title={alt}
aria-label={alt} aria-label={alt}
/> />
@ -432,6 +480,7 @@ class Audio extends React.PureComponent {
className={classNames('video-player__seek__handle', { active: dragging })} className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex='0' tabIndex='0'
style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }} style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
onKeyDown={this.handleAudioKeyDown}
/> />
</div> </div>

View File

@ -266,6 +266,81 @@ class Video extends React.PureComponent {
} }
}, 15); }, 15);
seekBy (time) {
const currentTime = this.video.currentTime + time;
if (!isNaN(currentTime)) {
this.setState({ currentTime }, () => {
this.video.currentTime = currentTime;
});
}
}
handleVideoKeyDown = e => {
// On the video element or the seek bar, we can safely use the space bar
// for playback control because there are no buttons to press
if (e.key === ' ') {
e.preventDefault();
e.stopPropagation();
this.togglePlay();
}
}
handleKeyDown = e => {
const frameTime = 1 / 25;
switch(e.key) {
case 'k':
e.preventDefault();
e.stopPropagation();
this.togglePlay();
break;
case 'm':
e.preventDefault();
e.stopPropagation();
this.toggleMute();
break;
case 'f':
e.preventDefault();
e.stopPropagation();
this.toggleFullscreen();
break;
case 'j':
e.preventDefault();
e.stopPropagation();
this.seekBy(-10);
break;
case 'l':
e.preventDefault();
e.stopPropagation();
this.seekBy(10);
break;
case ',':
e.preventDefault();
e.stopPropagation();
this.seekBy(-frameTime);
break;
case '.':
e.preventDefault();
e.stopPropagation();
this.seekBy(frameTime);
break;
}
// If we are in fullscreen mode, we don't want any hotkeys
// interacting with the UI that's not visible
if (this.state.fullscreen) {
e.preventDefault();
e.stopPropagation();
if (e.key === 'Escape') {
exitFullscreen();
}
}
}
togglePlay = () => { togglePlay = () => {
if (this.state.paused) { if (this.state.paused) {
this.setState({ paused: false }, () => this.video.play()); this.setState({ paused: false }, () => this.video.play());
@ -484,6 +559,7 @@ class Video extends React.PureComponent {
onMouseEnter={this.handleMouseEnter} onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave} onMouseLeave={this.handleMouseLeave}
onClick={this.handleClickRoot} onClick={this.handleClickRoot}
onKeyDown={this.handleKeyDown}
tabIndex={0} tabIndex={0}
> >
<Blurhash <Blurhash
@ -507,6 +583,7 @@ class Video extends React.PureComponent {
height={height} height={height}
volume={volume} volume={volume}
onClick={this.togglePlay} onClick={this.togglePlay}
onKeyDown={this.handleVideoKeyDown}
onPlay={this.handlePlay} onPlay={this.handlePlay}
onPause={this.handlePause} onPause={this.handlePause}
onLoadedData={this.handleLoadedData} onLoadedData={this.handleLoadedData}
@ -529,6 +606,7 @@ class Video extends React.PureComponent {
className={classNames('video-player__seek__handle', { active: dragging })} className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex='0' tabIndex='0'
style={{ left: `${progress}%` }} style={{ left: `${progress}%` }}
onKeyDown={this.handleVideoKeyDown}
/> />
</div> </div>