Initial frontend repository commit.
Include app source and update .gitignore to exclude local release artifacts and signing files. Made-with: Cursor
This commit is contained in:
145
src/components/common/VideoPlayerModal.tsx
Normal file
145
src/components/common/VideoPlayerModal.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* VideoPlayerModal 视频播放弹窗组件
|
||||
* 全屏模态播放视频,基于 expo-video
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
View,
|
||||
StyleSheet,
|
||||
Dimensions,
|
||||
TouchableOpacity,
|
||||
Text,
|
||||
StatusBar,
|
||||
} from 'react-native';
|
||||
import { VideoView, useVideoPlayer } from 'expo-video';
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { spacing, fontSizes, borderRadius } from '../../theme';
|
||||
|
||||
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
|
||||
|
||||
export interface VideoPlayerModalProps {
|
||||
/** 是否可见 */
|
||||
visible: boolean;
|
||||
/** 视频 URL */
|
||||
url: string;
|
||||
/** 关闭回调 */
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频播放器内容 - 独立组件,仅在弹窗可见时挂载,避免预加载
|
||||
*/
|
||||
const VideoPlayerContent: React.FC<{ url: string; onClose: () => void }> = ({ url, onClose }) => {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
const player = useVideoPlayer(url, (p) => {
|
||||
p.loop = false;
|
||||
p.play();
|
||||
});
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
player.pause();
|
||||
onClose();
|
||||
}, [player, onClose]);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<StatusBar hidden />
|
||||
|
||||
{/* 顶部关闭按钮 */}
|
||||
<View style={[styles.header, { paddingTop: insets.top + spacing.md }]}>
|
||||
<TouchableOpacity style={styles.closeButton} onPress={handleClose}>
|
||||
<MaterialCommunityIcons name="close" size={24} color="#FFF" />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.title}>视频</Text>
|
||||
<View style={styles.placeholder} />
|
||||
</View>
|
||||
|
||||
{/* 视频播放区域 */}
|
||||
<View style={styles.videoContainer}>
|
||||
<VideoView
|
||||
player={player}
|
||||
style={styles.video}
|
||||
contentFit="contain"
|
||||
nativeControls
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* 视频播放弹窗 - 仅在可见时渲染播放器,避免提前加载视频资源
|
||||
*/
|
||||
export const VideoPlayerModal: React.FC<VideoPlayerModalProps> = ({
|
||||
visible,
|
||||
url,
|
||||
onClose,
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
visible={visible}
|
||||
transparent={false}
|
||||
animationType="fade"
|
||||
onRequestClose={onClose}
|
||||
statusBarTranslucent
|
||||
supportedOrientations={['portrait', 'landscape']}
|
||||
>
|
||||
{visible && url ? (
|
||||
<VideoPlayerContent url={url} onClose={onClose} />
|
||||
) : (
|
||||
<View style={styles.container} />
|
||||
)}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingBottom: spacing.md,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 10,
|
||||
},
|
||||
closeButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: borderRadius.full,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
color: '#FFF',
|
||||
fontSize: fontSizes.md,
|
||||
fontWeight: '600',
|
||||
},
|
||||
placeholder: {
|
||||
width: 40,
|
||||
},
|
||||
videoContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
video: {
|
||||
width: SCREEN_WIDTH,
|
||||
height: SCREEN_HEIGHT,
|
||||
},
|
||||
});
|
||||
|
||||
export default VideoPlayerModal;
|
||||
Reference in New Issue
Block a user