
下方的播放/暫停按鈕、進度條、音量按鈕都是自定義的。
expo-video 有提供一個 useVideoPlayer
hook 來建立 VideoPlayer 實例,可以管理播放器的生命週期,並確保在組件卸載時正確銷毀播放器。(如果需要在元件卸載時不會自動銷毀的 VideoPlayer 可以使用 createVideoPlayer 函數來建立 VideoPlayer)
const player = useVideoPlayer(uri, player => {
player.loop = true
player.play()
})
控制靜音
player.muted
const toggleMute = () => {
player.muted = !isMuted
setIsMuted(!isMuted)
}
控制播放和暫停
- 播放:
player.play()
- 暫停:
player.pause()
const togglePlay = () => {
if (isPlaying) {
player.pause()
} else {
player.play()
}
setIsPlaying(!isPlaying)
}
播放進度條
Slider
Slider 使用的是 @react-native-community/slider
<Slider
style={{ width: 200 }}
minimumValue={0}
maximumValue={duration}
value={currentTime}
minimumTrackTintColor="#fff"
maximumTrackTintColor="#2E2E2E"
thumbTintColor="transparent"
/>
進度控制
timeUpdateEventInterval
表示播放器觸發 timeUpdate 事件的間隔(以秒為單位),值為 0 時不會觸發該事件,預設是 0。updateTime
函數用於獲取當前影片播放時間並更新 state- expo-video 有自帶Controls,要關閉需要設置
nativeControls={false}
- Slider 需使用
onValueChange
及onSlidingComplete
去更新當前播放時間onValueChange
:拖動滑塊時觸發,只更新滑塊在UI上的位置onSlidingComplete
:鬆開滑塊時觸發,更新 player.currentTime
const player = useVideoPlayer(uri, player => {
player.loop = true
player.play()
player.timeUpdateEventInterval = 250
})
const [isPlaying, setIsPlaying] = useState(false)
const [isMuted, setIsMuted] = useState(false)
const [currentTime, setCurrentTime] = useState(0)
const [duration, setDuration] = useState(0)
useEffect(() => {
let animationFrameId: number
const updateTime = () => {
if (player) {
const newTime = player.currentTime
setCurrentTime(newTime)
if (typeof player.duration === 'number') {
setDuration(player.duration)
}
if (newTime >= player.duration) {
player.currentTime = 0
player.play()
}
}
animationFrameId = requestAnimationFrame(updateTime)
}
animationFrameId = requestAnimationFrame(updateTime)
return () => cancelAnimationFrame(animationFrameId)
}, [player])
useEffect(() => {
if (player) {
setIsPlaying(player.playing)
}
}, [player?.playing])
const togglePlay = () => {
if (isPlaying) {
player.pause()
} else {
player.play()
}
setIsPlaying(!isPlaying)
}
const toggleMute = () => {
player.muted = !isMuted
setIsMuted(!isMuted)
}
return (
<View className={"w-full h-full bg-black justify-center items-center relative"} style={style}>
<VideoView
player={player}
style={{ width: '100%', height: '100%', position: 'absolute', top: 0, left: 0 }}
nativeControls={false}
allowsFullscreen={false}
allowsPictureInPicture={false}
/>
{/* Controls */}
<View className="absolute left-0 right-0 bottom-16 pb-4 px-4 bg-transparent">
<View className="flex-row items-center justify-center gap-2 mb-1">
<Pressable className="flex-1 items-end" onPress={togglePlay}>
<FontAwesomeIcon name={isPlaying ? 'pause' : 'play'} size={28} color="#fff" />
</Pressable>
<Slider
style={{ width: width - 100 }}
minimumValue={0}
maximumValue={duration}
value={currentTime}
minimumTrackTintColor="#fff"
maximumTrackTintColor="#2E2E2E"
thumbTintColor="transparent"
onValueChange={val => setCurrentTime(val)}
onSlidingComplete={val => {
player.currentTime = val
if (isPlaying) {
player.play()
}
}}
/>
<Pressable className="flex-1 items-start" onPress={toggleMute}>
<FontAwesomeIcon name={isMuted ? 'volume-xmark' : 'volume-low'} size={24} color="#fff" />
</Pressable>
</View>
</View>
</View>
)