CusVideo.js 16 KB

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