一、实现参考:(完全参考实现,不喜勿喷)
https://www.cnblogs.com/tengyuxin/p/12030742.html
二、实现播放和组件的依赖
需要三个组件
1. 播放视频组件, react-native-video
官网地址 https://www.npmjs.com/package/react-native-video#allowsexternalplayback
2. 进度条,官网上提供的 slider组件我忘记说的什么原因,即将停止支持,我找了react-native-silder
这个个第三方包 官网地址 https://github.com/react-native-community/react-native-slider#onvaluechange
3. 全屏播放,react-native-orientation这个包有问题,因为RN 将对 rnpm 换一种支持策略 ,所以选择用
react-native-orientation-locker 官网地址 https://github.com/wonday/react-native-orientation-locker
三、实现播放的代码
import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import color from "../../color";
import { View, Text, StyleSheet, TouchableWithoutFeedback, Image, TouchableOpacity, Dimensions } from "react-native";
//导入Video组件
import Video from "react-native-video";
// 导入 Slider组件
import Slider from "@react-native-community/slider";
// 屏幕方向锁定: 他需要改变 原来Android文件代码,当然适配两端的话,IOS也是需要更改的。
import Orientation from "react-native-orientation-locker";
/**
* page:{ 视频播放组件 }
* function:
* time:2021/6/22
* author:bai
*/
const windowSize = Dimensions.get("window");//全局属性【用于获取屏幕宽高等信息】
let that;//全局属性【用于保证that的有效区域】
export default class BaseVideo extends PureComponent {
//提供基本属性和方法
static propTypes = {
//视频播放源uri
videoUri: PropTypes.string,
//视频默认图片展示,也就是第一帧
posterImageUri: PropTypes.string,
//是否支持全屏
isSupportFullScreen: PropTypes.bool,
};
constructor(props) {
super(props);
this.playButtonClick = this.playButtonClick.bind(this);
this.customerSliderValue = this.customerSliderValue.bind(this);
this.enterFullScreen = this.enterFullScreen.bind(this);
this._changePauseSliderFullState = this._changePauseSliderFullState.bind(this);
that = this;
this.state = {
isPaused: true, //是暂停
duration: 0, //总时长
currentTime: 0, //当前播放时间
sliderValue: 0, //进度条的进度
//用来控制进入全屏的属性
videoViewWidth: windowSize.width,
videoViewHeight: 226,
isFullScreen: false,
isVisiblePausedSliderFullScreen: false,
};
}
/**
* 初始化屏幕方向
*/
componentDidMount() {
let initial = Orientation.getInitialOrientation();
if (initial === "PORTRAIT") {
console.log("是竖屏");
} else {
console.log("如果是横屏,就将其旋转过来");
Orientation.lockToPortrait();
}
}
/**
* Sort排序类型列表
* @returns {JSX.Element}
*/
render() {
// 播放按钮组件:是否显示
let playButtonComponent = (
<TouchableWithoutFeedback onPress={this.playButtonClick}>
<Image style={styles.playBtn} source={require("../../icons/play_icon.png")}/>
</TouchableWithoutFeedback>
);
// 暂停按钮组件:是否显示
let pauseButtonComponent = (
<TouchableWithoutFeedback onPress={this.playButtonClick}>
<Image style={styles.playBtn} source={require("../../icons/pause_icon.png")}/>
</TouchableWithoutFeedback>
);
// 进度条、全屏按钮 是否显示
let pausedSliderFullComponent = (
<View style={{ position: "absolute", bottom: 0,height:40,width:windowSize.width,
paddingLeft:10,paddingRight:10,flexDirection: "row", alignItems: "center" }}>
{/* 进度条按钮 */}
<View style={{ flex:5,flexDirection: "row",alignItems: "center",paddingLeft:5,paddingRight:5}}>
<Text style={{flex:0,color:color.white,fontSize: 12}}>{this.formatMediaTime(that.state.currentTime)}</Text>
<Slider
style={{ flex:6, height: 40}}
value={that.state.sliderValue}
maximumValue={that.state.duration}
thumbTintColor="#fff" //开关夹点的颜色
minimumTrackTintColor="red"
maximumTrackTintColor="#ccc"
step={1}
onValueChange={this.customerSliderValue}/>
<Text style={{flex:0,color:color.white,fontSize: 12}}>{this.formatMediaTime(that.state.duration)}</Text>
</View>
{/* 全屏按钮 */}
{
that.props.isSupportFullScreen?<TouchableOpacity style={{flex:0}} onPress={this.enterFullScreen}>
<Image style={{width:16,height:16,margin:5}} source={that.state.isFullScreen?
require("../../icons/narrow_screen_icon.png"):require("../../icons/full_screen_icon.png")}/>
</TouchableOpacity>:null
}
</View>
);
return (
<View>
<TouchableWithoutFeedback onPress={this._changePauseSliderFullState}>
<Video source={{uri:that.props.videoUri}}
ref={(ref) => {that.player = ref;}}
style={{ width: that.state.videoViewWidth, height: that.state.videoViewHeight, backgroundColor: "#000000" }}
allowsExternalPlayback={false} // 不允许导出 或 其他播放器播放
paused={that.state.isPaused} // 控制视频是否播放
resizeMode="cover"
poster={that.props.posterImageUri}//视频默认图片展示,也就是第一帧
posterResizeMode="cover"
onLoadStart={(event)=>{
//console.log('媒体开始加载时调用的回调函数,开始加载~~~')
}}
onBuffer={(event)=>{
//console.log('远程视频缓冲时调用,正在缓冲~~~')
}}
onReadyForDisplay={(event)=>{
//console.log('当第一个视频帧准备好显示时调用的回调函数。此时海报将被移除;准备好播放展示~~~')
}}
onEnd={(event)=>{
//console.log('播放结束~~~')
//播放结束后,展示播放按钮,点击重新播放
that.setState({
isPaused:true,
sliderValue:0
},()=>{
//播放完成,设置默认图片展示(第一帧)
that.player.setState({
showPoster: true
})
//重新播放【必须要设置isPaused:true之后】
//that.playButtonClick()
})
}}
onLoad={(e) => this.customerOnload(e)}
onProgress={(e) => this.customerOnprogress(e)}
fullscreen={that.state.isFullScreen}
/>
</TouchableWithoutFeedback>
{/* 播放的按钮:点击之后需要消失 */}
{that.state.isPaused?playButtonComponent:null}
{/* 暂停按钮 */}
{(that.state.isVisiblePausedSliderFullScreen &&!that.state.isPaused)? pauseButtonComponent : null}
{/* 进度条,全屏按钮 */}
{that.state.isVisiblePausedSliderFullScreen ? pausedSliderFullComponent : null}
</View>
);
}
/**
* 控制按钮显示播放,要显示进度条N秒钟,之后关闭显示
*/
playButtonClick() {
//设置播放视频、展示进度条和全屏按钮
that.setState({
isPaused: !that.state.isPaused,
isVisiblePausedSliderFullScreen: true,
});
//设置默认图片不展示(第一帧)
that.player.setState({
showPoster: false
})
//定时,处理进度条和全屏按钮、暂停按钮,多长时间后关闭显示
setTimeout(function() {
that.setState({
isVisiblePausedSliderFullScreen: false,
});
}, 10000);
}
/**
* 单击事件,是否显示 “暂停、进度条、全屏按钮 盒子”
* @private
*/
_changePauseSliderFullState() {
let flag = !that.state.isVisiblePausedSliderFullScreen;
that.setState({
isVisiblePausedSliderFullScreen: flag,
});
setTimeout(function() {
that.setState({
isVisiblePausedSliderFullScreen: false,
});
}, 10000);
}
/**
* 格式化音乐播放的时间为0:00。借助onProgress的定时器调用,更新当前时间
* @param time
* @returns {string}
*/
formatMediaTime(time) {
let minute = Math.floor(time / 60);
let second = parseInt(time - minute * 60);
minute = minute >= 10 ? minute : "0" + minute;
second = second >= 10 ? second : "0" + second;
return minute + ":" + second;
}
/**
* 加载视频调用,主要是拿到 “总时间”,并格式化
* @param e
*/
customerOnload(e) {
let time = e.duration;
that.setState({
duration: time,
});
}
/**
* 获得当前的,播放时间数,但这个数是0.104,需要处理
* @param e
*/
customerOnprogress(e) {
let time = e.currentTime; // 获取播放视频的秒数
that.setState({
currentTime: time,
sliderValue: time,
});
}
/**
* 移动滑块,改变视频播放进度
* @param value
*/
customerSliderValue(value) {
that.player.seek(value);
}
/**
* 全屏
* 1.改变宽高
* 2.允许进入全屏模式
* 3.如何配置屏幕旋转,不需要改变进度条盒子的显示和隐藏
*/
enterFullScreen() {
if(that.state.isFullScreen){
that.setState({
videoViewWidth: windowSize.width,
videoViewHeight: 226,
isFullScreen: false,
});
// 直接设置方向
Orientation.lockToPortrait();
}else{
that.setState({
videoViewWidth: windowSize.height,
videoViewHeight: windowSize.width,
isFullScreen: true,
});
// 直接设置方向
Orientation.lockToLandscape();
}
}
}
//全局样式
const styles = StyleSheet.create({
myVideo: {
width: 340,
height: 240,
},
playBtn: {
width: 30,
height: 30,
position: "absolute",
top: "50%",
left: "50%",
marginLeft: -15,
marginTop: -15,
zIndex: 999,
},
});
四、demo引用的icons
五、引用组件
import BaseVideo from "../../widget/Common/VideoViews/BaseVideo";
<BaseVideo videoUri={"https://v-cdn.zjol.com.cn/276984.mp4"}
posterImageUri={"https://baconmockup.com/300/200/"}/>