Change audio/video playback to stop playback when out of view (#12486)

Change video player to not loop, since the audio player doesn't

Change playback and mute buttons to feel snappier
This commit is contained in:
Eugen Rochko 2020-01-11 02:02:21 +01:00 committed by GitHub
parent d1f68fb589
commit baa3db3001
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 25 deletions

View file

@ -37,15 +37,14 @@ class Audio extends React.PureComponent {
volume: 0.5, volume: 0.5,
}; };
// hard coded in components.scss // Hard coded in components.scss
// any way to get ::before values programatically? // Any way to get ::before values programatically?
volWidth = 50; volWidth = 50;
volOffset = 70; volOffset = 70;
volHandleOffset = v => { volHandleOffset = v => {
const offset = v * this.volWidth + this.volOffset; const offset = v * this.volWidth + this.volOffset;
return (offset > 110) ? 110 : offset; return (offset > 110) ? 110 : offset;
} }
@ -61,6 +60,8 @@ class Audio extends React.PureComponent {
if (this.waveform) { if (this.waveform) {
this._updateWaveform(); this._updateWaveform();
} }
window.addEventListener('scroll', this.handleScroll);
} }
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
@ -70,6 +71,8 @@ class Audio extends React.PureComponent {
} }
componentWillUnmount () { componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
if (this.wavesurfer) { if (this.wavesurfer) {
this.wavesurfer.destroy(); this.wavesurfer.destroy();
this.wavesurfer = null; this.wavesurfer = null;
@ -128,16 +131,15 @@ class Audio extends React.PureComponent {
this.loaded = true; this.loaded = true;
} }
this.wavesurfer.play(); this.setState({ paused: false }, () => this.wavesurfer.play());
this.setState({ paused: false });
} else { } else {
this.wavesurfer.pause(); this.setState({ paused: true }, () => this.wavesurfer.pause());
this.setState({ paused: true });
} }
} }
toggleMute = () => { toggleMute = () => {
this.wavesurfer.setMute(!this.state.muted); const muted = !this.state.muted;
this.setState({ muted }, () => this.wavesurfer.setMute(muted));
} }
handleVolumeMouseDown = e => { handleVolumeMouseDown = e => {
@ -176,6 +178,19 @@ class Audio extends React.PureComponent {
} }
}, 60); }, 60);
handleScroll = throttle(() => {
if (!this.waveform || !this.wavesurfer) {
return;
}
const { top, height } = this.waveform.getBoundingClientRect();
const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
if (!this.state.paused && !inView) {
this.setState({ paused: true }, () => this.wavesurfer.pause());
}
}, 150, { trailing: true })
render () { render () {
const { height, intl, alt, editable } = this.props; const { height, intl, alt, editable } = this.props;
const { paused, muted, volume, currentTime } = this.state; const { paused, muted, volume, currentTime } = this.state;

View file

@ -124,12 +124,14 @@ class Video extends React.PureComponent {
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'),
}; };
// hard coded in components.scss // Hard-coded in components.scss
// any way to get ::before values programatically? // Any way to get ::before values programatically?
volWidth = 50; volWidth = 50;
volOffset = 70; volOffset = 70;
volHandleOffset = v => { volHandleOffset = v => {
const offset = v * this.volWidth + this.volOffset; const offset = v * this.volWidth + this.volOffset;
return (offset > 110) ? 110 : offset; return (offset > 110) ? 110 : offset;
} }
@ -138,6 +140,7 @@ class Video extends React.PureComponent {
if (c) { if (c) {
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: c.offsetWidth,
}); });
@ -205,12 +208,14 @@ class Video extends React.PureComponent {
const x = (e.clientX - rect.left) / this.volWidth; //x position within the element. const x = (e.clientX - rect.left) / this.volWidth; //x position within the element.
if(!isNaN(x)) { if(!isNaN(x)) {
var slideamt = x; let slideamt = x;
if(x > 1) { if(x > 1) {
slideamt = 1; slideamt = 1;
} else if(x < 0) { } else if(x < 0) {
slideamt = 0; slideamt = 0;
} }
this.video.volume = slideamt; this.video.volume = slideamt;
this.setState({ volume: slideamt }); this.setState({ volume: slideamt });
} }
@ -252,9 +257,9 @@ class Video extends React.PureComponent {
togglePlay = () => { togglePlay = () => {
if (this.state.paused) { if (this.state.paused) {
this.video.play(); this.setState({ paused: false }, () => this.video.play());
} else { } else {
this.video.pause(); this.setState({ paused: true }, () => this.video.pause());
} }
} }
@ -272,12 +277,16 @@ class Video extends React.PureComponent {
document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true); document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true); document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
window.addEventListener('scroll', this.handleScroll);
if (this.props.blurhash) { if (this.props.blurhash) {
this._decode(); this._decode();
} }
} }
componentWillUnmount () { componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
@ -294,6 +303,7 @@ class Video extends React.PureComponent {
if (prevState.revealed && !this.state.revealed && this.video) { if (prevState.revealed && !this.state.revealed && this.video) {
this.video.pause(); this.video.pause();
} }
if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) { if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) {
this._decode(); this._decode();
} }
@ -313,6 +323,19 @@ class Video extends React.PureComponent {
} }
} }
handleScroll = throttle(() => {
if (!this.video) {
return;
}
const { top, height } = this.video.getBoundingClientRect();
const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0);
if (!this.state.paused && !inView) {
this.setState({ paused: true }, () => this.video.pause());
}
}, 150, { trailing: true })
handleFullscreenChange = () => { handleFullscreenChange = () => {
this.setState({ fullscreen: isFullscreen() }); this.setState({ fullscreen: isFullscreen() });
} }
@ -326,8 +349,11 @@ class Video extends React.PureComponent {
} }
toggleMute = () => { toggleMute = () => {
this.video.muted = !this.video.muted; const muted = !this.video.muted;
this.setState({ muted: this.video.muted });
this.setState({ muted }, () => {
this.video.muted = muted;
});
} }
toggleReveal = () => { toggleReveal = () => {
@ -430,7 +456,6 @@ class Video extends React.PureComponent {
src={src} src={src}
poster={preview} poster={preview}
preload={preload} preload={preload}
loop
role='button' role='button'
tabIndex='0' tabIndex='0'
aria-label={alt} aria-label={alt}
@ -495,13 +520,8 @@ class Video extends React.PureComponent {
{(!onCloseVideo && !editable) && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} {(!onCloseVideo && !editable) && <button type='button' aria-label={intl.formatMessage(messages.hide)} onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
{(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>} {(!fullscreen && onOpenVideo) && <button type='button' aria-label={intl.formatMessage(messages.expand)} onClick={this.handleOpenVideo}><Icon id='expand' fixedWidth /></button>}
{onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>} {onCloseVideo && <button type='button' aria-label={intl.formatMessage(messages.close)} onClick={this.handleCloseVideo}><Icon id='compress' fixedWidth /></button>}
<button type='button' aria-label={intl.formatMessage(messages.download)}> <button type='button' aria-label={intl.formatMessage(messages.download)}><a className='video-player__download__icon' href={this.props.src} download><Icon id={'download'} fixedWidth /></a></button>
<a className='video-player__download__icon' href={this.props.src} download>
<Icon id={'download'} fixedWidth />
</a>
</button>
<button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button> <button type='button' aria-label={intl.formatMessage(fullscreen ? messages.exit_fullscreen : messages.fullscreen)} onClick={this.toggleFullscreen}><Icon id={fullscreen ? 'compress' : 'arrows-alt'} fixedWidth /></button>
</div> </div>
</div> </div>
</div> </div>