mirror of
https://github.com/kikobar/mastodon.git
synced 2024-12-28 08:44:16 +00:00
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:
parent
d1f68fb589
commit
baa3db3001
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue