首先是准备工作,安装着两个库:
1、安装video、orientation库,以及配置。
yarn add react-native-video
然后link
react-native link
详情见:https://github.com/react-native-community/react-native-video#installation
安装orientation库,
yarn add react-native-orientation
react-native link
详情请看:https://github.com/yamill/react-native-orientation
2、学习一些基本的属性和方法。
(1)orientation(你需要了解RN的生命周期)
添加监听和移除监听:
添加监听:
需要在生命周期方中进行
componentDidMount() {
Orientation.addOrientationListener((orientation)=>{});
Orientation.addSpecificOrientationListener((specificOrientation)=>{});
}
orientation 将返回以下值之一:
LANDSCAPE
PORTRAIT
PORTRAITUPSIDEDOWN
UNKNOWN
specificOrientation 将返回以下值之一:
LANDSCAPE-LEFT
LANDSCAPE-RIGHT
PORTRAIT
PORTRAITUPSIDEDOWN
UNKNOWN
移除监听:
componentWillUnmount() {
Orientation.removeOrientationListener(this._updateOrientation);
Orientation.removeSpecificOrientationListener(this._updateSpecificOrientation);
}
2、初始化:
componentWillMount() {
const init = Orientation.getInitialOrientation();//得到最初的手机屏幕
this.setState({
init,
orientation: init,
specificOrientation: init,
});
}
3、API
lockToPortrait():竖屏锁定
lockToLandscape():横屏锁定,但不锁定左右横屏
lockToLandscapeLeft():左横屏
lockToLandscapeRight():右横屏
unlockAllOrientations():解除所有锁定
getOrientation((err, orientation) => {}):得到当前屏幕的信息
getSpecificOrientation((err, specificOrientation) => {}):同上
4、实例demo:
import React, { Component } from 'react';
import { Alert, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Orientation from 'react-native-orientation';
export default class Demo extends Component {
componentWillMount() {
const init = Orientation.getInitialOrientation();
this.setState({
init,
orientation: init,
specificOrientation: init,
});
}
componentDidMount() {
Orientation.addOrientationListener(this._updateOrientation);
Orientation.addSpecificOrientationListener(this._updateSpecificOrientation);
}
componentWillUnmount() {
Orientation.removeOrientationListener(this._updateOrientation);
Orientation.removeSpecificOrientationListener(this._updateSpecificOrientation);
}
_getOrientation() {
Orientation.getOrientation((err, orientation) => {
Alert.alert(`Orientation is ${orientation}`);
});
}
_getSpecificOrientation() {
Orientation.getSpecificOrientation((err, orientation) => {
Alert.alert(`Specific orientation is ${orientation}`);
});
}
_updateOrientation = (orientation) => this.setState({ orientation });
_updateSpecificOrientation = (specificOrientation) => this.setState({ specificOrientation });
render() {
const { init, orientation, specificOrientation } = this.state;
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native Orientation Demo!
</Text>
<Text style={styles.instructions}>
{`Initial Orientation: ${init}`}
</Text>
<Text style={styles.instructions}>
{`Current Orientation: ${orientation}`}
</Text>
<Text style={styles.instructions}>
{`Specific Orientation: ${specificOrientation}`}
</Text>
<TouchableOpacity
onPress={Orientation.unlockAllOrientations}
style={styles.button}
>
<Text style={styles.instructions}>
Unlock All Orientations
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={Orientation.lockToPortrait}
style={styles.button}
>
<Text style={styles.instructions}>
Lock To Portrait
</Text>
</TouchableOpacity>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={Orientation.lockToLandscapeLeft}
style={styles.button}
>
<Text style={styles.instructions}>
Lock To Left
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={Orientation.lockToLandscape}
style={styles.button}
>
<Text style={styles.instructions}>
Lock To Landscape
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={Orientation.lockToLandscapeRight}
style={styles.button}
>
<Text style={styles.instructions}>
Lock To Right
</Text>
</TouchableOpacity>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity
onPress={this._getOrientation}
style={styles.button}
>
<Text style={styles.instructions}>
Get Orientation
</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={this._getSpecificOrientation}
style={styles.button}
>
<Text style={styles.instructions}>
Get Specific Orientation
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
buttonContainer: {
flex: 0,
flexDirection: 'row',
justifyContent: 'space-around'
},
button: {
padding: 5,
margin: 5,
borderWidth: 1,
borderColor: 'white',
borderRadius: 3,
backgroundColor: 'grey',
}
});
(这是orientation的实例Demo)见:https://github.com/yamill/react-native-orientation/blob/master/demo/app.js
实例图:
(2)Video
一大堆属性:
真的是很多我就不一一举出:详情见:https://www.npmjs.com/package/react-native-video
source:这个跟图片资源是一样的。
audioOnly:仅播放音频(boolean)
paused:是否暂停(boolean)
playInBackground:是否允许后台播放(boolean)
rate:视频倍速(num)
0.0 - 暂停视频
1.0 - 以正常速度播放
其他值 - 减慢或加快播放速度
volume:声音
1.0(默认) - 以全音量播放
0.0 - 静音
其他值 - 减少音量
repeat:是否循环播放(boolean)
muted:是否静音
resizeMode
确定当帧与原始视频尺寸不匹配时如何调整视频大小。
“无”(默认) - 不应用调整大小
“包含” - 统一缩放视频(保持视频的宽高比),使视频的尺寸(宽度和高度)等于或小于视图的相应尺寸(减去填充)。
“cover” - 均匀缩放视频(保持视频的宽高比),使图像的尺寸(宽度和高度)等于或大于视图的相应尺寸(减去填充)。
“stretch” - 独立缩放宽度和高度,这可能会改变src的宽高比。
平台:Android ExoPlayer,Android MediaPlayer,iOS,Windows UWP
方法:
onLoad
Callback function that is called when the media is loaded and ready to play.
Payload:
Property | Type | Description |
---|---|---|
currentPosition | number | Time in seconds where the media will start |
duration | number | Length of the media in seconds |
naturalSize | object | Properties: |
- width - Width in pixels that the video was encoded at
- height - Height in pixels that the video was encoded at
- orientation - "portrait" or "landscape" |
| audioTracks | array | An array of audio track info objects with the following properties: - index - Index number
- title - Description of the track
- language - 2 letter ISO 639-1 or 3 letter ISO639-2 language code
- type - Mime type of track |
| textTracks | array | An array of text track info objects with the following properties: - index - Index number
- title - Description of the track
- language - 2 letter ISO 639-1 or 3 letter ISO 639-2 language code
- type - Mime type of track |
onProgress
Callback function that is called every progressInterval seconds with info about which position the media is currently playing.
Property Description
currentTime number
playableDuration number
seekableDuration number
seek()
seek(seconds)
Seek to the specified position represented by seconds. seconds is a float value.
seek() can only be called after the onLoad event has fired.
'use strict';
import React, {
Component
} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Video from 'react-native-video';
class VideoPlayer extends Component {
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
paused: true,
};
video: Video;
onLoad = (data) => {
this.setState({ duration: data.duration });
};
onProgress = (data) => {
this.setState({ currentTime: data.currentTime });
};
onEnd = () => {
this.setState({ paused: true })
this.video.seek(0)
};
onAudioBecomingNoisy = () => {
this.setState({ paused: true })
};
onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
this.setState({ paused: !event.hasAudioFocus })
};
getCurrentTimePercentage() {
if (this.state.currentTime > 0) {
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
}
return 0;
};
renderRateControl(rate) {
const isSelected = (this.state.rate === rate);
return (
<TouchableOpacity onPress={() => { this.setState({ rate }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{rate}x
</Text>
</TouchableOpacity>
);
}
renderResizeModeControl(resizeMode) {
const isSelected = (this.state.resizeMode === resizeMode);
return (
<TouchableOpacity onPress={() => { this.setState({ resizeMode }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{resizeMode}
</Text>
</TouchableOpacity>
)
}
renderVolumeControl(volume) {
const isSelected = (this.state.volume === volume);
return (
<TouchableOpacity onPress={() => { this.setState({ volume }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{volume * 100}%
</Text>
</TouchableOpacity>
)
}
render() {
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.fullScreen}
onPress={() => this.setState({ paused: !this.state.paused })}
>
<Video
ref={(ref: Video) => { this.video = ref }}
/* For ExoPlayer */
/* source={{ uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0', type: 'mpd' }} */
source={require('./broadchurch.mp4')}
style={styles.fullScreen}
rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
onLoad={this.onLoad}
onProgress={this.onProgress}
onEnd={this.onEnd}
onAudioBecomingNoisy={this.onAudioBecomingNoisy}
onAudioFocusChanged={this.onAudioFocusChanged}
repeat={false}
/>
</TouchableOpacity>
<View style={styles.controls}>
<View style={styles.generalControls}>
<View style={styles.rateControl}>
{this.renderRateControl(0.25)}
{this.renderRateControl(0.5)}
{this.renderRateControl(1.0)}
{this.renderRateControl(1.5)}
{this.renderRateControl(2.0)}
</View>
<View style={styles.volumeControl}>
{this.renderVolumeControl(0.5)}
{this.renderVolumeControl(1)}
{this.renderVolumeControl(1.5)}
</View>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
</View>
</View>
<View style={styles.trackingControls}>
<View style={styles.progress}>
<View style={[styles.innerProgressCompleted, { flex: flexCompleted }]} />
<View style={[styles.innerProgressRemaining, { flex: flexRemaining }]} />
</View>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
},
fullScreen: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
controls: {
backgroundColor: 'transparent',
borderRadius: 5,
position: 'absolute',
bottom: 20,
left: 20,
right: 20,
},
progress: {
flex: 1,
flexDirection: 'row',
borderRadius: 3,
overflow: 'hidden',
},
innerProgressCompleted: {
height: 20,
backgroundColor: '#cccccc',
},
innerProgressRemaining: {
height: 20,
backgroundColor: '#2C2C2C',
},
generalControls: {
flex: 1,
flexDirection: 'row',
borderRadius: 4,
overflow: 'hidden',
paddingBottom: 10,
},
rateControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
volumeControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
});
Video 的demo:https://github.com/react-native-community/react-native-video/blob/master/example/index.android.js
下面是我结合使用的情况:
import React, {
Component
} from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View,
Image,
Dimensions
} from 'react-native';
let {width,height} = Dimensions.get('window');
import Video from 'react-native-video';
import Orientation from 'react-native-orientation';
export default class VideoTest extends Component {
state = {
rate: 1,
volume: 1,
muted: false,
resizeMode: 'contain',
duration: 0.0,
currentTime: 0.0,
paused: true,
};
componentWillMount(){
const init = Orientation.getInitialOrientation();
this.setState({
orientation:init,
})
};
componentDidMount(){
Orientation.addOrientationListener(this.updateOrientation)
}
componentWillUnmount(){
Orientation.removeOrientationListener(this.updateOrientation);
}
updateOrientation = (orientation) => this.setState({orientation});
Change(){
if(this.state.orientation === 'PORTRAIT'){
Orientation.lockToLandscape();
this.setState({
orientation:'LANDSCAPE'
})
}else{
Orientation.lockToPortrait();
this.setState({
orientation:'PORTRAIT'
})
}
}
//video: Video;
onLoad = (data) => {
this.setState({ duration: data.duration });
};
onProgress = (data) => {
this.setState({ currentTime: data.currentTime });
};
onEnd = () => {
this.setState({ paused: true });
this.video.seek(0);
};
onAudioBecomingNoisy = () => {
this.setState({ paused: true })
};
onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
this.setState({ paused: !event.hasAudioFocus })
};
getCurrentTimePercentage() {
if (this.state.currentTime > 0) {
return parseFloat(this.state.currentTime) / parseFloat(this.state.duration);
}
return 0;
};
renderRateControl(rate) {
const isSelected = (this.state.rate === rate);
return (
<TouchableOpacity onPress={() => { this.setState({ rate }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{rate}x
</Text>
</TouchableOpacity>
);
}
renderResizeModeControl(resizeMode) {
const isSelected = (this.state.resizeMode === resizeMode);
return (
<TouchableOpacity onPress={() => { this.setState({ resizeMode }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{resizeMode}
</Text>
</TouchableOpacity>
)
}
renderVolumeControl(volume) {
const isSelected = (this.state.volume === volume);
return (
<TouchableOpacity onPress={() => { this.setState({ volume }) }}>
<Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
{volume * 100}%
</Text>
</TouchableOpacity>
)
}
render() {
const flexCompleted = this.getCurrentTimePercentage() * 100;
const flexRemaining = (1 - this.getCurrentTimePercentage()) * 100;
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.fullScreen}
onPress={() => this.setState({ paused: !this.state.paused })}
>
<Video
ref={(ref: Video) => { this.video = ref }}
/* For ExoPlayer */
/* source={{ uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0', type: 'mpd' }} */
//source={require('./img/video.mp4')}
source={{uri:'http://baobab.kaiyanapp.com/api/v1/playUrl?vid=121398&resourceType=video&editionType=normal&source=aliyun'}}
//audioOnly={true}
playInBackground={true}
//poster='https://baconmockup.com/300/200/'
style={styles.fullScreen}
rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume}
muted={this.state.muted}
resizeMode={this.state.resizeMode}
onLoad={this.onLoad}
onProgress={this.onProgress}
onEnd={this.onEnd}
onAudioBecomingNoisy={this.onAudioBecomingNoisy}
onAudioFocusChanged={this.onAudioFocusChanged}
repeat={false}
/>
</TouchableOpacity>
{this.state.paused ? <Image source={{uri:'http://ww1.sinaimg.cn/large/005T39qagy1fuiz5uzl1zj301e01e0dx.jpg'}}
style={{width:30,height:30}}
/>:null}
<View style={styles.controls}>
<View style={styles.generalControls}>
<View style={styles.rateControl}>
{this.renderRateControl(0.25)}
{this.renderRateControl(0.5)}
{this.renderRateControl(1.0)}
{this.renderRateControl(1.5)}
{this.renderRateControl(2.0)}
</View>
<View style={styles.volumeControl}>
{this.renderVolumeControl(0.5)}
{this.renderVolumeControl(1)}
{this.renderVolumeControl(1.5)}
</View>
<View style={styles.resizeModeControl}>
{this.renderResizeModeControl('cover')}
{this.renderResizeModeControl('contain')}
{this.renderResizeModeControl('stretch')}
</View>
</View>
<View style={styles.trackingControls}>
<View style={styles.progress}>
<View style={[styles.innerProgressCompleted, { flex: flexCompleted }]} />
<View style={[styles.innerProgressRemaining, { flex: flexRemaining }]} />
</View>
</View>
<View style={{flexDirection:'row',justifyContent:'space-between'}}>
<TouchableOpacity
onPress={()=>{
if(this.state.muted){
this.setState({muted:false})
}else{
this.setState({muted:true})
}
}}
>
<Image source={{uri:'http://ww1.sinaimg.cn/large/005T39qagy1fu5pr4xhesj300q00q0ir.jpg'}}
style={{width:20,height:20}}
/>
</TouchableOpacity>
<TouchableOpacity
onPress={this.Change.bind(this)}
>
<Image source={{uri:'http://ww1.sinaimg.cn/large/005T39qagy1fuiyd51g0gj300o00o06h.jpg'}}
style={{width:20,height:20}}
/>
</TouchableOpacity>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'black',
},
fullScreen: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
controls: {
backgroundColor: 'transparent',
borderRadius: 5,
position: 'absolute',
bottom: 20,
left: 20,
right: 20,
},
progress: {
flex: 1,
flexDirection: 'row',
borderRadius: 3,
overflow: 'hidden',
},
innerProgressCompleted: {
height: 20,
backgroundColor: '#cccccc',
},
innerProgressRemaining: {
height: 20,
backgroundColor: '#2C2C2C',
},
generalControls: {
flex: 1,
flexDirection: 'row',
borderRadius: 4,
overflow: 'hidden',
paddingBottom: 10,
},
rateControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
volumeControl: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
resizeModeControl: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
controlOption: {
alignSelf: 'center',
fontSize: 11,
color: 'white',
paddingLeft: 2,
paddingRight: 2,
lineHeight: 12,
},
});
效果图:
Screenshot_2018-08-23-12-19-35.png
网友评论