CusVideo.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  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: true,
  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. if (this.state.pause) {
  141. }
  142. this.videotouch = PanResponder.create({
  143. onStartShouldSetPanResponder: (evt, gestureState) => true,
  144. onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  145. onMoveShouldSetPanResponder: (evt, gestureState) => true,
  146. onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  147. onPanResponderGrant: (evt, gestureState) => {
  148. this.pressStatus = true;
  149. this.config.changeY = evt.nativeEvent.pageY;
  150. this.config.changeX = evt.nativeEvent.pageX;
  151. if (this.state.show_controller) {
  152. this.hideController();
  153. } else {
  154. this.showController();
  155. }
  156. console.log("onPanResponderGrant");
  157. },
  158. onPanResponderStart: (evt, gestureState) => {
  159. this.pressStatus = true;
  160. console.log("onPanResponderStart");
  161. },
  162. onPanResponderMove: (evt, gestureState) => {
  163. this.config.yDiff = evt.nativeEvent.pageY - this.config.changeY;
  164. this.config.xDiff = evt.nativeEvent.pageX - this.config.changeX;
  165. this.config.changeY = evt.nativeEvent.pageY;
  166. this.config.changeX = evt.nativeEvent.pageX;
  167. },
  168. onPanResponderEnd: (evt, gestureState) => {
  169. this.pressStatus = true;
  170. console.log("onPanResponderEnd");
  171. },
  172. onPanResponderTerminationRequest: (evt, gestureState) => true,
  173. onPanResponderRelease: (evt, gestureState) => {
  174. if (this.pressStatus) {
  175. this.props.onPress && this.props.onPress();
  176. }
  177. },
  178. onPanResponderTerminate: (evt, gestureState) => {
  179. // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  180. },
  181. onShouldBlockNativeResponder: (evt, gestureState) => {
  182. // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
  183. // 默认返回true。目前暂时只支持android。
  184. //基于业务交互场景,如果这里使用js事件处理,会导致容器不能左右滑动。所以设置成false.
  185. return false;
  186. }
  187. });
  188. }
  189. loadStart() {
  190. // alert("loadStart");
  191. }
  192. onBuffer({ isBuffering }: { isBuffering: boolean }) {
  193. //true为正在加载,false为没有加载,此处给loading提示
  194. this.setState({
  195. show_loading: isBuffering
  196. });
  197. console.log("isBuffering:" + isBuffering);
  198. }
  199. onLoad = data => {
  200. //获取的是秒数
  201. this.setState({ duration: data.duration });
  202. this.bottomcontroller.setMax(data.duration);
  203. };
  204. onProgress = data => {
  205. this.setState({
  206. currentTime: data.currentTime
  207. });
  208. if (this.state.show_controller) {
  209. this.bottomcontroller.setProgress(this.state.currentTime);
  210. this.bottomcontroller.setMax(this.state.duration);
  211. }
  212. };
  213. onError() {
  214. alert("播放器异常");
  215. }
  216. onEnd() {
  217. alert("播放结束");
  218. }
  219. play() {
  220. //controller的play点击无法换图...
  221. if (this.state.paused) {
  222. this.start();
  223. } else {
  224. this.pause();
  225. }
  226. }
  227. pause() {
  228. this.setState({
  229. paused: true,
  230. player_status_icon: require("../images/video/start.png")
  231. });
  232. this.player_icon_index = 1;
  233. }
  234. start() {
  235. this.setState({
  236. paused: false,
  237. player_status_icon: require("../images/video/pause.png")
  238. });
  239. this.player_icon_index = 0;
  240. }
  241. seekTo(progress) {
  242. this.player.seek(progress);
  243. }
  244. presentFullscreenPlayer() {
  245. if (this.state.isFull) {
  246. this.setState({
  247. isFull: false,
  248. needback: this.props.needback
  249. });
  250. } else {
  251. this.setState({
  252. isFull: true,
  253. needback: true
  254. });
  255. }
  256. this.props.videofullScreenPlayer();
  257. this.bottomcontroller.setProgress(this.state.currentTime);
  258. }
  259. touch_up_callback(progress) {
  260. //抬起之后,获取算出来的progress
  261. this.setState({
  262. currentTime: progress
  263. });
  264. this.seekTo(progress);
  265. }
  266. refreshVideo() {
  267. this.setState({
  268. duration: 0,
  269. currentTime: 0
  270. });
  271. }
  272. videoBackClick() {
  273. // if (this.props.needback != undefined && this.props.needback) {
  274. // }
  275. if (this.state.isFull) {
  276. //全屏状态下,变小屏
  277. this.presentFullscreenPlayer();
  278. } else {
  279. this.props.videoback();
  280. }
  281. }
  282. showController() {
  283. this.setState({
  284. show_controller: true
  285. // needback: this.props.needback
  286. });
  287. this.bottomcontroller.setBottom(0);
  288. this.topcontroller.setTop(0);
  289. }
  290. hideController() {
  291. this.setState({
  292. show_controller: false
  293. // needback: false
  294. });
  295. this.bottomcontroller.setBottom(-50);
  296. this.topcontroller.setTop(-50);
  297. }
  298. }
  299. /**
  300. * 将秒转换为 分:秒
  301. * s int 秒数
  302. */
  303. function formatTime(s) {
  304. //计算分钟
  305. //算法:将秒数除以60,然后下舍入,既得到分钟数
  306. var h;
  307. h = Math.floor(s / 60);
  308. //计算秒
  309. //算法:取得秒%60的余数,既得到秒数
  310. s = Math.round(s % 60);
  311. //将变量转换为字符串
  312. h += "";
  313. s += "";
  314. //如果只有一位数,前面增加一个0
  315. h = h.length == 1 ? "0" + h : h;
  316. s = s.length == 1 ? "0" + s : s;
  317. return h + ":" + s;
  318. }
  319. class TopController extends Component {
  320. state = {
  321. controller_top: 0
  322. };
  323. render() {
  324. if (this.props.needback == false) {
  325. return null;
  326. }
  327. return (
  328. <View
  329. style={[styles.player_controller, { top: this.state.controller_top }]}
  330. >
  331. <View
  332. style={{
  333. flex: 1,
  334. alignItems: "center"
  335. }}
  336. >
  337. <TouchableOpacity
  338. //点击事件放在这个组件里才管用
  339. style={{
  340. justifyContent: "center",
  341. width: "100%",
  342. height: "100%"
  343. }}
  344. onPress={() => this.props.videoback()}
  345. >
  346. <Image
  347. style={[styles.player_pause_icon]}
  348. source={require("../images/video/back.png")}
  349. />
  350. </TouchableOpacity>
  351. </View>
  352. <View style={{ flex: 2 }} />
  353. <View
  354. style={{
  355. flex: 9,
  356. overflow: "hidden"
  357. }}
  358. />
  359. <View style={{ flex: 2 }} />
  360. <View
  361. style={{
  362. flex: 1,
  363. alignItems: "center"
  364. }}
  365. />
  366. </View>
  367. );
  368. }
  369. setTop(top) {
  370. this.setState({
  371. controller_top: top
  372. });
  373. }
  374. }
  375. class Loading extends Component {
  376. constructor(props) {
  377. super(props);
  378. this.loading_index = 0;
  379. }
  380. state = {
  381. loading_img: this.props.loading_arr[0]
  382. };
  383. render() {
  384. if (this.props.show) {
  385. return (
  386. <Image
  387. source={this.state.loading_img}
  388. style={{ width: 100, height: 20 }}
  389. />
  390. );
  391. } else {
  392. return null;
  393. }
  394. }
  395. componentWillMount() {
  396. this.changeIndex();
  397. }
  398. changeIndex() {
  399. console.log("this.loading_index :" + this.loading_index);
  400. setTimeout(() => {
  401. if (this.loading_index > this.props.loading_arr.length - 2) {
  402. this.loading_index = 0;
  403. } else {
  404. this.loading_index = this.loading_index + 1;
  405. }
  406. this.setState({
  407. loading_img: this.props.loading_arr[this.loading_index]
  408. });
  409. this.changeIndex();
  410. }, 1);
  411. }
  412. }
  413. class BottomController extends Component {
  414. state = {
  415. controller_bottom: 0
  416. };
  417. render() {
  418. return (
  419. <View
  420. style={[
  421. styles.player_controller,
  422. { bottom: this.state.controller_bottom }
  423. ]}
  424. >
  425. <View
  426. style={{
  427. flex: 1,
  428. alignItems: "center"
  429. }}
  430. >
  431. {/* 暂停播放按钮 */}
  432. <TouchableOpacity
  433. //点击事件放在这个组件里才管用
  434. style={{
  435. justifyContent: "center",
  436. width: "100%",
  437. height: "100%"
  438. }}
  439. onPress={() => this.props.play_click()}
  440. >
  441. <Image
  442. style={[styles.player_pause_icon]}
  443. source={this.props.player_status_icon}
  444. />
  445. </TouchableOpacity>
  446. </View>
  447. <View style={{ flex: 2 }}>
  448. {/* //左侧当前播放时长 */}
  449. <Text style={[styles.player_time]}>
  450. {formatTime(this.props.currentTime)}
  451. </Text>
  452. </View>
  453. <View
  454. style={{
  455. flex: 9,
  456. overflow: "hidden"
  457. }}
  458. >
  459. {/* //中间进度条 */}
  460. <SeekBar
  461. style={{ flex: 1 }}
  462. ref={view => (this.seekbar = view)}
  463. //必须带此方法,作为滑动之后抬起的回调
  464. touchUpCallBack={this.props.touch_up_callback}
  465. />
  466. </View>
  467. <View style={{ flex: 2 }}>
  468. {/* //右侧总时长 */}
  469. <Text style={[styles.player_time]}>
  470. {formatTime(this.props.duration)}
  471. </Text>
  472. </View>
  473. <View
  474. style={{
  475. flex: 1,
  476. alignItems: "center"
  477. }}
  478. >
  479. <TouchableOpacity
  480. style={{
  481. justifyContent: "center",
  482. width: "100%",
  483. height: "100%"
  484. }}
  485. onPress={() => this.props.full_click()}
  486. >
  487. {/* //放大按钮 */}
  488. <Image
  489. style={[styles.fullscreen_icon]}
  490. source={require("../images/video/fullscreen.png")}
  491. />
  492. </TouchableOpacity>
  493. </View>
  494. </View>
  495. );
  496. }
  497. setMax(duration) {
  498. this.seekbar.setMax(duration);
  499. }
  500. getMax() {
  501. return this.seekbar.getMax();
  502. }
  503. setBottom(bottom) {
  504. this.setState({
  505. controller_bottom: bottom
  506. });
  507. }
  508. setProgress(currentTime) {
  509. this.seekbar.setProgress(currentTime);
  510. }
  511. }
  512. const styles = StyleSheet.create({
  513. player_controller: {
  514. flexDirection: "row",
  515. position: "absolute",
  516. width: "100%",
  517. height: 50,
  518. alignItems: "center",
  519. justifyContent: "center"
  520. },
  521. player_pause_icon: {
  522. width: "80%",
  523. resizeMode: "center",
  524. height: "40%",
  525. justifyContent: "center",
  526. alignItems: "center",
  527. left: 5
  528. },
  529. fullscreen_icon: {
  530. width: "80%",
  531. resizeMode: "center",
  532. height: "30%",
  533. justifyContent: "center",
  534. alignItems: "center"
  535. },
  536. player_time: {
  537. fontSize: 18,
  538. color: "white",
  539. alignItems: "center",
  540. justifyContent: "center",
  541. textAlignVertical: "center",
  542. textAlign: "center"
  543. }
  544. });
  545. /**
  546. 使用方法
  547. <CusVideo
  548. show={this.state.video_show} //是否显示
  549. uri={this.state.video_uri} //播放路径
  550. ref={view => (this.video = view)} //设置ID
  551. needback={false}
  552. videoback={() => alert("videoback")}
  553. videofullScreenPlayer={this.fullScreenPlayer.bind(this)}
  554. style={{
  555. left: this.state.x,
  556. top: this.state.y,
  557. width: this.state.video_width,
  558. height: this.state.video_height,
  559. overflow: "hidden",
  560. position: "absolute"
  561. }}
  562. />
  563. */