From baa3db3001b576a3d7b1ae5ca8b414416bd4567f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 11 Jan 2020 02:02:21 +0100 Subject: [PATCH] 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 --- .../mastodon/features/audio/index.js | 35 +++++++++---- .../mastodon/features/video/index.js | 50 +++++++++++++------ 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.js index 1b4cdbb4f4..fda5a074fe 100644 --- a/app/javascript/mastodon/features/audio/index.js +++ b/app/javascript/mastodon/features/audio/index.js @@ -37,15 +37,14 @@ class Audio extends React.PureComponent { volume: 0.5, }; - // hard coded in components.scss - // any way to get ::before values programatically? - - volWidth = 50; - + // 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; } @@ -61,6 +60,8 @@ class Audio extends React.PureComponent { if (this.waveform) { this._updateWaveform(); } + + window.addEventListener('scroll', this.handleScroll); } componentDidUpdate (prevProps) { @@ -70,6 +71,8 @@ class Audio extends React.PureComponent { } componentWillUnmount () { + window.removeEventListener('scroll', this.handleScroll); + if (this.wavesurfer) { this.wavesurfer.destroy(); this.wavesurfer = null; @@ -128,16 +131,15 @@ class Audio extends React.PureComponent { this.loaded = true; } - this.wavesurfer.play(); - this.setState({ paused: false }); + this.setState({ paused: false }, () => this.wavesurfer.play()); } else { - this.wavesurfer.pause(); - this.setState({ paused: true }); + this.setState({ paused: true }, () => this.wavesurfer.pause()); } } toggleMute = () => { - this.wavesurfer.setMute(!this.state.muted); + const muted = !this.state.muted; + this.setState({ muted }, () => this.wavesurfer.setMute(muted)); } handleVolumeMouseDown = e => { @@ -176,6 +178,19 @@ class Audio extends React.PureComponent { } }, 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 () { const { height, intl, alt, editable } = this.props; const { paused, muted, volume, currentTime } = this.state; diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.js index f6aeb8c9a9..0e2a994160 100644 --- a/app/javascript/mastodon/features/video/index.js +++ b/app/javascript/mastodon/features/video/index.js @@ -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'), }; - // hard coded in components.scss - // any way to get ::before values programatically? - volWidth = 50; + // 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; } @@ -138,6 +140,7 @@ class Video extends React.PureComponent { if (c) { if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth); + this.setState({ 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. if(!isNaN(x)) { - var slideamt = x; + let slideamt = x; + if(x > 1) { slideamt = 1; } else if(x < 0) { slideamt = 0; } + this.video.volume = slideamt; this.setState({ volume: slideamt }); } @@ -252,9 +257,9 @@ class Video extends React.PureComponent { togglePlay = () => { if (this.state.paused) { - this.video.play(); + this.setState({ paused: false }, () => this.video.play()); } 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('MSFullscreenChange', this.handleFullscreenChange, true); + window.addEventListener('scroll', this.handleScroll); + if (this.props.blurhash) { this._decode(); } } componentWillUnmount () { + window.removeEventListener('scroll', this.handleScroll); + document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('webkitfullscreenchange', 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) { this.video.pause(); } + if (prevProps.blurhash !== this.props.blurhash && this.props.blurhash) { 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 = () => { this.setState({ fullscreen: isFullscreen() }); } @@ -326,8 +349,11 @@ class Video extends React.PureComponent { } toggleMute = () => { - this.video.muted = !this.video.muted; - this.setState({ muted: this.video.muted }); + const muted = !this.video.muted; + + this.setState({ muted }, () => { + this.video.muted = muted; + }); } toggleReveal = () => { @@ -430,7 +456,6 @@ class Video extends React.PureComponent { src={src} poster={preview} preload={preload} - loop role='button' tabIndex='0' aria-label={alt} @@ -495,13 +520,8 @@ class Video extends React.PureComponent { {(!onCloseVideo && !editable) && } {(!fullscreen && onOpenVideo) && } {onCloseVideo && } - + -