CusVideo.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. import React, { Component } from "react";
  2. import {
  3. Platform,
  4. Text,
  5. View,
  6. ImageBackground,
  7. Button,
  8. Image,
  9. TouchableOpacity,
  10. PanResponder,
  11. StyleSheet
  12. } from "react-native";
  13. import { createStackNavigator, createAppContainer } from "react-navigation";
  14. import Video from "react-native-video";
  15. import SeekBar from "../components/SeekBar";
  16. const instructions = Platform.select({
  17. ios: "Press Cmd+R to reload,\n" + "Cmd+D or shake for dev menu",
  18. android:
  19. "Double tap R on your keyboard to reload,\n" +
  20. "Shake or press menu button for dev menu"
  21. });
  22. type Props = {};
  23. export default class CusVideo extends React.Component {
  24. constructor(props) {
  25. super(props);
  26. this.pressStatus = false;
  27. this.progress = 0;
  28. }
  29. config = {
  30. changeX: 0,
  31. changeY: 0,
  32. xDiff: 0,
  33. yDiff: 0
  34. };
  35. state = {
  36. player_status_icon: require("../images/video/start.png"),
  37. rate: 1,
  38. volume: 1,
  39. muted: false,
  40. resizeMode: "stretch",
  41. duration: 0.0,
  42. currentTime: 0.0,
  43. paused: false,
  44. wheel: false,
  45. isFull: false,
  46. needback: this.props.needback,
  47. show_controller: true,
  48. show_loading: true,
  49. loading_arr: [
  50. require("../images/video/loading1.png"),
  51. require("../images/video/loading2.png"),
  52. // require("../images/video/loading3.png"),
  53. require("../images/video/loading4.png"),
  54. require("../images/video/loading5.png"),
  55. // require("../images/video/loading6.png"),
  56. require("../images/video/loading7.png"),
  57. require("../images/video/loading8.png"),
  58. require("../images/video/loading9.png"),
  59. // require("../images/video/loading10.png"),
  60. require("../images/video/loading11.png"),
  61. require("../images/video/loading12.png"),
  62. require("../images/video/loading13.png"),
  63. // require("../images/video/loading14.png"),
  64. require("../images/video/loading15.png"),
  65. require("../images/video/loading16.png"),
  66. // require("../images/video/loading17.png"),
  67. require("../images/video/loading18.png"),
  68. require("../images/video/loading19.png")
  69. // require("../images/video/loading20.png")
  70. ]
  71. };
  72. render() {
  73. if (!this.props.show) {
  74. return null;
  75. }
  76. return (
  77. <View style={[this.props.style, { overflow: "hidden" }]}>
  78. <Video
  79. {...this.videotouch.panHandlers}
  80. style={{ flex: 1 }}
  81. source={{
  82. uri: this.props.uri
  83. }}
  84. ref={ref => {
  85. this.player = ref;
  86. }}
  87. // poster={this.props.poster}
  88. resizeMode={[this.state.resizeMode]}
  89. // posterResizeMode={this.state.resizeMode}
  90. onBuffer={this.onBuffer.bind(this)}
  91. rate={this.state.rate} //播放速率
  92. paused={this.state.paused} //暂停
  93. volume={this.state.volume} //调节音量
  94. muted={this.state.muted} //控制音频是否静音
  95. resizeMode={this.state.resizeMode} //缩放模式
  96. onLoadStart={this.loadStart} // 当视频开始加载时的回调函数
  97. onLoad={this.onLoad} //加载媒体并准备播放时调用的回调函数。
  98. onProgress={this.onProgress} //视频播放过程中每个间隔进度单位调用的回调函数
  99. onEnd={this.onEnd} //视频播放结束时的回调函数
  100. onAudioBecomingNoisy={this.onAudioBecomingNoisy} //音频变得嘈杂时的回调 - 应暂停视频
  101. onAudioFocusChanged={this.onAudioFocusChanged} //音频焦点丢失时的回调 - 如果焦点丢失则暂停
  102. repeat={this.state.wheel} //确定在到达结尾时是否重复播放视频。
  103. onError={this.onError} // 当视频不能加载,或出错后的回调函数
  104. playInBackground={false} // 当app转到后台运行的时候,播放是否暂停
  105. playWhenInactive={true} // [iOS] Video continues to play when control or notification center are shown. 仅适用于IOS
  106. />
  107. <View
  108. style={{
  109. position: "absolute",
  110. width: "100%",
  111. height: "100%",
  112. alignItems: "center",
  113. justifyContent: "center"
  114. }}
  115. >
  116. <Loading
  117. loading_arr={this.state.loading_arr}
  118. show={this.state.show_loading}
  119. />
  120. </View>
  121. <TopController
  122. ref={view => (this.topcontroller = view)}
  123. needback={this.state.needback}
  124. videoback={this.videoBackClick.bind(this)}
  125. />
  126. <BottomController
  127. ref={view => (this.bottomcontroller = view)}
  128. show={this.state.show_controller}
  129. touch_up_callback={this.touch_up_callback.bind(this)}
  130. full_click={this.presentFullscreenPlayer.bind(this)}
  131. play_click={this.play.bind(this)}
  132. duration={this.state.duration}
  133. currentTime={this.state.currentTime}
  134. player_status_icon={this.state.player_status_icon}
  135. />
  136. </View>
  137. );
  138. }
  139. componentWillMount() {
  140. this.videotouch = PanResponder.create({
  141. onStartShouldSetPanResponder: (evt, gestureState) => true,
  142. onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  143. onMoveShouldSetPanResponder: (evt, gestureState) => true,
  144. onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  145. onPanResponderGrant: (evt, gestureState) => {
  146. this.pressStatus = true;
  147. this.config.changeY = evt.nativeEvent.pageY;
  148. this.config.changeX = evt.nativeEvent.pageX;
  149. if (this.state.show_controller) {
  150. this.hideController();
  151. } else {
  152. this.showController();
  153. }
  154. console.log("onPanResponderGrant");
  155. },
  156. onPanResponderStart: (evt, gestureState) => {
  157. this.pressStatus = true;
  158. console.log("onPanResponderStart");
  159. },
  160. onPanResponderMove: (evt, gestureState) => {
  161. this.config.yDiff = evt.nativeEvent.pageY - this.config.changeY;
  162. this.config.xDiff = evt.nativeEvent.pageX - this.config.changeX;
  163. this.config.changeY = evt.nativeEvent.pageY;
  164. this.config.changeX = evt.nativeEvent.pageX;
  165. },
  166. onPanResponderEnd: (evt, gestureState) => {
  167. this.pressStatus = true;
  168. console.log("onPanResponderEnd");
  169. },
  170. onPanResponderTerminationRequest: (evt, gestureState) => true,
  171. onPanResponderRelease: (evt, gestureState) => {
  172. if (this.pressStatus) {
  173. this.props.onPress && this.props.onPress();
  174. }
  175. },
  176. onPanResponderTerminate: (evt, gestureState) => {
  177. // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  178. },
  179. onShouldBlockNativeResponder: (evt, gestureState) => {
  180. // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
  181. // 默认返回true。目前暂时只支持android。
  182. //基于业务交互场景,如果这里使用js事件处理,会导致容器不能左右滑动。所以设置成false.
  183. return false;
  184. }
  185. });
  186. }
  187. loadStart() {
  188. // alert("loadStart");
  189. }
  190. onBuffer({ isBuffering }: { isBuffering: boolean }) {
  191. //true为正在加载,false为没有加载,此处给loading提示
  192. this.setState({
  193. show_loading: isBuffering
  194. });
  195. console.log("isBuffering:" + isBuffering);
  196. }
  197. onLoad = data => {
  198. //获取的是秒数
  199. this.setState({ duration: data.duration });
  200. this.bottomcontroller.setMax(data.duration);
  201. };
  202. onProgress = data => {
  203. this.setState({
  204. currentTime: data.currentTime
  205. });
  206. if (this.state.show_controller) {
  207. this.bottomcontroller.setProgress(this.state.currentTime);
  208. this.bottomcontroller.setMax(this.state.duration);
  209. }
  210. };
  211. onError() {
  212. alert("播放器异常");
  213. }
  214. onEnd() {
  215. alert("播放结束");
  216. }
  217. play() {
  218. //controller的play点击无法换图...
  219. if (this.state.paused) {
  220. this.start();
  221. } else {
  222. this.pause();
  223. }
  224. }
  225. pause() {
  226. this.setState({
  227. paused: true,
  228. player_status_icon: require("../images/video/start.png")
  229. });
  230. this.player_icon_index = 1;
  231. }
  232. start() {
  233. this.setState({
  234. paused: false,
  235. player_status_icon: require("../images/video/pause.png")
  236. });
  237. this.player_icon_index = 0;
  238. }
  239. seekTo(progress) {
  240. this.player.seek(progress);
  241. }
  242. presentFullscreenPlayer() {
  243. if (this.state.isFull) {
  244. this.setState({
  245. isFull: false,
  246. needback: this.props.needback
  247. });
  248. } else {
  249. this.setState({
  250. isFull: true,
  251. needback: true
  252. });
  253. }
  254. this.props.videofullScreenPlayer();
  255. this.bottomcontroller.setProgress(this.state.currentTime);
  256. }
  257. touch_up_callback(progress) {
  258. //抬起之后,获取算出来的progress
  259. this.setState({
  260. currentTime: progress
  261. });
  262. this.seekTo(progress);
  263. }
  264. refreshVideo() {
  265. this.setState({
  266. duration: 0,
  267. currentTime: 0
  268. });
  269. }
  270. videoBackClick() {
  271. // if (this.props.needback != undefined && this.props.needback) {
  272. // }
  273. if (this.state.isFull) {
  274. //全屏状态下,变小屏
  275. this.presentFullscreenPlayer();
  276. } else {
  277. this.props.videoback();
  278. }
  279. }
  280. showController() {
  281. this.setState({
  282. show_controller: true
  283. // needback: this.props.needback
  284. });
  285. this.bottomcontroller.setBottom(0);
  286. this.topcontroller.setTop(0);
  287. }
  288. hideController() {
  289. this.setState({
  290. show_controller: false
  291. // needback: false
  292. });
  293. this.bottomcontroller.setBottom(-50);
  294. this.topcontroller.setTop(-50);
  295. }
  296. }
  297. /**
  298. * 将秒转换为 分:秒
  299. * s int 秒数
  300. */
  301. function formatTime(s) {
  302. //计算分钟
  303. //算法:将秒数除以60,然后下舍入,既得到分钟数
  304. var h;
  305. h = Math.floor(s / 60);
  306. //计算秒
  307. //算法:取得秒%60的余数,既得到秒数
  308. s = Math.round(s % 60);
  309. //将变量转换为字符串
  310. h += "";
  311. s += "";
  312. //如果只有一位数,前面增加一个0
  313. h = h.length == 1 ? "0" + h : h;
  314. s = s.length == 1 ? "0" + s : s;
  315. return h + ":" + s;
  316. }
  317. class TopController extends Component {
  318. state = {
  319. controller_top: 0
  320. };
  321. render() {
  322. if (this.props.needback == false) {
  323. return null;
  324. }
  325. return (
  326. <View
  327. style={[styles.player_controller, { top: this.state.controller_top }]}
  328. >
  329. <View
  330. style={{
  331. flex: 1,
  332. alignItems: "center"
  333. }}
  334. >
  335. <TouchableOpacity
  336. //点击事件放在这个组件里才管用
  337. style={{
  338. justifyContent: "center",
  339. width: "100%",
  340. height: "100%"
  341. }}
  342. onPress={() => this.props.videoback()}
  343. >
  344. <Image
  345. style={[styles.player_pause_icon]}
  346. source={require("../images/video/back.png")}
  347. />
  348. </TouchableOpacity>
  349. </View>
  350. <View style={{ flex: 2 }} />
  351. <View
  352. style={{
  353. flex: 9,
  354. overflow: "hidden"
  355. }}
  356. />
  357. <View style={{ flex: 2 }} />
  358. <View
  359. style={{
  360. flex: 1,
  361. alignItems: "center"
  362. }}
  363. />
  364. </View>
  365. );
  366. }
  367. setTop(top) {
  368. this.setState({
  369. controller_top: top
  370. });
  371. }
  372. }
  373. class Loading extends Component {
  374. constructor(props) {
  375. super(props);
  376. this.loading_index = 0;
  377. }
  378. state = {
  379. loading_img: this.props.loading_arr[0]
  380. };
  381. render() {
  382. if (this.props.show) {
  383. return (
  384. <Image
  385. source={this.state.loading_img}
  386. style={{ width: "20%", height: "10%" }}
  387. />
  388. );
  389. } else {
  390. return null;
  391. }
  392. }
  393. componentWillMount() {
  394. this.changeIndex();
  395. }
  396. changeIndex() {
  397. setTimeout(() => {
  398. if (this.loading_index > this.props.loading_arr.length - 1) {
  399. this.loading_index = 1;
  400. } else {
  401. this.loading_index = this.loading_index + 1;
  402. }
  403. this.setState({
  404. loading_img: this.props.loading_arr[this.loading_index]
  405. });
  406. this.changeIndex();
  407. }, 500);
  408. }
  409. }
  410. class BottomController extends Component {
  411. state = {
  412. controller_bottom: 0
  413. };
  414. render() {
  415. return (
  416. <View
  417. style={[
  418. styles.player_controller,
  419. { bottom: this.state.controller_bottom }
  420. ]}
  421. >
  422. <View
  423. style={{
  424. flex: 1,
  425. alignItems: "center"
  426. }}
  427. >
  428. {/* 暂停播放按钮 */}
  429. <TouchableOpacity
  430. //点击事件放在这个组件里才管用
  431. style={{
  432. justifyContent: "center",
  433. width: "100%",
  434. height: "100%"
  435. }}
  436. onPress={() => this.props.play_click()}
  437. >
  438. <Image
  439. style={[styles.player_pause_icon]}
  440. source={this.props.player_status_icon}
  441. />
  442. </TouchableOpacity>
  443. </View>
  444. <View style={{ flex: 2 }}>
  445. {/* //左侧当前播放时长 */}
  446. <Text style={[styles.player_time]}>
  447. {formatTime(this.props.currentTime)}
  448. </Text>
  449. </View>
  450. <View
  451. style={{
  452. flex: 9,
  453. overflow: "hidden"
  454. }}
  455. >
  456. {/* //中间进度条 */}
  457. <SeekBar
  458. style={{ flex: 1 }}
  459. ref={view => (this.seekbar = view)}
  460. //必须带此方法,作为滑动之后抬起的回调
  461. touchUpCallBack={this.props.touch_up_callback}
  462. />
  463. </View>
  464. <View style={{ flex: 2 }}>
  465. {/* //右侧总时长 */}
  466. <Text style={[styles.player_time]}>
  467. {formatTime(this.props.duration)}
  468. </Text>
  469. </View>
  470. <View
  471. style={{
  472. flex: 1,
  473. alignItems: "center"
  474. }}
  475. >
  476. <TouchableOpacity
  477. style={{
  478. justifyContent: "center",
  479. width: "100%",
  480. height: "100%"
  481. }}
  482. onPress={() => this.props.full_click()}
  483. >
  484. {/* //放大按钮 */}
  485. <Image
  486. style={[styles.fullscreen_icon]}
  487. source={require("../images/video/fullscreen.png")}
  488. />
  489. </TouchableOpacity>
  490. </View>
  491. </View>
  492. );
  493. }
  494. setMax(duration) {
  495. this.seekbar.setMax(duration);
  496. }
  497. getMax() {
  498. return this.seekbar.getMax();
  499. }
  500. setBottom(bottom) {
  501. this.setState({
  502. controller_bottom: bottom
  503. });
  504. }
  505. setProgress(currentTime) {
  506. this.seekbar.setProgress(currentTime);
  507. }
  508. }
  509. const styles = StyleSheet.create({
  510. player_controller: {
  511. flexDirection: "row",
  512. position: "absolute",
  513. width: "100%",
  514. height: 50,
  515. alignItems: "center",
  516. justifyContent: "center"
  517. },
  518. player_pause_icon: {
  519. width: "80%",
  520. resizeMode: "center",
  521. height: "40%",
  522. justifyContent: "center",
  523. alignItems: "center",
  524. left: 5
  525. },
  526. fullscreen_icon: {
  527. width: "80%",
  528. resizeMode: "center",
  529. height: "30%",
  530. justifyContent: "center",
  531. alignItems: "center"
  532. },
  533. player_time: {
  534. fontSize: 18,
  535. color: "white",
  536. alignItems: "center",
  537. justifyContent: "center",
  538. textAlignVertical: "center",
  539. textAlign: "center"
  540. }
  541. });
  542. /**
  543. 使用方法
  544. <CusVideo
  545. uri={this.state.video_uri}
  546. ref={view => (this.video = view)}
  547. needback={true} //(是否需要小窗口的返回按钮)
  548. videoback={this.clickVideoBack.bind(this)}//(小窗口返回按钮的事件)
  549. videofullScreenPlayer={this.fullScreenPlayer.bind(this)}//(点击全屏按钮的事件)
  550. style={{
  551. flex: this.state.video_flex,
  552. width: this.state.video_width,
  553. height: this.state.video_height
  554. }}
  555. />
  556. */