CusVideo.js 16 KB

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