【发布时间】:2022-01-03 03:01:30
【问题描述】:
我有一个使用 react-native-video-controls 的视频组件,除了一件事之外一切都很好:
我用来跳过/查找和播放/暂停的自定义按钮不会重置 controlTimeout 值(上次交互与显示/隐藏视频控件的 hideControls 操作之间的延迟。从这个意义上说,无论如何多次按下我的按钮,超时只会倒计时并消失,而不是在每次按下时重置。
我应该能够连续按下按钮以无限期地保持控件开启(只要计时器没有达到限制)。如何重置 controlTimeout 值?
我的视频组件:
import React, { useEffect, useRef, useState } from 'react';
import { VideoProperties } from 'react-native-video';
import Video from 'react-native-video-controls';
import { Animated, StyleSheet, Text, View } from 'react-native';
import { Image } from 'react-native-elements';
import { colors } from '../styles/colorPalette';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { useTheme } from '../contexts/ThemeContext';
import { ReactNativeProps } from 'react-native-render-html';
import { useIsFocused } from '@react-navigation/native';
interface VideoPlayerProps extends VideoProperties {
autoPlay?: boolean
categoryOverlay?: boolean | string
disableSeekSkip?: boolean
ref?: any
}
const VideoPlayer = (props: VideoPlayerProps & ReactNativeProps) => {
const [vidAspectRatio, setVidAspectRatio] = useState(1.75)
const [duration, setDuration] = useState(null);
const [error, setError] = useState(null)
const [lastTouched, setLastTouched] = useState(0)
const [progress, setProgress] = useState(1);
const [isPlaying, setIsPlaying] = useState(!props.paused || false);
const [controlsActive, setControlsActive] = useState(false);
const { darkMode } = useTheme();
const { categoryOverlay, disableSeekSkip = false, source } = props;
const isFocused = useIsFocused();
const videoRef = useRef<Video>(null);
const handleError = (e: any) => {
console.log("ERROR: ", e)
}
const handleSeek = (num: number) => {
if (!videoRef.current || videoRef.current.state.seeking === true || (Date.now() - lastTouched < 250)) {
return
} else {
videoRef.current.player.ref.seek(Math.max(0, Math.min((videoRef.current.state.currentTime + num), videoRef.current.state.duration)))
setLastTouched(Date.now())
}
}
const handleHeight = (res: any) => {
// set height and duration
duration && setDuration(res.duration ?? null);
setVidAspectRatio(res.naturalSize ? (res.naturalSize.width / res.naturalSize.height) : 1.75);
}
const handlePause = (res: any) => {
// The logic to handle the pause/play logic
res.playbackRate === 0 ? setIsPlaying(false) : setIsPlaying(true);
}
const handlePlayPausePress = () => {
isPlaying ? setIsPlaying(false) : setIsPlaying(true)
}
const handleProgress = (progress: any) => {
setProgress(progress.atValue);
}
const handleSetControlsActive = (active: boolean) => {
setControlsActive(active)
}
const styles = StyleSheet.create({
container: {
position: 'relative',
resizeMode: 'cover',
aspectRatio: vidAspectRatio ? vidAspectRatio : 1.75,
justifyContent: 'center',
alignItems: 'center',
},
controls: {
},
controlsImage: {
resizeMode: 'contain',
width: '100%',
},
playIcon: {
color: darkMode ? colors.primary.purple4 : "#fff",
fontSize: 30,
marginHorizontal: 30,
},
playIconContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
paddingHorizontal: 15,
paddingVertical: 7.5,
borderRadius: 10,
},
video: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
},
videoPlayer: {
position: 'absolute',
height: '100%',
width: '100%',
},
videoPoster: {
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
resizeMode: 'cover',
},
videoWrapper: {
position: 'absolute',
width: '100%',
height: '100%',
},
volumeOverlay: {
position: 'absolute',
top: 0,
right: 0,
},
categoryOverlay: {
paddingHorizontal: 10,
paddingVertical: 5,
position: 'absolute',
color: '#fff',
bottom: 10,
right: 10,
backgroundColor: 'rgba(0,0,0, .75)',
borderRadius: 10,
zIndex: 999,
textTransform: 'uppercase',
},
});
const convertTime = (seconds: number) => {
const secsRemaining = Math.floor(seconds % 60);
return `${Math.floor(seconds / 60)}:${secsRemaining < 10 ? '0' + secsRemaining : secsRemaining}`
}
const convertTimeV2 = (secs: number) => {
var hours = Math.floor(secs / 3600)
var minutes = Math.floor(secs / 60) % 60
var seconds = Math.floor(secs % 60)
return [hours,minutes,seconds]
.map(v => v < 10 ? "0" + v : v)
.filter((v,i) => v !== "00" || i > 0)
.join(":")
}
useEffect(() => {
if (error) console.log("ERROR", error)
}, [error])
return (
<Animated.View style={styles.container}>
{ (controlsActive || !isPlaying) &&
<View style={styles.volumeOverlay}>
<Image containerStyle={{height: 60, width: 60}} source={videoRef.current.state.muted ? require('../assets/icons/Miscellaneous/Video_Controls/volume-muted.png') : require('../assets/icons/Miscellaneous/Video_Controls/volume-on.png') }/>
</View>
}
<View style={styles.videoWrapper}>
<Video
ref={videoRef}
// controls={Platform.OS === 'ios' ? true : false} /* Added Platform flag to circumvent iOS control bug for now */
{...props}
source={source}
showOnStart
disableBack
disableFullscreen
disablePlayPause
disableSeekbar={disableSeekSkip}
disableTimer={disableSeekSkip}
paused={(!isPlaying || !isFocused) || props.paused}
onLoad={handleHeight}
onError={handleError}
onHideControls={() => handleSetControlsActive(false)}
onShowControls={() => handleSetControlsActive(true)}
onPlaybackRateChange={handlePause}
onProgress={handleProgress}
seekColor="#a146b7"
controlTimeout={3000}
style={{flex: 1, flexGrow: 1}}
containerStyle={{flex: 1, flexGrow: 1}}
/>
</View>
{categoryOverlay && progress === 1 &&
<View style={styles.categoryOverlay}>
<Text style={{color: "#fff", textTransform: 'uppercase'}}>{(typeof categoryOverlay === 'boolean') && duration ? convertTime(duration) : categoryOverlay}</Text>
</View>
}
{ (progress === 1 && !isPlaying) && <View style={styles.videoPoster}><Image style={{width: '100%', height: '100%', resizeMode: 'contain'}} source={{ uri: `https://home.test.com${props.poster}` }} /></View> }
{ (controlsActive || !isPlaying) &&
<View style={styles.playIconContainer}>
{ !disableSeekSkip && <TouchableOpacity disabled={videoRef.current.state.currentTime == 0 || videoRef.current.state.seeking} onPress={() => handleSeek(-15)}>
<Image containerStyle={{height: 60, width: 60}} style={styles.controlsImage} source={require('../assets/icons/Miscellaneous/Video_Controls/back-15s.png')}/>
</TouchableOpacity> }
<TouchableOpacity onPress={handlePlayPausePress}>
<Image containerStyle={{height: 60, width: 60}} source={isPlaying ? require('../assets/icons/Miscellaneous/Video_Controls/pause-video-white.png') : require('../assets/icons/Miscellaneous/Video_Controls/play-video-white.png')}/>
</TouchableOpacity>
{ !disableSeekSkip && <TouchableOpacity disabled={videoRef.current.state.currentTime == videoRef.current.state.duration || videoRef.current.state.seeking} onPress={() => handleSeek(15)}>
<Image containerStyle={{height: 60, width: 60}} style={styles.controlsImage} source={require('../assets/icons/Miscellaneous/Video_Controls/skip-15s.png')}/>
</TouchableOpacity> }
</View> }
</Animated.View>
);
}
export default React.memo(VideoPlayer)
【问题讨论】:
-
您能否提供一些代码,以便任何人都能更好地理解您的问题。
-
我认为这更像是一个一般性的“你知道吗”问题 - 但如果有帮助,我已经添加了代码。 videoRef(ref)中有一个名为 player.controlTimeout 的道具,它增加一个计时器,如果超过阈值则清除控件。当我的自定义按钮被按下时,它们不会重置计时器,因此即使在点击后控件仍会关闭。
标签: react-native react-native-video