react-native-expo-video-player 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/types.ts ADDED
@@ -0,0 +1,89 @@
1
+ import type { VideoView } from 'expo-video';
2
+
3
+ /**
4
+ * 从 VideoView 组件中提取 props 类型。
5
+ * 用于透传所有原生 VideoView 属性。
6
+ */
7
+ type VideoViewProps = React.ComponentProps<typeof VideoView>;
8
+
9
+ export interface VideoPlayerTheme {
10
+ /** 主题色,用于滑块、选中状态等 */
11
+ primary: string;
12
+ /** 文字颜色 */
13
+ text?: string;
14
+ /** 次要文字颜色 */
15
+ textSecondary?: string;
16
+ }
17
+
18
+ /**
19
+ * ExpoVideoPlayer 组件的 Props。
20
+ *
21
+ * 继承了 expo-video 的所有原生 `VideoView` 属性(如 `contentFit`、
22
+ * `allowsPictureInPicture`、`surfaceType`、`onFirstFrameRender` 等)。
23
+ *
24
+ * 内部管理的属性(`player`、`nativeControls`、`style`)已排除,
25
+ * 请使用本接口中的专用属性代替。
26
+ */
27
+ export interface ExpoVideoPlayerProps
28
+ extends Omit<VideoViewProps, 'player' | 'nativeControls' | 'style'> {
29
+ /** expo-video 的 VideoPlayer 实例,通过 useVideoPlayer() 创建 */
30
+ player: import('expo-video').VideoPlayer;
31
+ /** 播放器 UI 主题色 */
32
+ theme?: VideoPlayerTheme;
33
+ /** 是否当前处于全屏模式 */
34
+ isFullscreen?: boolean;
35
+ /** 全屏切换回调 */
36
+ onToggleFullscreen?: () => void;
37
+ /** 返回按钮回调 */
38
+ onBack?: () => void;
39
+ /** 倍速选项,默认 [0.5, 0.75, 1, 1.25, 1.5, 2] */
40
+ speedOptions?: number[];
41
+ /** 1x 速度时显示的文字,默认 "倍速" */
42
+ speedLabel?: string;
43
+ /** 控制栏自动隐藏超时(毫秒),默认 3000 */
44
+ autoHideTimeout?: number;
45
+ /** 是否启用全屏手势(音量/亮度/快进),默认 true */
46
+ enableGestures?: boolean;
47
+ /** 是否显示倍速按钮,默认 true */
48
+ showSpeedPicker?: boolean;
49
+ /** 是否显示全屏按钮,默认 true */
50
+ showFullscreenButton?: boolean;
51
+ /** 是否显示画质选择按钮,默认 false */
52
+ showQualityPicker?: boolean;
53
+ /** 画质选择标签(按钮上"自动"时显示的文字),默认 "画质" */
54
+ qualityLabel?: string;
55
+ /**
56
+ * 画质选项列表,格式为 { label: string; url: string }[]。
57
+ * 示例: [{ label: '1080p', url: 'https://...' }, { label: '720p', url: 'https://...' }]
58
+ * 会自动在最前面添加"自动"选项(使用第一个 URL)。
59
+ * 不传或为空数组时画质按钮不会显示。
60
+ */
61
+ qualityOptions?: Array<{ label: string; url: string }>;
62
+ /** 画质切换回调,返回选中的画质标签和 URL,null 表示选择了"自动" */
63
+ onQualityChange?: (quality: { label: string; url: string } | null) => void;
64
+ /** 自定义滑块拇指图片(require() 结果),iOS 专用 */
65
+ thumbImage?: any;
66
+ /** 视频容器宽度(非全屏),默认窗口宽度 */
67
+ width?: number;
68
+ /** 视频容器高度(非全屏),默认 width * 9/16 */
69
+ height?: number;
70
+ }
71
+
72
+ export interface VideoControlsProps {
73
+ player: import('expo-video').VideoPlayer;
74
+ theme: VideoPlayerTheme;
75
+ isFullscreen: boolean;
76
+ onToggleFullscreen: () => void;
77
+ onBack: () => void;
78
+ speedOptions: number[];
79
+ speedLabel: string;
80
+ autoHideTimeout: number;
81
+ enableGestures: boolean;
82
+ showSpeedPicker: boolean;
83
+ showFullscreenButton: boolean;
84
+ showQualityPicker: boolean;
85
+ qualityLabel: string;
86
+ qualityOptions: Array<{ label: string; url: string }>;
87
+ onQualityChange: (quality: { label: string; url: string } | null) => void;
88
+ thumbImage?: any;
89
+ }
@@ -0,0 +1,64 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { Platform, StatusBar, BackHandler } from 'react-native';
3
+
4
+ /**
5
+ * 全屏状态管理 Hook:屏幕方向锁定、状态栏显隐、Android 返回键处理。
6
+ *
7
+ * 需要 `expo-screen-orientation` 作为 peer dependency。
8
+ */
9
+ export function useFullscreen(initialFullscreen = false) {
10
+ const [isFullscreen, setIsFullscreen] = useState(initialFullscreen);
11
+ const prevOrientation = useRef<number | null>(null);
12
+
13
+ const enterFullscreen = useCallback(async () => {
14
+ try {
15
+ const ScreenOrientation = require('expo-screen-orientation');
16
+ prevOrientation.current = await ScreenOrientation.getOrientationLockAsync?.() ?? null;
17
+ await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.LANDSCAPE);
18
+ } catch {}
19
+ StatusBar.setHidden(true, 'fade');
20
+ setIsFullscreen(true);
21
+ }, []);
22
+
23
+ const exitFullscreen = useCallback(async () => {
24
+ try {
25
+ const ScreenOrientation = require('expo-screen-orientation');
26
+ await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
27
+ } catch {}
28
+ StatusBar.setHidden(false, 'fade');
29
+ setIsFullscreen(false);
30
+ }, []);
31
+
32
+ const toggleFullscreen = useCallback(() => {
33
+ if (isFullscreen) exitFullscreen();
34
+ else enterFullscreen();
35
+ }, [isFullscreen, enterFullscreen, exitFullscreen]);
36
+
37
+ // Android 返回键退出全屏
38
+ useEffect(() => {
39
+ if (Platform.OS !== 'android' || !isFullscreen) return;
40
+ const sub = BackHandler.addEventListener('hardwareBackPress', () => {
41
+ exitFullscreen();
42
+ return true;
43
+ });
44
+ return () => sub.remove();
45
+ }, [isFullscreen, exitFullscreen]);
46
+
47
+ // 组件卸载时恢复屏幕方向
48
+ useEffect(() => {
49
+ return () => {
50
+ try {
51
+ const ScreenOrientation = require('expo-screen-orientation');
52
+ ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP).catch(() => {});
53
+ } catch {}
54
+ StatusBar.setHidden(false, 'fade');
55
+ };
56
+ }, []);
57
+
58
+ return {
59
+ isFullscreen,
60
+ enterFullscreen,
61
+ exitFullscreen,
62
+ toggleFullscreen,
63
+ };
64
+ }