参考
安装步骤
yarn add react-native-video
yarn add @types/react-native-video --dev
加类型声明文件
在android/settings.gradle
加入
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
在android/build.gradle
加入
allprojects {
repositories {
...
jcenter() {
content {
includeModule("com.yqritc", "android-scalablevideoview")
}
}
}
}
在android/app/build.gradle
加入---
dependencies {
...
implementation project(':react-native-video') //加这个
}
在android/app/src/main/java/com/文件名/MainApplication.java
加入
import com.brentvatne.react.ReactVideoPackage; //加这个
...
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new ReactVideoPackage()); //加这个
return packages;
}
使用
注意: Video组件自带的controls不会跟着页面滚动,所以最好自己封装一个controls。
封装Video组件
import React from 'react';
import {
ViewStyle,
StyleProp,
} from 'react-native'
import { View, Text, TouchableOpacity } from '@/components'
import Loading from "@/components/loading";
import RNVideo, { VideoProperties } from 'react-native-video';
interface componentProps extends VideoProperties {
style?: StyleProp<ViewStyle>
}
interface State {
videoPaused?: boolean
hideControl?: boolean
duration: number //总时长-秒
currentTime: number //当前进度-秒
isLoading?: boolean //加载中
}
const date = {
prefixZero: (num, n) => {
return (Array (n).join ('0') + num).slice(-n);
},
parseSeconds: (second:number) => {
if (second > 0) {
let hour = 0
let minute = 0
let seconds = 0
let data:{
hour?: number
minute?: number
seconds?: number
} = {}
minute = Math.floor(second / 60)
if (parseInt(String(minute)) > 60) {
hour = parseInt(String(minute / 60))
minute %= 60 //算出有多分钟
}
seconds = second%60;
data.hour = hour;
data.minute = minute;
data.seconds = parseInt(String(seconds))
return data
}
}
} as const;
export default class Video extends React.PureComponent<componentProps, State>{
static defaultProps = {
resizeMode: 'contain',
controls: false,
repeat: true,
paused: false,
// poster: '',
// posterResizeMode: ''
}
public state:State = {
currentTime: 0,
duration: 0
};
render() {
const { videoPaused, hideControl, currentTime, duration, isLoading } = this.state
const { style, ...restProps } = this.props
return (
<TouchableOpacity activeOpacity={1} style={{
position: 'relative'
}} onPress={this.toggleHideControl}>
<RNVideo style={[{
backgroundColor: 'rgba(0,0,0,0.8)'
}, style]}
{...restProps}
ref={this.handleBindRef}
paused={videoPaused}
onLoadStart={this.onVideoLoadStart}
onLoad={this.onLoad}
onEnd={this.onEnd}
progressUpdateInterval={1000}
onProgress={this.onProgress}
/>
{hideControl? null:
<View style={styles.controlBox()}>
<View style={[styles.controlBtns(), {flex: 1}]}>
<TouchableOpacity style={styles.playBtn()} onPress={this.togglePause}>
{videoPaused?
<Text>{'播放'}</Text> :
<Text>{'暂停'}</Text>
}
</TouchableOpacity>
<TouchableOpacity style={styles.towardBtn()} onPress={this.headBack}>
<Text>{'<<'}</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.towardBtn()} onPress={this.headForward}>
<Text>{'>>'}</Text>
</TouchableOpacity>
</View>
{(!isLoading && duration) ?
<View style={{paddingHorizontal: 10}}>
<Text style={{}}>{this.parseSeconds(currentTime)}/{this.parseSeconds(duration)}</Text>
</View>: null
}
</View>
}
{isLoading?
<View style={styles.loadingBox()}>
<Loading visible={true}></Loading>
</View> : null
}
</TouchableOpacity>
);
};
private refVideo:any = null;
private handleBindRef = (e:any) => {
this.refVideo = e
}
private parseSeconds = (second:number) => {
const vals = date.parseSeconds(second)
if(vals) {
let hour = vals.hour? date.prefixZero(vals.hour, 2): '';
let minute = date.prefixZero(vals.minute, 2);
let seconds = date.prefixZero(vals.seconds, 2);
return hour? (hour+':'+minute+':'+seconds): (minute+':'+seconds)
}else {
return ''
}
}
private togglePause = () => {
const { videoPaused } = this.state
if(videoPaused) {
this.autoHideControls()
}
this.setState({videoPaused: !videoPaused})
}
private toggleHideControl = () => {
const { hideControl } = this.state
if(hideControl) {
this.autoHideControls()
}
this.setState({hideControl: !hideControl})
}
private autoHideTimeout:any = null;
private autoHideControls = () => {
if(this.autoHideTimeout) {
clearTimeout(this.autoHideTimeout)
}
this.autoHideTimeout = setTimeout(() => {
if(!this.state.videoPaused) {
this.setState({hideControl: true})
}
}, 5000)
}
onVideoLoadStart = () => {
this.setState({ isLoading: true })
}
private onLoad = (data:any) => {
this.setState({ duration: data.duration, isLoading: false, });
this.autoHideControls()
}
private onEnd = () => {
// TODO: //BUG: repeat=false的情况下,onEnd后重新播放,不执行onProgress;
this.setState({
videoPaused: true,
currentTime: 0,
duration: 0,
})
}
private onProgress = (data: any) => {
this.setState({ currentTime: data.currentTime })
}
private seekTo = (second:number) => {
this.refVideo?.seek(second)
}
private stepNumber: number = 5; //一次前进后退的秒数
private headForward = () => {
const { currentTime, duration } = this.state
let target = currentTime+this.stepNumber;
target = target > duration? duration: target;
this.seekTo(target);
this.autoHideControls()
}
private headBack = () => {
const { currentTime, duration } = this.state
let target = currentTime-this.stepNumber;
target = target > 0? target: 0;
this.seekTo(target);
this.autoHideControls()
}
}
调用
import Video from '@/components/Video';
...
render() {
...
<Video style={{
height: 500, width: 300
}}
repeat={true}
source={{uri: 'https://test.mp4'}}
/>
...
}
遗留问题
repeat=false的情况下,onEnd后重新播放,不执行onProgress