React Native - 在导航时暂停视频
React Native - Pause Video on Navigation
在我的 react-native 项目中,我将 react-navigation 5 用于导航,将 react-native-video 用于 audio/video 播放器。
我的要求是,当用户导航到另一个屏幕时,如果 audio/video 应该停止播放。然而,这并没有发生,音频继续播放。
我在堆栈导航器中创建了两个屏幕。视频播放器是一个单独的组件。
屏幕代码:
function MainScreen({ navigation }) {
const [audiostatus, setAudioStatus] = useState(true);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('Leaving Home Screen');
setAudioStatus(true);
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
<Player tracks={TRACKS} paused={audiostatus} />
<Button
title="Go to Screen Without Audio"
onPress={() => navigation.navigate('No Audio Screen')}
/>
<Button
title="Go to Screen With Another Audio (Love Yourself)"
onPress={() => navigation.navigate('Another Audio Screen')}
/>
</View>
);
}
玩家代码
在播放器中,我收到 paused 属性来决定视频是应该已经播放还是暂停。然后播放器通过改变状态来控制播放。
export default class Player extends Component {
constructor(props) {
super(props);
this.state = {
paused: props.paused,
totalLength: 1,
currentPosition: 0,
selectedTrack: 0,
repeatOn: false,
shuffleOn: false,
};
}
setDuration(data) {
this.setState({totalLength: Math.floor(data.duration)});
}
setTime(data) {
this.setState({currentPosition: Math.floor(data.currentTime)});
}
seek(time) {
time = Math.round(time);
this.refs.audioElement && this.refs.audioElement.seek(time);
this.setState({
currentPosition: time,
paused: false,
});
}
render() {
const track = this.props.tracks[this.state.selectedTrack];
const video = this.state.isChanging ? null : (
<Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
ref="audioElement"
paused={this.state.paused} // Pauses playback entirely.
resizeMode="cover" // Fill the whole screen at aspect ratio.
repeat={false} // Repeat forever.
onLoadStart={this.loadStart} // Callback when video starts to load
onLoad={this.setDuration.bind(this)} // Callback when video loads
onProgress={this.setTime.bind(this)} // Callback every ~250ms with currentTime
onEnd={this.onEnd}
onError={this.videoError}
style={styles.audioElement}
audioOnly={true} />
);
return (
<View style={styles.container}>
<SeekBar
onSeek={this.seek.bind(this)}
trackLength={this.state.totalLength}
onSlidingStart={() => this.setState({paused: true})}
currentPosition={this.state.currentPosition} />
<Controls
onPressPlay={() => this.setState({paused: false})}
onPressPause={() => this.setState({paused: true})}
paused={this.state.paused}/>
{video}
</View>
);
}
}
问题是,一旦用户开始播放视频,然后如果他导航到另一个屏幕,视频就会继续播放。我想让视频暂停。在屏幕上,我添加了 useEffect() 来设置 audiostatus 在屏幕模糊时暂停,但没有任何反应。视频一直在播放。请帮忙
您的 Player
似乎只在 paused
道具挂载时在构造函数中引用一次。 Player
在父组件中更改并在挂载后传递时,不会对 props.paused
的任何更改做出反应或处理。实施 componentDidUpdate
以响应对 props.paused
的更新以更新组件状态。
export default class Player extends Component {
constructor(props) {
super(props);
this.state = {
paused: props.paused,
totalLength: 1,
currentPosition: 0,
selectedTrack: 0,
repeatOn: false,
shuffleOn: false,
};
}
...
componentDidUpdate(prevProps, prevState) {
const { paused } = this.props;
if (!prevState.paused && paused) {
this.setState({ paused });
}
}
...
render() {
...
const video = this.state.isChanging ? null : (
<Video
...
paused={this.state.paused}
...
/>
);
return (
<View style={styles.container}>
...
{video}
</View>
);
}
}
按照以下方式暂停视频
import React, {useState, useRef} from 'react';
function MainScreen({ navigation }) {
const [audiostatus, setAudioStatus] = useState(true);
// create ref
const playerRef = useRef();
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('Leaving Home Screen');
setAudioStatus(false);
// new code add to pause video from ref
playerRef.current.pauseVideo();
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
<Player ... playerRef={playerRef} />
</View>
);
}
像我一样将 Player class 转换为 Hooks
import React, {useState, useImperativeHandle, useRef} from 'react';
function Player = (props) => {
const [paused, setPaused] = useState(props.paused);
const [totalLength, setTotalLength] = useState(1);
const [currentPosition, setCurrentPosition] = useState(0);
const [selectedTrack, setSelectedTrack] = useState(0);
const [repeatOn, setRepeatOn] = useState(false);
const [shuffleOn, setShuffleOn] = useState(false);
const [isChanging, setIsChanging] = useState(false);
const audioElement = useRef(null);
const setDuration = (data) => {
setTotalLength(Math.floor(data.duration));
}
const setTime = (data) => {
setCurrentPosition(Math.floor(data.currentTime));
}
const seek = (time) => {
time = Math.round(time);
audioElement && audioElement.current.seek(time);
setCurrentPosition(time);
setPaused(false);
}
const loadStart = () => {}
// add for accessing ref
useImperativeHandle(props.playerRef, () => ({
pauseVideo: () => setPaused(true),
}));
const track = props.tracks[selectedTrack];
const video = isChanging ? null : (
<Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
ref={audioElement}
paused={paused} // Pauses playback entirely.
resizeMode="cover"
....
onLoadStart={loadStart} // new added
onLoad={setDuration} // new added
/>
);
return (
<View style={styles.container}>
<SeekBar
onSeek={seek}
trackLength={totalLength}
onSlidingStart={() => setPaused(true)}
currentPosition={currentPosition} />
<Controls
onPressPlay={() => setPaused(false) }
onPressPause={() => setPaused(true)}
paused={paused}/>
{video}
</View>
);
}
一个带有功能组件和挂钩的简单解决方案是使用
useIsFocused
which returns true or false 并在更改时重新渲染组件 import it using
import { useIsFocused } from '@react-navigation/native';
const screenIsFocused = useIsFocused();
如果您使用的是“react-native-video”或任何其他需要类似内容的库
isPaused
你可以使用
paused={isPaused || (!screenIsFocused )}
视频只有在未暂停且屏幕也处于对焦状态时才会 运行
在我的 react-native 项目中,我将 react-navigation 5 用于导航,将 react-native-video 用于 audio/video 播放器。
我的要求是,当用户导航到另一个屏幕时,如果 audio/video 应该停止播放。然而,这并没有发生,音频继续播放。
我在堆栈导航器中创建了两个屏幕。视频播放器是一个单独的组件。
屏幕代码:
function MainScreen({ navigation }) {
const [audiostatus, setAudioStatus] = useState(true);
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('Leaving Home Screen');
setAudioStatus(true);
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
<Player tracks={TRACKS} paused={audiostatus} />
<Button
title="Go to Screen Without Audio"
onPress={() => navigation.navigate('No Audio Screen')}
/>
<Button
title="Go to Screen With Another Audio (Love Yourself)"
onPress={() => navigation.navigate('Another Audio Screen')}
/>
</View>
);
}
玩家代码 在播放器中,我收到 paused 属性来决定视频是应该已经播放还是暂停。然后播放器通过改变状态来控制播放。
export default class Player extends Component {
constructor(props) {
super(props);
this.state = {
paused: props.paused,
totalLength: 1,
currentPosition: 0,
selectedTrack: 0,
repeatOn: false,
shuffleOn: false,
};
}
setDuration(data) {
this.setState({totalLength: Math.floor(data.duration)});
}
setTime(data) {
this.setState({currentPosition: Math.floor(data.currentTime)});
}
seek(time) {
time = Math.round(time);
this.refs.audioElement && this.refs.audioElement.seek(time);
this.setState({
currentPosition: time,
paused: false,
});
}
render() {
const track = this.props.tracks[this.state.selectedTrack];
const video = this.state.isChanging ? null : (
<Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
ref="audioElement"
paused={this.state.paused} // Pauses playback entirely.
resizeMode="cover" // Fill the whole screen at aspect ratio.
repeat={false} // Repeat forever.
onLoadStart={this.loadStart} // Callback when video starts to load
onLoad={this.setDuration.bind(this)} // Callback when video loads
onProgress={this.setTime.bind(this)} // Callback every ~250ms with currentTime
onEnd={this.onEnd}
onError={this.videoError}
style={styles.audioElement}
audioOnly={true} />
);
return (
<View style={styles.container}>
<SeekBar
onSeek={this.seek.bind(this)}
trackLength={this.state.totalLength}
onSlidingStart={() => this.setState({paused: true})}
currentPosition={this.state.currentPosition} />
<Controls
onPressPlay={() => this.setState({paused: false})}
onPressPause={() => this.setState({paused: true})}
paused={this.state.paused}/>
{video}
</View>
);
}
}
问题是,一旦用户开始播放视频,然后如果他导航到另一个屏幕,视频就会继续播放。我想让视频暂停。在屏幕上,我添加了 useEffect() 来设置 audiostatus 在屏幕模糊时暂停,但没有任何反应。视频一直在播放。请帮忙
您的 Player
似乎只在 paused
道具挂载时在构造函数中引用一次。 Player
在父组件中更改并在挂载后传递时,不会对 props.paused
的任何更改做出反应或处理。实施 componentDidUpdate
以响应对 props.paused
的更新以更新组件状态。
export default class Player extends Component {
constructor(props) {
super(props);
this.state = {
paused: props.paused,
totalLength: 1,
currentPosition: 0,
selectedTrack: 0,
repeatOn: false,
shuffleOn: false,
};
}
...
componentDidUpdate(prevProps, prevState) {
const { paused } = this.props;
if (!prevState.paused && paused) {
this.setState({ paused });
}
}
...
render() {
...
const video = this.state.isChanging ? null : (
<Video
...
paused={this.state.paused}
...
/>
);
return (
<View style={styles.container}>
...
{video}
</View>
);
}
}
按照以下方式暂停视频
import React, {useState, useRef} from 'react';
function MainScreen({ navigation }) {
const [audiostatus, setAudioStatus] = useState(true);
// create ref
const playerRef = useRef();
React.useEffect(() => {
const unsubscribe = navigation.addListener('blur', () => {
console.log('Leaving Home Screen');
setAudioStatus(false);
// new code add to pause video from ref
playerRef.current.pauseVideo();
});
return unsubscribe;
}, [navigation]);
return (
<View style={{ flex: 1, justifyContent: 'center',backgroundColor: '#fff' }}>
<Player ... playerRef={playerRef} />
</View>
);
}
像我一样将 Player class 转换为 Hooks
import React, {useState, useImperativeHandle, useRef} from 'react';
function Player = (props) => {
const [paused, setPaused] = useState(props.paused);
const [totalLength, setTotalLength] = useState(1);
const [currentPosition, setCurrentPosition] = useState(0);
const [selectedTrack, setSelectedTrack] = useState(0);
const [repeatOn, setRepeatOn] = useState(false);
const [shuffleOn, setShuffleOn] = useState(false);
const [isChanging, setIsChanging] = useState(false);
const audioElement = useRef(null);
const setDuration = (data) => {
setTotalLength(Math.floor(data.duration));
}
const setTime = (data) => {
setCurrentPosition(Math.floor(data.currentTime));
}
const seek = (time) => {
time = Math.round(time);
audioElement && audioElement.current.seek(time);
setCurrentPosition(time);
setPaused(false);
}
const loadStart = () => {}
// add for accessing ref
useImperativeHandle(props.playerRef, () => ({
pauseVideo: () => setPaused(true),
}));
const track = props.tracks[selectedTrack];
const video = isChanging ? null : (
<Video source={{uri: track.audioUrl}} // Can be a URL or a local file.
ref={audioElement}
paused={paused} // Pauses playback entirely.
resizeMode="cover"
....
onLoadStart={loadStart} // new added
onLoad={setDuration} // new added
/>
);
return (
<View style={styles.container}>
<SeekBar
onSeek={seek}
trackLength={totalLength}
onSlidingStart={() => setPaused(true)}
currentPosition={currentPosition} />
<Controls
onPressPlay={() => setPaused(false) }
onPressPause={() => setPaused(true)}
paused={paused}/>
{video}
</View>
);
}
一个带有功能组件和挂钩的简单解决方案是使用
useIsFocused
which returns true or false 并在更改时重新渲染组件 import it using
import { useIsFocused } from '@react-navigation/native';
const screenIsFocused = useIsFocused();
如果您使用的是“react-native-video”或任何其他需要类似内容的库
isPaused
你可以使用
paused={isPaused || (!screenIsFocused )}
视频只有在未暂停且屏幕也处于对焦状态时才会 运行