feat: add orientation lock and dual-instance fullscreen to VideoPlayer

This commit is contained in:
Developer
2026-03-10 22:52:05 +08:00
parent 85ceec8b58
commit 6871d061fc

View File

@@ -1,10 +1,8 @@
import React, { useState } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { View, StyleSheet, Dimensions, Text, Platform, Modal, StatusBar } from 'react-native'; import { View, StyleSheet, Text, Platform, Modal, StatusBar, useWindowDimensions } from 'react-native';
import * as ScreenOrientation from 'expo-screen-orientation';
import { NativeVideoPlayer } from './NativeVideoPlayer'; import { NativeVideoPlayer } from './NativeVideoPlayer';
import type { PlayUrlResponse } from '../services/types'; import type { PlayUrlResponse, DanmakuItem } from '../services/types';
const { width } = Dimensions.get('window');
const VIDEO_HEIGHT = width * 0.5625;
interface Props { interface Props {
playData: PlayUrlResponse | null; playData: PlayUrlResponse | null;
@@ -14,14 +12,38 @@ interface Props {
onMiniPlayer?: () => void; onMiniPlayer?: () => void;
bvid?: string; bvid?: string;
cid?: number; cid?: number;
danmakus?: DanmakuItem[];
onTimeUpdate?: (t: number) => void;
} }
export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, onMiniPlayer, bvid, cid }: Props) { export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, onMiniPlayer, bvid, cid, danmakus, onTimeUpdate }: Props) {
const [fullscreen, setFullscreen] = useState(false); const [fullscreen, setFullscreen] = useState(false);
const { width } = useWindowDimensions();
const VIDEO_HEIGHT = width * 0.5625;
const lastTimeRef = useRef(0);
const handleEnterFullscreen = async () => {
setFullscreen(true);
if (Platform.OS !== 'web')
await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE_RIGHT);
};
const handleExitFullscreen = async () => {
setFullscreen(false);
if (Platform.OS !== 'web')
await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
};
useEffect(() => {
return () => {
if (Platform.OS !== 'web')
ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
};
}, []);
if (!playData) { if (!playData) {
return ( return (
<View style={[styles.container, styles.placeholder]}> <View style={[{ width, height: VIDEO_HEIGHT, backgroundColor: '#000' }, styles.placeholder]}>
<Text style={styles.placeholderText}>...</Text> <Text style={styles.placeholderText}>...</Text>
</View> </View>
); );
@@ -30,7 +52,7 @@ export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, o
if (Platform.OS === 'web') { if (Platform.OS === 'web') {
const url = playData.durl?.[0]?.url ?? ''; const url = playData.durl?.[0]?.url ?? '';
return ( return (
<View style={styles.container}> <View style={{ width, height: VIDEO_HEIGHT, backgroundColor: '#000' }}>
<video <video
src={url} src={url}
style={{ width: '100%', height: '100%', backgroundColor: '#000' } as any} style={{ width: '100%', height: '100%', backgroundColor: '#000' } as any}
@@ -43,29 +65,37 @@ export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, o
return ( return (
<> <>
<NativeVideoPlayer {/* Portrait player: unmount when fullscreen */}
playData={playData} {!fullscreen && (
qualities={qualities} <NativeVideoPlayer
currentQn={currentQn} playData={playData}
onQualityChange={onQualityChange} qualities={qualities}
onFullscreen={() => setFullscreen(true)} currentQn={currentQn}
onMiniPlayer={onMiniPlayer} onQualityChange={onQualityChange}
bvid={bvid} onFullscreen={handleEnterFullscreen}
cid={cid} onMiniPlayer={onMiniPlayer}
/> bvid={bvid}
cid={cid}
isFullscreen={false}
onTimeUpdate={(t) => { lastTimeRef.current = t; onTimeUpdate?.(t); }}
/>
)}
<Modal visible={fullscreen} animationType="fade" statusBarTranslucent> <Modal visible={fullscreen} animationType="fade" statusBarTranslucent>
<StatusBar hidden /> <StatusBar hidden />
<View style={styles.fullscreenContainer}> <View style={{ flex: 1, backgroundColor: '#000' }}>
<NativeVideoPlayer <NativeVideoPlayer
playData={playData} playData={playData}
qualities={qualities} qualities={qualities}
currentQn={currentQn} currentQn={currentQn}
onQualityChange={onQualityChange} onQualityChange={onQualityChange}
onFullscreen={() => setFullscreen(false)} onFullscreen={handleExitFullscreen}
bvid={bvid} bvid={bvid}
cid={cid} cid={cid}
style={{ width: '100%', height: '100%' } as any} danmakus={danmakus}
isFullscreen={true}
initialTime={lastTimeRef.current}
onTimeUpdate={(t) => { lastTimeRef.current = t; onTimeUpdate?.(t); }}
/> />
</View> </View>
</Modal> </Modal>
@@ -74,8 +104,6 @@ export function VideoPlayer({ playData, qualities, currentQn, onQualityChange, o
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { width, height: VIDEO_HEIGHT, backgroundColor: '#000' },
placeholder: { justifyContent: 'center', alignItems: 'center' }, placeholder: { justifyContent: 'center', alignItems: 'center' },
placeholderText: { color: '#fff', fontSize: 14 }, placeholderText: { color: '#fff', fontSize: 14 },
fullscreenContainer: { flex: 1, backgroundColor: '#000' },
}); });